142 lines
3.5 KiB
GDScript3
142 lines
3.5 KiB
GDScript3
|
@icon("./assets/responses_menu.svg")
|
||
|
|
||
|
## A [Container] for dialogue responses provided by [b]Dialogue Manager[/b].
|
||
|
class_name DialogueResponsesMenu extends Container
|
||
|
|
||
|
|
||
|
## Emitted when a response is selected.
|
||
|
signal response_selected(response)
|
||
|
|
||
|
|
||
|
## Optionally specify a control to duplicate for each response
|
||
|
@export var response_template: Control
|
||
|
|
||
|
## The action for accepting a response (is possibly overridden by parent dialogue balloon).
|
||
|
@export var next_action: StringName = &""
|
||
|
|
||
|
## The list of dialogue responses.
|
||
|
var responses: Array = []:
|
||
|
get:
|
||
|
return responses
|
||
|
set(value):
|
||
|
responses = value
|
||
|
|
||
|
# Remove any current items
|
||
|
for item in get_children():
|
||
|
if item == response_template: continue
|
||
|
|
||
|
remove_child(item)
|
||
|
item.queue_free()
|
||
|
|
||
|
# Add new items
|
||
|
if responses.size() > 0:
|
||
|
for response in responses:
|
||
|
var item: Control
|
||
|
if is_instance_valid(response_template):
|
||
|
item = response_template.duplicate(DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_SIGNALS)
|
||
|
item.show()
|
||
|
else:
|
||
|
item = Button.new()
|
||
|
item.name = "Response%d" % get_child_count()
|
||
|
if not response.is_allowed:
|
||
|
item.name = String(item.name) + "Disallowed"
|
||
|
item.disabled = true
|
||
|
|
||
|
# If the item has a response property then use that
|
||
|
if "response" in item:
|
||
|
item.response = response
|
||
|
# Otherwise assume we can just set the text
|
||
|
else:
|
||
|
item.text = response.text
|
||
|
|
||
|
item.set_meta("response", response)
|
||
|
|
||
|
add_child(item)
|
||
|
|
||
|
_configure_focus()
|
||
|
|
||
|
|
||
|
func _ready() -> void:
|
||
|
visibility_changed.connect(func():
|
||
|
if visible and get_menu_items().size() > 0:
|
||
|
get_menu_items()[0].grab_focus()
|
||
|
)
|
||
|
|
||
|
if is_instance_valid(response_template):
|
||
|
response_template.hide()
|
||
|
|
||
|
|
||
|
## Get the selectable items in the menu.
|
||
|
func get_menu_items() -> Array:
|
||
|
var items: Array = []
|
||
|
for child in get_children():
|
||
|
if not child.visible: continue
|
||
|
if "Disallowed" in child.name: continue
|
||
|
items.append(child)
|
||
|
|
||
|
return items
|
||
|
|
||
|
|
||
|
## [b]DEPRECATED[/b]. Do not use.
|
||
|
func set_responses(next_responses: Array) -> void:
|
||
|
self.responses = next_responses
|
||
|
|
||
|
|
||
|
#region Internal
|
||
|
|
||
|
|
||
|
# Prepare the menu for keyboard and mouse navigation.
|
||
|
func _configure_focus() -> void:
|
||
|
var items = get_menu_items()
|
||
|
for i in items.size():
|
||
|
var item: Control = items[i]
|
||
|
|
||
|
item.focus_mode = Control.FOCUS_ALL
|
||
|
|
||
|
item.focus_neighbor_left = item.get_path()
|
||
|
item.focus_neighbor_right = item.get_path()
|
||
|
|
||
|
if i == 0:
|
||
|
item.focus_neighbor_top = item.get_path()
|
||
|
item.focus_previous = item.get_path()
|
||
|
else:
|
||
|
item.focus_neighbor_top = items[i - 1].get_path()
|
||
|
item.focus_previous = items[i - 1].get_path()
|
||
|
|
||
|
if i == items.size() - 1:
|
||
|
item.focus_neighbor_bottom = item.get_path()
|
||
|
item.focus_next = item.get_path()
|
||
|
else:
|
||
|
item.focus_neighbor_bottom = items[i + 1].get_path()
|
||
|
item.focus_next = items[i + 1].get_path()
|
||
|
|
||
|
item.mouse_entered.connect(_on_response_mouse_entered.bind(item))
|
||
|
item.gui_input.connect(_on_response_gui_input.bind(item, item.get_meta("response")))
|
||
|
|
||
|
items[0].grab_focus()
|
||
|
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Signals
|
||
|
|
||
|
|
||
|
func _on_response_mouse_entered(item: Control) -> void:
|
||
|
if "Disallowed" in item.name: return
|
||
|
|
||
|
item.grab_focus()
|
||
|
|
||
|
|
||
|
func _on_response_gui_input(event: InputEvent, item: Control, response) -> void:
|
||
|
if "Disallowed" in item.name: return
|
||
|
|
||
|
get_viewport().set_input_as_handled()
|
||
|
|
||
|
if event is InputEventMouseButton and event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT:
|
||
|
response_selected.emit(response)
|
||
|
elif event.is_action_pressed(&"ui_accept" if next_action.is_empty() else next_action) and item in get_menu_items():
|
||
|
response_selected.emit(response)
|
||
|
|
||
|
|
||
|
#endregion
|