diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/chooser/simple.tscn b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/chooser/simple.tscn index 7a2a356f..acf63543 100644 --- a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/chooser/simple.tscn +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/chooser/simple.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=5 format=2] [ext_resource path="res://addons/escoria-dialog-simple/chooser/simple.gd" type="Script" id=1] -[ext_resource path="res://gymkhana/addons/escoria-ui-return-monkey-island/theme.tres" type="Theme" id=2] +[ext_resource path="res://gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/theme.tres" type="Theme" id=2] [sub_resource type="Gradient" id=1] colors = PoolColorArray( 1, 0, 0, 1, 1, 0, 0, 1 ) diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/esc_dialog_simple.gd b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/esc_dialog_simple.gd index 2c6088ae..0d387738 100644 --- a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/esc_dialog_simple.gd +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/esc_dialog_simple.gd @@ -3,12 +3,27 @@ extends ESCDialogManager class_name ESCReturnToMonekyIslandDialogs +# State machine that governs how the dialog manager behaves +var state_machine = preload("res://gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/esc_dialog_simple_state_machine.gd").new() + # The currently running player var _type_player: Node = null +var _preserved_type_player_type: String = "" # Reference to the dialog player var _dialog_player: Node = null +# Basic state tracking +var _is_saying: bool = false + +# Whether to preserve the next dialog box used by `say`, or, if already +# preserving a dialog box, whether to continue using that dialog box +var _should_preserve_dialog_box: bool = false + + +func _ready() -> void: + add_child(state_machine) + # Check whether a specific type is supported by the # dialog plugin @@ -17,7 +32,7 @@ var _dialog_player: Node = null # - type: required type # *Returns* Whether the type is supported or not func has_type(type: String) -> bool: - return true if type in ["floating", "avatar"] else false + return true if type in ["floating"] else false # Check whether a specific chooser type is supported by the @@ -29,6 +44,29 @@ func has_type(type: String) -> bool: func has_chooser_type(type: String) -> bool: return true if type == "simple" else false +# Instructs the dialog manager to preserve the next dialog box used by a `say` +# command until a call to `disable_preserve_dialog_box` is made. +# +# This method should be idempotent, i.e. if called after the first time and +# prior to `disable_preserve_dialog_box` being called, the result should be the +# same. +func enable_preserve_dialog_box() -> void: + _should_preserve_dialog_box = true + + +# Instructs the dialog manager to no longer preserve the currently-preserved +# dialog box or to not preserve the next dialog box used by a `say` command +# (this is the default state). +# +# This method should be idempotent, i.e. if called after the first time and +# prior to `enable_preserve_dialog_box` being called, the result should be the +# same. +func disable_preserve_dialog_box() -> void: + _should_preserve_dialog_box = false + + if is_instance_valid(_dialog_player) and _dialog_player.get_children().has(_type_player): + _dialog_player.remove_child(_type_player) + _preserved_type_player_type = "" # Output a text said by the item specified by the global id. Emit # `say_finished` after finishing displaying the text. @@ -42,6 +80,37 @@ func has_chooser_type(type: String) -> bool: func say(dialog_player: Node, global_id: String, text: String, type: String): _dialog_player = dialog_player + _initialize_say_states(global_id, text, type) + + if _should_preserve_dialog_box: + # If the dialog box type doesn't match what's currently being reused (if anything), + # we want to remove the old one (if it exists) and then initialize and add the new dialog + # box type to the dialog player + if type != _preserved_type_player_type: + if _dialog_player.get_children().has(_type_player): + _dialog_player.remove_child(_type_player) + + _init_type_player(type) + + _preserved_type_player_type = type + else: + _init_type_player(type) + + state_machine._change_state("say") + + +func do_say(global_id: String, text: String) -> void: + # Only add_child here in order to prevent _type_player from running its _process method + # before we're ready, and only if it's necessary + if not _dialog_player.get_children().has(_type_player): + _dialog_player.add_child(_type_player) + + _type_player.say(global_id, text) + + + + +func _init_type_player(type: String) -> void: if type == "floating": _type_player = preload(\ "res://gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/types/floating.tscn"\ @@ -51,26 +120,30 @@ func say(dialog_player: Node, global_id: String, text: String, type: String): "res://gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/types/avatar.tscn"\ ).instance() - _type_player.connect("say_finished", self, "_on_say_finished", [], CONNECT_ONESHOT) - _type_player.connect("say_visible", self, "_on_say_visible", [], CONNECT_ONESHOT) + _type_player.connect("say_finished", self, "_on_say_finished") + _type_player.connect("say_visible", self, "_on_say_visible") - _dialog_player.add_child(_type_player) - _type_player.say(global_id, text) -# yield(_type_player, "say_finished") -# if _dialog_player.get_children().has(_type_player): -# _dialog_player.remove_child(_type_player) -# emit_signal("say_finished") +func _initialize_say_states(global_id: String, text: String, type: String) -> void: + state_machine.states_map["say"].initialize(self, global_id, text, type) + state_machine.states_map["finish"].initialize(_dialog_player) + state_machine.states_map["say_fast"].initialize(self) + state_machine.states_map["say_finish"].initialize(self) + state_machine.states_map["visible"].initialize(self) + state_machine.states_map["interrupt"].initialize(self) func _on_say_finished(): - if _dialog_player.get_children().has(_type_player): + if not _should_preserve_dialog_box and _dialog_player.get_children().has(_type_player): _dialog_player.remove_child(_type_player) - emit_signal("say_finished") + + _is_saying = false + + emit_signal("say_finished") func _on_say_visible(): emit_signal("say_visible") - + # Present an option chooser to the player and sends the signal # `option_chosen` with the chosen dialog option @@ -78,13 +151,26 @@ func _on_say_visible(): # #### Parameters # - dialog_player: Node of the dialog player in the UI # - dialog: Information about the dialog to display +# - type: The dialog chooser type to use func choose(dialog_player: Node, dialog: ESCDialog, type: String): - var chooser = preload(\ - "res://gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/chooser/simple.tscn"\ - ).instance() + _dialog_player = dialog_player + + state_machine.states_map["choices"].initialize(dialog_player, self, dialog, type) + state_machine._change_state("choices") + + +func do_choose(dialog_player: Node, dialog: ESCDialog, type: String = "simple"): + var chooser + + if type == "simple" or type == "": + chooser = preload(\ + "res://gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/chooser/simple.tscn"\ + ).instance() + dialog_player.add_child(chooser) chooser.set_dialog(dialog) chooser.show_chooser() + var option = yield(chooser, "option_chosen") dialog_player.remove_child(chooser) emit_signal("option_chosen", option) @@ -92,13 +178,13 @@ func choose(dialog_player: Node, dialog: ESCDialog, type: String): # Trigger running the dialogue faster func speedup(): - if _type_player != null: + if is_instance_valid(_type_player): _type_player.speedup() # Trigger an instant finish of the current dialog func finish(): - if _type_player != null: + if is_instance_valid(_type_player): _type_player.finish() @@ -110,11 +196,13 @@ func interrupt(): as ESCSpeechPlayer ).set_state("off") - _dialog_player.remove_child(_type_player) + if not _should_preserve_dialog_box and _dialog_player.get_children().has(_type_player): + _dialog_player.remove_child(_type_player) + emit_signal("say_finished") # To be called if voice audio has finished. func voice_audio_finished(): - if _type_player != null: + if is_instance_valid(_type_player): _type_player.voice_audio_finished() diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/esc_dialog_simple_settings.gd b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/esc_dialog_simple_settings.gd new file mode 100644 index 00000000..8bbf9dba --- /dev/null +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/esc_dialog_simple_settings.gd @@ -0,0 +1,20 @@ +extends Resource +class_name SimpleDialogSettings + +const SETTINGS_ROOT = "escoria/dialog_simple" + +const AVATARS_PATH = "%s/avatars_path" % SETTINGS_ROOT +const TEXT_TIME_PER_LETTER_MS = "%s/text_time_per_letter_ms" % SETTINGS_ROOT +const TEXT_TIME_PER_LETTER_MS_FAST = "%s/text_time_per_fast_letter_ms" % SETTINGS_ROOT +const READING_SPEED_IN_WPM = "%s/reading_speed_in_wpm" % SETTINGS_ROOT +const CLEAR_TEXT_BY_CLICK_ONLY = "%s/clear_text_by_click_only" % SETTINGS_ROOT +const LEFT_CLICK_ACTION = "%s/left_click_action" % SETTINGS_ROOT + +const STOP_TALKING_ANIMATION_ON = "%s/stop_talking_animation_on" % SETTINGS_ROOT + +const LEFT_CLICK_ACTION_SPEED_UP = "Speed up" +const LEFT_CLICK_ACTION_INSTANT_FINISH = "Instant finish" +const LEFT_CLICK_ACTION_NOTHING = "None" + +const STOP_TALKING_ANIMATION_ON_END_OF_TEXT = "End of text" +const STOP_TALKING_ANIMATION_ON_END_OF_AUDIO = "End of audio" diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/esc_dialog_simple_state_machine.gd b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/esc_dialog_simple_state_machine.gd new file mode 100644 index 00000000..e2051ad9 --- /dev/null +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/esc_dialog_simple_state_machine.gd @@ -0,0 +1,31 @@ +extends "res://addons/escoria-dialog-simple/patterns/state_machine/state_machine.gd" + + +func _init(): + _create_states() + _add_states_to_machine() + + current_state_name = "idle" + START_STATE = states_map[current_state_name] + + initialize(START_STATE) + + +# Creates the states for this state machine. +func _create_states() -> void: + states_map = { + "idle": preload("res://addons/escoria-dialog-simple/states/dialog_idle.gd").new(), + "say": preload("res://addons/escoria-dialog-simple/states/dialog_say.gd").new(), + "say_fast": preload("res://addons/escoria-dialog-simple/states/dialog_say_fast.gd").new(), + "say_finish": preload("res://addons/escoria-dialog-simple/states/dialog_say_finish.gd").new(), + "visible": preload("res://addons/escoria-dialog-simple/states/dialog_visible.gd").new(), + "finish": preload("res://addons/escoria-dialog-simple/states/dialog_finish.gd").new(), + "interrupt": preload("res://addons/escoria-dialog-simple/states/dialog_interrupt.gd").new(), + "choices": preload("res://addons/escoria-dialog-simple/states/dialog_choices.gd").new(), + } + + +# Adds any created states into the state machine as children. +func _add_states_to_machine() -> void: + for key in states_map: + add_child(states_map[key]) diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/patterns/state_machine/state.gd b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/patterns/state_machine/state.gd new file mode 100644 index 00000000..9e928083 --- /dev/null +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/patterns/state_machine/state.gd @@ -0,0 +1,31 @@ +""" +Base interface for all states: it doesn't do anything in itself +but forces us to pass the right arguments to the methods below +and makes sure every State object had all of these methods. +""" +extends Node + + +signal finished(next_state_name) + + +# Initialize the state. E.g. change the animation +func enter(): + return + + +# Clean up the state. Reinitialize values like a timer +func exit(): + return + + +func handle_input(_event): + return + + +func update(_delta): + return + + +func _on_animation_finished(_anim_name): + return diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/patterns/state_machine/state_machine.gd b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/patterns/state_machine/state_machine.gd new file mode 100644 index 00000000..cd5ab1a1 --- /dev/null +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/patterns/state_machine/state_machine.gd @@ -0,0 +1,92 @@ +""" +Base interface for a generic state machine +It handles initializing, setting the machine active or not +delegating _physics_process, _input calls to the State nodes, +and changing the current/active state. +""" +extends Node + + +signal state_changed(current_state) + + +""" +You must set a starting node from the inspector or on +the node that inherits from this state machine interface +If you don't the game will crash (on purpose, so you won't +forget to initialize the state machine) +""" +export(NodePath) var START_STATE +var states_map = {} + +var states_stack = [] # can also be used as a pushdown automaton +var current_state = null +var current_state_name = "" +var _active = false setget set_active + + +func initialize(start_state): + for child in get_children(): + child.connect("finished", self, "_change_state") + + set_active(true) + states_stack.push_front(start_state) + current_state = states_stack[0] + current_state.enter() + + +func set_active(value): + _active = value + set_physics_process(value) + set_process_input(value) + if not _active: + states_stack = [] + current_state = null + + +func _input(event): + current_state.handle_input(event) + + +func _physics_process(delta): + current_state.update(delta) + + +func _on_animation_finished(anim_name): + if not _active: + return + current_state._on_animation_finished(anim_name) + + +func _change_state(state_name): + if not _active: + return + + escoria.logger.trace( + self, + "Dialog State Machine: Changing state from '%s' to '%s'." % [current_state_name, state_name] + ) + + current_state.exit() + + if state_name == "previous": + states_stack.pop_front() + else: + states_stack[0] = states_map[state_name] + + current_state = states_stack[0] + + emit_signal("state_changed", current_state) + + #if state_name != "previous": + current_state.enter() + + current_state_name = state_name + + +func get_current_state_name(): + for key in states_map.keys(): + if states_map[key] == current_state: + return key + + return null diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/plugin.gd b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/plugin.gd index 5f052ee6..ad12d5a3 100644 --- a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/plugin.gd +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/plugin.gd @@ -1,7 +1,7 @@ # A simple dialog manager for Escoria tool extends EditorPlugin -class_name SimpleDialogPlugin +class_name RTMISimpleDialogPlugin const MANAGER_CLASS="res://gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/esc_dialog_simple.gd" @@ -41,12 +41,12 @@ var stop_talking_animation_on_options: PoolStringArray = [ # Override function to return the plugin name. func get_plugin_name(): - return "escoria-dialog-simple" + return "escoria-rtmi-dialog-simple" # Unregister ourselves func disable_plugin(): - print("Disabling plugin Escoria Dialog Simple") + print("Disabling plugin Escoria Return to the Monkey Island Dialog Simple") ESCProjectSettingsManager.remove_setting( ESCProjectSettingsManager.DEFAULT_DIALOG_TYPE ) @@ -84,7 +84,7 @@ func disable_plugin(): # Add ourselves to the list of dialog managers func enable_plugin(): - print("Enabling plugin Escoria Dialog Simple") + print("Enabling plugin Escoria Return to the Monkey Island Dialog Simple") if EscoriaPlugin.register_dialog_manager(self, MANAGER_CLASS): ESCProjectSettingsManager.register_setting( diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_choices.gd b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_choices.gd new file mode 100644 index 00000000..635d6831 --- /dev/null +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_choices.gd @@ -0,0 +1,44 @@ +extends "res://addons/escoria-dialog-simple/patterns/state_machine/state.gd" + + +# The owning dialog player. +var _dialog_player + +# The dialog to start. +var _dialog: ESCDialog +var _type: String = "simple" + +var _dialog_chooser_ui: ESCDialogManager = null + +var _ready_to_choose: bool + + +func initialize(dialog_player, dialog_chooser_ui: ESCDialogManager, dialog: ESCDialog, type: String) -> void: + _dialog_player = dialog_player + _dialog_chooser_ui = dialog_chooser_ui + _dialog = dialog + _type = type + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'choices'.") + + if _dialog.options.empty(): + escoria.logger.error( + self, + "Received dialog options array was empty." + ) + + _ready_to_choose = true + + +func update(_delta): + if _ready_to_choose: + _ready_to_choose = false + _dialog_chooser_ui.do_choose(_dialog_player, _dialog, _type) + var option = yield(_dialog_chooser_ui, "option_chosen") + + escoria.logger.trace(self, "Dialog State Machine: 'choices' -> 'idle'") + + emit_signal("finished", "idle") + _dialog_player.emit_signal("option_chosen", option) diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_finish.gd b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_finish.gd new file mode 100644 index 00000000..3bdccd76 --- /dev/null +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_finish.gd @@ -0,0 +1,19 @@ +extends "res://addons/escoria-dialog-simple/patterns/state_machine/state.gd" + + +# Owning dialog player +var _dialog_player + + +func initialize(dialog_player) -> void: + _dialog_player = dialog_player + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'finish'.") + + +func update(_delta): + escoria.logger.trace(self, "Dialog State Machine: 'finish' -> 'idle'") + emit_signal("finished", "idle") + _dialog_player.emit_signal("say_finished") diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_idle.gd b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_idle.gd new file mode 100644 index 00000000..b36d254d --- /dev/null +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_idle.gd @@ -0,0 +1,5 @@ +extends "res://addons/escoria-dialog-simple/patterns/state_machine/state.gd" + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'idle'.") diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_interrupt.gd b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_interrupt.gd new file mode 100644 index 00000000..e9bd54c0 --- /dev/null +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_interrupt.gd @@ -0,0 +1,25 @@ +extends "res://addons/escoria-dialog-simple/patterns/state_machine/state.gd" + + +# Reference to the currently playing dialog manager +var _dialog_manager: ESCDialogManager = null + + +func initialize(dialog_manager: ESCDialogManager) -> void: + _dialog_manager = dialog_manager + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'interrupt'.") + + if _dialog_manager != null: + if not _dialog_manager.is_connected("say_finished", self, "_on_say_finished"): + _dialog_manager.connect("say_finished", self, "_on_say_finished") + + _dialog_manager.interrupt() + + +func _on_say_finished() -> void: + escoria.logger.trace(self, "Dialog State Machine: 'interrupt' -> 'finish'") + emit_signal("finished", "finish") + diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_say.gd b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_say.gd new file mode 100644 index 00000000..a057f4f3 --- /dev/null +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_say.gd @@ -0,0 +1,181 @@ +extends "res://addons/escoria-dialog-simple/patterns/state_machine/state.gd" + + +# A regular expression that separates the translation key from the text +const KEYTEXT_REGEX = "^((?[^:]+):)?\"(?.+)\"" + + +# Reference to the currently playing dialog manager +var _dialog_manager: ESCDialogManager = null + +# Character that is talking +var _character: String + +# UI to use for the dialog +var _type: String + +# Text to say +var _text: String + +# Regular expression object for the separation of key and text +var _keytext_regex: RegEx = RegEx.new() + +var _ready_to_say: bool + +var _stop_talking_animation_on_option: String + + +# Constructor +func _init() -> void: + _keytext_regex.compile(KEYTEXT_REGEX) + + +func initialize(dialog_manager: ESCDialogManager, character: String, text: String, type: String) -> void: + _dialog_manager = dialog_manager + _character = character + _text = text + _type = type + _stop_talking_animation_on_option = \ + ESCProjectSettingsManager.get_setting(SimpleDialogSettings.STOP_TALKING_ANIMATION_ON) + + +func handle_input(_event): + if _event is InputEventMouseButton and _event.pressed: + if escoria.inputs_manager.input_mode != \ + escoria.inputs_manager.INPUT_NONE and \ + _dialog_manager != null: + + var left_click_action = ESCProjectSettingsManager.get_setting(SimpleDialogSettings.LEFT_CLICK_ACTION) + + _handle_left_click_action(left_click_action) + + +func _handle_left_click_action(left_click_action: String) -> void: + match left_click_action: + SimpleDialogSettings.LEFT_CLICK_ACTION_SPEED_UP: + if _dialog_manager.is_connected("say_visible", self, "_on_say_visible"): + _dialog_manager.disconnect("say_visible", self, "_on_say_visible") + + escoria.logger.trace(self, "Dialog State Machine: 'say' -> 'say_fast'") + emit_signal("finished", "say_fast") + SimpleDialogSettings.LEFT_CLICK_ACTION_INSTANT_FINISH: + if _dialog_manager.is_connected("say_visible", self, "_on_say_visible"): + _dialog_manager.disconnect("say_visible", self, "_on_say_visible") + + escoria.logger.trace(self, "Dialog State Machine: 'say' -> 'say_finish'") + emit_signal("finished", "say_finish") + + get_tree().set_input_as_handled() + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'say'.") + + if not _dialog_manager.is_connected("say_visible", self, "_on_say_visible"): + _dialog_manager.connect("say_visible", self, "_on_say_visible") + + var matches = _keytext_regex.search(_text) + + if not matches: + escoria.logger.error( + self, + "Unexpected text encountered: %s." % _text + ) + + var key = matches.get_string("key") + + if matches.get_string("key") != "": + var _speech_resource = _get_voice_file( + matches.get_string("key") + ) + + if _speech_resource == "": + escoria.logger.warn( + self, + "Unable to find voice file with key '%s'." % matches.get_string("key") + ) + else: + ( + escoria.object_manager.get_object(escoria.object_manager.SPEECH).node\ + as ESCSpeechPlayer + ).set_state(_speech_resource) + + if _stop_talking_animation_on_option == SimpleDialogSettings.STOP_TALKING_ANIMATION_ON_END_OF_AUDIO: + if not ( + escoria.object_manager.get_object(escoria.object_manager.SPEECH).node\ + as ESCSpeechPlayer + ).stream.is_connected("finished", self, "_on_audio_finished"): + + ( + escoria.object_manager.get_object(escoria.object_manager.SPEECH).node\ + as ESCSpeechPlayer + ).stream.connect("finished", self, "_on_audio_finished") + + var translated_text: String = tr(matches.get_string("key")) + + # Only update the text if the translated text was found; otherwise, raise + # a warning and use the original, untranslated text. + if translated_text == matches.get_string("key"): + escoria.logger.warn( + self, + "Unable to find translation key '%s'. Using untranslated text." % matches.get_string("key") + ) + _text = matches.get_string("text") + else: + _text = translated_text + else: + _text = matches.get_string("text") + + _ready_to_say = true + + +func update(_delta): + if _ready_to_say: + _dialog_manager.do_say(_character, _text) + _ready_to_say = false + + +# Find the matching voice output file for the given key +# +# #### Parameters +# +# - key: Text key provided +# - start: Starting folder to search for voices +# +# *Returns* The path to the matching voice file +func _get_voice_file(key: String, start: String = "") -> String: + if start == "": + start = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.SPEECH_FOLDER + ) + var _dir = Directory.new() + if _dir.open(start) == OK: + _dir.list_dir_begin(true, true) + var file_name = _dir.get_next() + while file_name != "": + if _dir.current_is_dir(): + var _voice_file = _get_voice_file( + key, + start.plus_file(file_name) + ) + if _voice_file != "": + return _voice_file + else: + if file_name == "%s.%s.import" % [ + key, + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.SPEECH_EXTENSION + ) + ]: + return start.plus_file(file_name.trim_suffix(".import")) + file_name = _dir.get_next() + return "" + + +func _on_say_visible() -> void: + escoria.logger.trace(self, "Dialog State Machine: 'say' -> 'visible'") + emit_signal("finished", "visible") + + +func _on_audio_finished() -> void: + _dialog_manager.voice_audio_finished() diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_say_fast.gd b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_say_fast.gd new file mode 100644 index 00000000..f0cfaa44 --- /dev/null +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_say_fast.gd @@ -0,0 +1,29 @@ +extends "res://addons/escoria-dialog-simple/patterns/state_machine/state.gd" + + +# Reference to the currently playing dialog manager +var _dialog_manager: ESCDialogManager = null + + +func initialize(dialog_manager: ESCDialogManager) -> void: + _dialog_manager = dialog_manager + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'say_fast'.") + + if escoria.inputs_manager.input_mode != \ + escoria.inputs_manager.INPUT_NONE and \ + _dialog_manager != null: + + if not _dialog_manager.is_connected("say_visible", self, "_on_say_visible"): + _dialog_manager.connect("say_visible", self, "_on_say_visible") + + _dialog_manager.speedup() + else: + escoria.logger.error(self, "Illegal state.") + + +func _on_say_visible() -> void: + escoria.logger.trace(self, "Dialog State Machine: 'say_fast' -> 'visible'") + emit_signal("finished", "visible") diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_say_finish.gd b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_say_finish.gd new file mode 100644 index 00000000..ac3efe44 --- /dev/null +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_say_finish.gd @@ -0,0 +1,29 @@ +extends "res://addons/escoria-dialog-simple/patterns/state_machine/state.gd" + + +# Reference to the currently playing dialog manager +var _dialog_manager: ESCDialogManager = null + + +func initialize(dialog_manager: ESCDialogManager) -> void: + _dialog_manager = dialog_manager + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'say_finish'.") + + if escoria.inputs_manager.input_mode != \ + escoria.inputs_manager.INPUT_NONE and \ + _dialog_manager != null: + + if not _dialog_manager.is_connected("say_visible", self, "_on_say_visible"): + _dialog_manager.connect("say_visible", self, "_on_say_visible") + + _dialog_manager.finish() + else: + escoria.logger.error(self, "Illegal state.") + + +func _on_say_visible() -> void: + escoria.logger.trace(self, "Dialog State Machine: 'say_finish' -> 'visible'") + emit_signal("finished", "visible") diff --git a/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_visible.gd b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_visible.gd new file mode 100644 index 00000000..f340d174 --- /dev/null +++ b/gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/states/dialog_visible.gd @@ -0,0 +1,34 @@ +extends "res://addons/escoria-dialog-simple/patterns/state_machine/state.gd" + + +# Reference to the currently playing dialog manager +var _dialog_manager: ESCDialogManager = null + + +func initialize(dialog_manager: ESCDialogManager) -> void: + _dialog_manager = dialog_manager + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'visible'.") + + if not _dialog_manager.is_connected("say_finished", self, "_on_say_finished"): + _dialog_manager.connect("say_finished", self, "_on_say_finished") + + +func handle_input(_event): + if _event is InputEventMouseButton and _event.pressed: + if escoria.inputs_manager.input_mode != \ + escoria.inputs_manager.INPUT_NONE: + + if _dialog_manager.is_connected("say_finished", self, "_on_say_finished"): + _dialog_manager.disconnect("say_finished", self, "_on_say_finished") + + emit_signal("finished", "interrupt") + get_tree().set_input_as_handled() + + +# Handles the end of a say function after it has emitted say_finished. +func _on_say_finished(): + escoria.logger.trace(self, "Dialog State Machine: 'visible' -> 'finish'") + emit_signal("finished", "finish") diff --git a/gymkhana/characters/eneko/eneko_smoking.esc b/gymkhana/characters/eneko/eneko_smoking.esc index bf3f7bd0..b2eaf64e 100644 --- a/gymkhana/characters/eneko/eneko_smoking.esc +++ b/gymkhana/characters/eneko/eneko_smoking.esc @@ -23,7 +23,7 @@ say current_player "Tiene pinta de que fuma" say eneko_smoking "Fumar" say eneko_smoking "no lo ves?" - "Agur" - say player "Agur yogur!" + say current_player "Agur yogur!" #turn_to eneko_smoking player stop ! diff --git a/project.godot b/project.godot index b3d3a739..c17b7225 100644 --- a/project.godot +++ b/project.godot @@ -557,7 +557,7 @@ _global_script_classes=[ { "base": "ESCBaseCommand", "class": "SetAnimationsCommand", "language": "GDScript", -"path": "res://gymkhana/addons/escoria-ui-return-monkey-island/esc/commands/set_tooltip.gd" +"path": "res://addons/escoria-core/game/core-scripts/esc/commands/set_animations.gd" }, { "base": "ESCBaseCommand", "class": "SetGlobalCommand", @@ -590,6 +590,11 @@ _global_script_classes=[ { "path": "res://addons/escoria-core/game/core-scripts/esc/commands/set_state.gd" }, { "base": "ESCBaseCommand", +"class": "SetTooltipCommand", +"language": "GDScript", +"path": "res://gymkhana/addons/escoria-ui-return-monkey-island/esc/commands/set_tooltip.gd" +}, { +"base": "ESCBaseCommand", "class": "ShowMenuCommand", "language": "GDScript", "path": "res://addons/escoria-core/game/core-scripts/esc/commands/show_menu.gd" @@ -602,7 +607,7 @@ _global_script_classes=[ { "base": "Resource", "class": "SimpleDialogSettings", "language": "GDScript", -"path": "res://addons/escoria-dialog-simple/esc_dialog_simple_settings.gd" +"path": "res://gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/esc_dialog_simple_settings.gd" }, { "base": "SlideCommand", "class": "SlideBlockCommand", @@ -801,6 +806,7 @@ _global_script_class_icons={ "SetInteractiveCommand": "", "SetSpeedCommand": "", "SetStateCommand": "", +"SetTooltipCommand": "", "ShowMenuCommand": "", "SimpleDialogPlugin": "", "SimpleDialogSettings": "", @@ -854,7 +860,7 @@ search_in_file_extensions=PoolStringArray( "gd", "shader", "esc" ) [editor_plugins] -enabled=PoolStringArray( "res://addons/escoria-core/plugin.cfg", "res://gymkhana/addons/escoria-ui-return-monkey-island/plugin.cfg", "res://addons/escoria-wizard/plugin.cfg" ) +enabled=PoolStringArray( "res://addons/escoria-core/plugin.cfg", "res://gymkhana/addons/escoria-ui-return-monkey-island/plugin.cfg", "res://gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/plugin.cfg", "res://addons/escoria-wizard/plugin.cfg" ) [escoria] @@ -882,7 +888,7 @@ ui/inventory_items_path="res://gymkhana/items/inventory" ui/default_transition="instant" ui/transition_paths=[ "res://addons/escoria-core/game/scenes/transitions/shaders/" ] ui/inventory_item_size=Vector2( 72, 72 ) -ui/dialog_managers=[ "res://addons/escoria-dialog-simple/esc_dialog_simple.gd" ] +ui/dialog_managers=[ "res://gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/esc_dialog_simple.gd" ] sound/master_volume=1 sound/music_volume=1 sound/sfx_volume=1 @@ -894,8 +900,7 @@ platform/skip_cache.mobile=true sound/speech_enabled=1 ui/tooltip_follows_mouse=false main/escoria_version="" -ui/dialogs_chooser="res://gymkhana/addons/escoria-ui-return-monkey-island-dialog-simple/text_dialog_chooser.tscn" -ui/default_dialog_scene="res://addons/escoria-core/ui_library/dialogs/floating_dialog_player.tscn" +ui/default_dialog_scene="res://addons/escoria-core/game/scenes/dialogs/esc_dialog_player.gd" main/action_default_script="res://action_defaults.esc" dialog_simple/text_speed_per_character=0.1 dialog_simple/fast_text_speed_per_character=0.25 @@ -908,6 +913,7 @@ dialog_simple/reading_speed_in_wpm=200 dialog_simple/left_click_action="Speed up" dialog_simple/stop_talking_animation_on="End of audio" debug/enable_hover_stack_viewer=true +ui/dialogs_chooser="res://addons/escoria-core/game/scenes/dialogs/esc_dialog_options_chooser.gd" [input] @@ -922,6 +928,11 @@ switch_action_verb={ , Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":5,"pressed":false,"doubleclick":false,"script":null) ] } +ui_show_hints={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":16777237,"unicode":0,"echo":false,"script":null) + ] +} [locale]