Dialogue system really good

This commit is contained in:
2026-06-12 14:15:31 -05:00
parent aae68a60d5
commit 7fc1a4645c
2 changed files with 83 additions and 37 deletions
+54 -35
View File
@@ -25,44 +25,70 @@ func setup(responses:Array[DialogueResponse], entity:Entity) -> void:
for child in _list.get_children():
child.queue_free()
for response in _responses:
var label:Label = Label.new()
label.text = response.text
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
_list.add_child(label)
var innerWidth:float = _labelWidth()
var sep:int = _list.get_theme_constant("separation")
var totalHeight:float = 0.0
size.x = MAX_WIDTH
modulate.a = 0.0
for i in range(_responses.size()):
var label:Label = Label.new()
label.text = _responses[i].text
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
label.focus_mode = Control.FOCUS_ALL
_list.add_child(label)
label.size.x = innerWidth
totalHeight += label.get_minimum_size().y
if i > 0:
totalHeight += sep
var idx:int = i
label.focus_entered.connect(func(): _onFocused(idx))
# Prevent focus escaping the list at the edges
var count:int = _list.get_child_count()
for i in range(count):
var label:Label = _list.get_child(i)
label.focus_neighbor_top = _list.get_child(max(0, i - 1)).get_path()
label.focus_neighbor_bottom = _list.get_child(min(count - 1, i + 1)).get_path()
var style:StyleBox = get_theme_stylebox("panel")
if style:
totalHeight += style.get_margin(SIDE_TOP) + style.get_margin(SIDE_BOTTOM)
size = Vector2(MAX_WIDTH, totalHeight)
_updateWorldPosition()
visible = true
_updateSelection()
_list.get_child(0).grab_focus()
func _labelWidth() -> float:
var style:StyleBox = get_theme_stylebox("panel")
if style == null:
return MAX_WIDTH
return MAX_WIDTH - style.get_margin(SIDE_LEFT) - style.get_margin(SIDE_RIGHT)
func _process(_delta:float) -> void:
if not visible:
return
_updateWorldPosition()
_processInput()
func _processInput() -> void:
if Input.is_action_just_released("interact"):
func _input(event:InputEvent) -> void:
if not visible:
return
if event.is_action_released("interact"):
_hasLetGoOfInteract = true
if Input.is_action_just_pressed("ui_up") or Input.is_action_just_pressed("move_forward"):
_selectedIndex = max(0, _selectedIndex - 1)
_updateSelection()
if Input.is_action_just_pressed("ui_down") or Input.is_action_just_pressed("move_back"):
_selectedIndex = min(_responses.size() - 1, _selectedIndex + 1)
_updateSelection()
if _hasLetGoOfInteract and Input.is_action_just_pressed("interact"):
return
if event.is_action_pressed("interact") and _hasLetGoOfInteract:
_confirm()
get_viewport().set_input_as_handled()
func _onFocused(idx:int) -> void:
_selectedIndex = idx
_updateSelection()
func _confirm() -> void:
if _responses.is_empty():
if not visible:
return
var response:DialogueResponse = _responses[_selectedIndex]
chosen.emit(response)
visible = false
chosen.emit(_responses[_selectedIndex])
queue_free()
func _updateSelection() -> void:
@@ -70,8 +96,10 @@ func _updateSelection() -> void:
for i in children.size():
var label:Label = children[i]
if i == _selectedIndex:
label.text = "" + _responses[i].text
label.add_theme_color_override("font_color", Color.YELLOW)
else:
label.text = _responses[i].text
label.remove_theme_color_override("font_color")
func _updateWorldPosition() -> void:
@@ -80,18 +108,9 @@ func _updateWorldPosition() -> void:
var camera:Camera3D = get_viewport().get_camera_3d()
if camera == null:
return
if size.y == 0:
return
var worldPos:Vector3 = _entity.global_position + Vector3(0, 2.5, 0)
if camera.is_position_behind(worldPos):
modulate.a = 0.0
return
modulate.a = 1.0
var screenPos:Vector2 = camera.unproject_position(worldPos)
var viewportSize:Vector2 = get_viewport().get_visible_rect().size
position.x = clamp(screenPos.x - size.x * 0.5, 0.0, viewportSize.x - size.x)
var yAbove:float = screenPos.y - size.y
if yAbove >= 0.0:
position.y = yAbove
else:
position.y = clamp(screenPos.y + 10.0, 0.0, viewportSize.y - size.y)
position = screenPos - size * 0.5
position.x = clamp(position.x, 0.0, viewportSize.x - size.x)
position.y = clamp(position.y, 0.0, viewportSize.y - size.y)