214 lines
6.5 KiB
GDScript
214 lines
6.5 KiB
GDScript
# 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
|
|
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")
|
|
|
|
# yield(_type_player, "say_finished")
|
|
# if _dialog_player.get_children().has(_type_player):
|
|
# _dialog_player.remove_child(_type_player)
|
|
# emit_signal("say_finished")
|
|
|
|
|
|
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"\
|
|
).instance()
|
|
else:
|
|
_type_player = preload(\
|
|
"res://addons/escoria-dialog-simple/types/avatar.tscn"\
|
|
).instance()
|
|
|
|
_type_player.connect("say_finished", self, "_on_say_finished")
|
|
_type_player.connect("say_visible", self, "_on_say_visible")
|
|
|
|
|
|
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 not _should_preserve_dialog_box and _dialog_player.get_children().has(_type_player):
|
|
_dialog_player.remove_child(_type_player)
|
|
|
|
_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
|
|
#
|
|
# #### 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"\
|
|
).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)
|
|
|
|
|
|
# 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)
|
|
|
|
emit_signal("say_finished")
|
|
|
|
|
|
# To be called if voice audio has finished.
|
|
func voice_audio_finished():
|
|
if is_instance_valid(_type_player):
|
|
_type_player.voice_audio_finished()
|