extends CanvasLayer @onready var audio_stream_player: AudioStreamPlayer = $AudioStreamPlayer @onready var dialogue_label: DialogueLabel = $DialogueLabel @onready var responses_menu: DialogueResponsesMenu = %DialogueResponsesMenu ## The dialogue resource var resource: DialogueResource ## Temporary game states var temporary_game_states: Array = [] ## See if we are waiting for the player var is_waiting_for_input: bool = false # Who is talking var dialogue_marker: DialogueMarker ## The current line var dialogue_line: DialogueLine: set(value): if not value: queue_free() else: dialogue_line = value update_dialogue_line() get: return dialogue_line func _ready() -> void: dialogue_label.hide() Engine.get_singleton("DialogueManager").mutated.connect(_on_mutated) func _unhandled_input(_event: InputEvent) -> void: # Only the balloon is allowed to handle input while it's showing get_viewport().set_input_as_handled() ## Start some dialogue func start(dialogue_resource: DialogueResource, title: String, extra_game_states: Array = []) -> void: if not is_node_ready(): await ready temporary_game_states = extra_game_states is_waiting_for_input = false resource = dialogue_resource dialogue_line = await resource.get_next_dialogue_line(title, temporary_game_states) ## Go to the next line func next(next_id: String) -> void: dialogue_line = await resource.get_next_dialogue_line(next_id, temporary_game_states) #region Helpers func update_dialogue_line() -> void: responses_menu.responses = [] dialogue_line.text = dialogue_line.text dialogue_label.hide() dialogue_label.dialogue_line = dialogue_line # Set the colour and attach the dialogue to the character dialogue_marker = DialogueMarker.find_for_character(dialogue_line.character) if is_instance_valid(dialogue_marker): dialogue_label.set("theme_override_colors/default_color", dialogue_marker.color) dialogue_label.set("theme_override_colors/font_outline_color", dialogue_marker.color.darkened(0.4)) dialogue_label.global_position = dialogue_marker.global_position - Vector2(dialogue_label.size.x * 0.5, dialogue_label.get_content_height()) dialogue_label.show() dialogue_label.type_out() # Play sound and wait for sound to finish var voice_path: String = "res://l10n/voice/en/%s.ogg" % [dialogue_line.static_id] if ResourceLoader.exists(voice_path): var stream = load(voice_path) audio_stream_player.stream = stream audio_stream_player.play() Events.character_started_talking.emit(dialogue_line.character) await audio_stream_player.finished Events.character_finished_talking.emit(dialogue_line.character) else: printerr("Voice missing: %s" % [voice_path]) Events.character_started_talking.emit(dialogue_line.character) await get_tree().create_timer(dialogue_line.text.length() * 0.05).timeout Events.character_finished_talking.emit(dialogue_line.character) dialogue_label.hide() if dialogue_line.responses.size() == 0: next(dialogue_line.next_id) else: responses_menu.responses = dialogue_line.responses for item: RichTextLabel in responses_menu.get_menu_items(): item.mouse_entered.connect(_on_response_mouse_entered.bind(item)) item.focus_entered.connect(_on_response_focus_entered.bind(item)) item.focus_exited.connect(_on_response_focus_exited.bind(item)) item.gui_input.connect(_on_response_gui_input.bind(item)) _on_response_focus_entered(responses_menu.get_menu_items()[0]) #endregion #region Signals func _on_mutated(_mutation: Dictionary) -> void: is_waiting_for_input = false 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) -> 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 == 1: next(dialogue_line.responses[item.get_index()].next_id) elif event.is_action_pressed("ui_accept") and item in responses_menu.get_menu_items(): next(dialogue_line.responses[item.get_index()].next_id) func _on_response_focus_entered(item): item.set("theme_override_colors/default_color", DialogueMarker.find_for_character("Jimmy").color) func _on_response_focus_exited(item): item.set("theme_override_colors/default_color", Color.WHITE) func _on_dialogue_label_resized() -> void: if is_instance_valid(dialogue_marker): dialogue_label.global_position = dialogue_marker.global_position - Vector2(dialogue_label.size.x * 0.5, dialogue_label.get_content_height()) #endregion