diff options
Diffstat (limited to 'addons/escoria-dialog-simple/esc_dialog_simple.gd')
| -rw-r--r-- | addons/escoria-dialog-simple/esc_dialog_simple.gd | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/addons/escoria-dialog-simple/esc_dialog_simple.gd b/addons/escoria-dialog-simple/esc_dialog_simple.gd new file mode 100644 index 0000000..6966159 --- /dev/null +++ b/addons/escoria-dialog-simple/esc_dialog_simple.gd @@ -0,0 +1,211 @@ +## A simple dialog manager for Escoria +extends ESCDialogManager + + +## State machine that governs how the dialog manager behaves +var state_machine = preload("res://addons/escoria-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 +# +# #### Parameters +# - 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 + + +# Check whether a specific chooser type is supported by the +# dialog plugin +# +# #### Parameters +# - type: required chooser type +# **Returns** Whether the type is supported or not +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. +# +# #### Parameters +# - dialog_player: Node of the dialog player in the UI +# - global_id: Global id of the item that is speaking +# - text: Text to say, optional prefixed by a translation key separated +# by a ":" +# - type: Type of dialog box to use +# - *key*: Translation key +func say(dialog_player: Node, global_id: String, text: String, type: String, key: String): + _dialog_player = dialog_player + + _initialize_say_states(global_id, text, type, key) + + 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 is_instance_valid(_type_player) and _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://addons/escoria-dialog-simple/types/floating.tscn"\ + ).instantiate() + else: + _type_player = preload(\ + "res://addons/escoria-dialog-simple/types/avatar.tscn"\ + ).instantiate() + + _type_player.say_finished.connect(_on_say_finished) + _type_player.say_visible.connect(_on_say_visible) + + +func _initialize_say_states(global_id: String, text: String, type: String, key: String) -> void: + state_machine.states_map["say"].initialize(self, global_id, text, type, key) + 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 not _should_preserve_dialog_box and _dialog_player.get_children().has(_type_player): + _dialog_player.remove_child(_type_player) + + _is_saying = false + + say_finished.emit() + + +func _on_say_visible(): + say_visible.emit() + + +# Present an option chooser to the player and sends the signal +# `option_chosen` with the chosen dialog option +# +# #### 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): + _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://addons/escoria-dialog-simple/chooser/simple.tscn"\ + ).instantiate() + + dialog_player.add_child(chooser) + chooser.set_dialog(dialog) + chooser.show_chooser() + + var option = await chooser.option_chosen + dialog_player.remove_child(chooser) + option_chosen.emit(option) + + +# Trigger running the dialogue faster +func speedup(): + if is_instance_valid(_type_player): + _type_player.speedup() + + +# Trigger an instant finish of the current dialog +func finish(): + if is_instance_valid(_type_player): + _type_player.finish() + + +# The say command has been interrupted, cancel the dialog display +func interrupt(): + if _dialog_player.get_children().has(_type_player): + ( + escoria.object_manager.get_object(escoria.object_manager.SPEECH).node\ + as ESCSpeechPlayer + ).set_state("off") + + if not _should_preserve_dialog_box and _dialog_player.get_children().has(_type_player): + _dialog_player.remove_child(_type_player) + + say_finished.emit() + + +# To be called if voice audio has finished. +func voice_audio_finished(): + if is_instance_valid(_type_player): + _type_player.voice_audio_finished() |
