feat: Made dialogs pluggable (#424)
Co-authored-by: Dennis Ploeger <develop@dieploegers.de>
This commit is contained in:
@@ -62,6 +62,8 @@ func run(command_params: Array) -> int:
|
||||
)
|
||||
|
||||
escoria.event_manager.interrupt_running_event()
|
||||
if escoria.dialog_player:
|
||||
escoria.dialog_player.interrupt()
|
||||
|
||||
if !command_params[1]:
|
||||
escoria.main.scene_transition.transition(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# `say object text [type] [avatar]`
|
||||
# `say player text [type]`
|
||||
#
|
||||
# Runs the specified string as a dialog said by the object. Blocks execution
|
||||
# Runs the specified string as a dialog said by the player. Blocks execution
|
||||
# until the dialog finishes playing.
|
||||
#
|
||||
# The text supports translation keys by prepending the key and separating
|
||||
@@ -11,8 +11,6 @@
|
||||
# Optional parameters:
|
||||
#
|
||||
# * "type" determines the type of dialog UI to use. Default value is "default"
|
||||
# * "avatar" determines the avatar to use for the dialog. Default value is
|
||||
# "default"
|
||||
#
|
||||
# @ESC
|
||||
extends ESCBaseCommand
|
||||
@@ -23,42 +21,41 @@ class_name SayCommand
|
||||
func configure() -> ESCCommandArgumentDescriptor:
|
||||
return ESCCommandArgumentDescriptor.new(
|
||||
2,
|
||||
[TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING],
|
||||
[TYPE_STRING, TYPE_STRING, TYPE_STRING],
|
||||
[
|
||||
null,
|
||||
null,
|
||||
ProjectSettings.get_setting("escoria/ui/default_dialog_scene")\
|
||||
.get_file().get_basename(),
|
||||
"default"
|
||||
ProjectSettings.get_setting("escoria/ui/default_dialog_type")
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
|
||||
# Validate wether the given arguments match the command descriptor
|
||||
func validate(arguments: Array):
|
||||
if not escoria.object_manager.objects.has(arguments[0]):
|
||||
escoria.logger.report_errors(
|
||||
"anim: invalid object",
|
||||
[
|
||||
"Object with global id %s not found." % arguments[0]
|
||||
]
|
||||
)
|
||||
return false
|
||||
if ProjectSettings.get_setting("escoria/ui/default_dialog_type") == "" \
|
||||
and arguments[2] == "":
|
||||
escoria.logger.report_errors(
|
||||
"say()",
|
||||
[
|
||||
"Project setting 'escoria/ui/default_dialog_type' is not set.",
|
||||
"Please set a default dialog type."
|
||||
]
|
||||
)
|
||||
return .validate(arguments)
|
||||
|
||||
|
||||
# Run the command
|
||||
func run(command_params: Array) -> int:
|
||||
|
||||
var dict: Dictionary
|
||||
var dialog_scene_name = ProjectSettings.get_setting(
|
||||
"escoria/ui/default_dialog_scene"
|
||||
)
|
||||
|
||||
if dialog_scene_name.empty():
|
||||
escoria.logger.report_errors(
|
||||
"say()",
|
||||
[
|
||||
"Project setting 'escoria/ui/default_dialog_scene' is not set.",
|
||||
"Please set a default dialog scene."
|
||||
]
|
||||
)
|
||||
return ESCExecution.RC_ERROR
|
||||
|
||||
var file = dialog_scene_name.get_file()
|
||||
var extension = dialog_scene_name.get_extension()
|
||||
dialog_scene_name = file.rstrip("." + extension)
|
||||
|
||||
# Manage specific dialog scene
|
||||
if command_params.size() > 2:
|
||||
dialog_scene_name = command_params[2]
|
||||
|
||||
escoria.current_state = escoria.GAME_STATE.DIALOG
|
||||
|
||||
@@ -74,9 +71,9 @@ func run(command_params: Array) -> int:
|
||||
|
||||
escoria.dialog_player.say(
|
||||
command_params[0],
|
||||
dialog_scene_name,
|
||||
command_params[2],
|
||||
command_params[1]
|
||||
)
|
||||
yield(escoria.dialog_player, "dialog_line_finished")
|
||||
yield(escoria.dialog_player, "say_finished")
|
||||
escoria.current_state = escoria.GAME_STATE.DEFAULT
|
||||
return ESCExecution.RC_OK
|
||||
|
||||
@@ -53,7 +53,7 @@ var resource_cache: ESCResourceCache
|
||||
var room_terrain
|
||||
|
||||
# Dialog player instantiator. This instance is called directly for dialogs.
|
||||
var dialog_player: ESCDialogsPlayer
|
||||
var dialog_player: ESCDialogPlayer
|
||||
|
||||
# Inventory scene
|
||||
var inventory
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# A base class for dialog plugins to work with Escoria
|
||||
extends Control
|
||||
class_name ESCDialogManager
|
||||
|
||||
|
||||
# Emitted when the say function has completed showing the text
|
||||
signal say_finished
|
||||
|
||||
# Emitted when the player has chosen an option
|
||||
signal option_chosen(option)
|
||||
|
||||
|
||||
# Check wether a specific type is supported by the
|
||||
# dialog plugin
|
||||
#
|
||||
# #### Parameters
|
||||
# - type: required type
|
||||
# *Returns* Wether the type is supported or not
|
||||
func has_type(type: String) -> bool:
|
||||
return false
|
||||
|
||||
|
||||
# Check wether a specific chooser type is supported by the
|
||||
# dialog plugin
|
||||
#
|
||||
# #### Parameters
|
||||
# - type: required chooser type
|
||||
# *Returns* Wether the type is supported or not
|
||||
func has_chooser_type(type: String) -> bool:
|
||||
return false
|
||||
|
||||
|
||||
# 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):
|
||||
pass
|
||||
|
||||
|
||||
# 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
|
||||
func choose(dialog_player: Node, dialog: ESCDialog):
|
||||
pass
|
||||
|
||||
|
||||
# Trigger running the dialog faster
|
||||
func speedup():
|
||||
pass
|
||||
|
||||
|
||||
# The say command has been interrupted, cancel the dialog display
|
||||
func interrupt():
|
||||
pass
|
||||
@@ -1,5 +1,5 @@
|
||||
# Base class for all dialog options implementations
|
||||
extends Node
|
||||
extends Control
|
||||
class_name ESCDialogOptionsChooser
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Escoria dialog player
|
||||
extends ResourcePreloader
|
||||
class_name ESCDialogsPlayer
|
||||
extends Node
|
||||
class_name ESCDialogPlayer
|
||||
|
||||
|
||||
# Emitted when an answer as chosem
|
||||
@@ -10,38 +10,25 @@ class_name ESCDialogsPlayer
|
||||
# - option: The dialog option that was chosen
|
||||
signal option_chosen(option)
|
||||
|
||||
# Emitted when a dialog line was finished
|
||||
signal dialog_line_finished
|
||||
# Emitted when a say command finished
|
||||
signal say_finished
|
||||
|
||||
|
||||
# Wether the player is currently speaking
|
||||
var is_speaking = false
|
||||
var is_speaking: bool = false
|
||||
|
||||
|
||||
# Reference to the dialog UI
|
||||
var _dialog_ui = null
|
||||
|
||||
# Reference to the dialog chooser UI
|
||||
var _dialog_chooser_ui: ESCDialogOptionsChooser = null
|
||||
# Reference to the currently playing dialog manager
|
||||
var _dialog_manager: ESCDialogManager = null
|
||||
|
||||
|
||||
# Register the dialog player and load the dialog resources
|
||||
func _ready():
|
||||
if !Engine.is_editor_hint():
|
||||
escoria.dialog_player = self
|
||||
_dialog_chooser_ui = ResourceLoader.load(
|
||||
ProjectSettings.get_setting("escoria/ui/dialogs_chooser")
|
||||
).instance()
|
||||
assert(_dialog_chooser_ui is ESCDialogOptionsChooser)
|
||||
_dialog_chooser_ui.connect(
|
||||
"option_chosen",
|
||||
self,
|
||||
"play_dialog_option_chosen"
|
||||
)
|
||||
get_parent().call_deferred("add_child", _dialog_chooser_ui)
|
||||
|
||||
|
||||
# Trigger the finish fast function on the dialog ui
|
||||
# Trigger the speedup function in the dialog manager
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
@@ -49,9 +36,17 @@ func _ready():
|
||||
func _input(event):
|
||||
if event is InputEventMouseButton and \
|
||||
event.pressed:
|
||||
finish_fast()
|
||||
speedup()
|
||||
|
||||
|
||||
# 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 = ProjectSettings.get("escoria/sound/speech_folder")
|
||||
@@ -77,40 +72,54 @@ func _get_voice_file(key: String, start: String = "") -> String:
|
||||
return ""
|
||||
|
||||
|
||||
# A short one line dialog
|
||||
# Make a character say a text
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - character: Character that is talking
|
||||
# - ui: UI to use for the dialog
|
||||
# - line: Line to say
|
||||
func say(character: String, ui: String, line: String) -> void:
|
||||
# - type: UI to use for the dialog
|
||||
# - text: Text to say
|
||||
func say(character: String, type: String, text: String) -> void:
|
||||
is_speaking = true
|
||||
_dialog_ui = get_resource(ui).instance()
|
||||
get_parent().add_child(_dialog_ui)
|
||||
var _key_line = line.split(":")
|
||||
if _key_line.size() == 1:
|
||||
line = _key_line[0]
|
||||
elif _key_line.size() >= 2:
|
||||
var _speech_resource = _get_voice_file(_key_line[0])
|
||||
for _manager_class in ProjectSettings.get_setting(
|
||||
"escoria/ui/dialog_managers"
|
||||
):
|
||||
if ResourceLoader.exists(_manager_class):
|
||||
var _manager: ESCDialogManager = load(_manager_class).new()
|
||||
if _manager.has_type(type):
|
||||
_dialog_manager = _manager
|
||||
|
||||
if _dialog_manager == null:
|
||||
escoria.logger.report_errors(
|
||||
"esc_dialog_player.gd: Unknown type",
|
||||
[
|
||||
"No dialog manager supports the type %s" % type
|
||||
]
|
||||
)
|
||||
var _key_text = text.split(":")
|
||||
if _key_text.size() == 1:
|
||||
text = _key_text[0]
|
||||
elif _key_text.size() >= 2:
|
||||
var _speech_resource = _get_voice_file(_key_text[0])
|
||||
if _speech_resource != "":
|
||||
(
|
||||
escoria.object_manager.get_object("_speech").node\
|
||||
as ESCSpeechPlayer
|
||||
).set_state(_speech_resource)
|
||||
line = tr(_key_line[0])
|
||||
|
||||
_dialog_ui.say(character, line)
|
||||
yield(_dialog_ui, "dialog_line_finished")
|
||||
text = tr(_key_text[0])
|
||||
|
||||
_dialog_manager.say(self, character, text, type)
|
||||
yield(_dialog_manager, "say_finished")
|
||||
is_speaking = false
|
||||
emit_signal("dialog_line_finished")
|
||||
emit_signal("say_finished")
|
||||
|
||||
|
||||
# Called when a dialog line is skipped
|
||||
func finish_fast() -> void:
|
||||
if is_speaking and\
|
||||
escoria.inputs_manager.input_mode != escoria.inputs_manager.INPUT_NONE:
|
||||
_dialog_ui.finish_fast()
|
||||
func speedup() -> void:
|
||||
if is_speaking and escoria.inputs_manager.input_mode != \
|
||||
escoria.inputs_manager.INPUT_NONE and \
|
||||
_dialog_manager != null:
|
||||
_dialog_manager.speedup()
|
||||
|
||||
|
||||
# Display a list of choices
|
||||
@@ -118,22 +127,37 @@ func finish_fast() -> void:
|
||||
# #### Parameters
|
||||
#
|
||||
# - dialog: The dialog to start
|
||||
func start_dialog_choices(dialog: ESCDialog):
|
||||
func start_dialog_choices(dialog: ESCDialog, type: String = "simple"):
|
||||
if dialog.options.empty():
|
||||
escoria.logger.report_errors(
|
||||
"dialog_player.gd:start_dialog_choices()",
|
||||
["Received answers array was empty."]
|
||||
)
|
||||
_dialog_chooser_ui.set_dialog(dialog)
|
||||
_dialog_chooser_ui.show_chooser()
|
||||
|
||||
|
||||
# Called when an option was chosen and emits the option_chosen signal
|
||||
#
|
||||
# #### Parameters
|
||||
#
|
||||
# - option: Option, that was chosen.
|
||||
func play_dialog_option_chosen(option: ESCDialogOption):
|
||||
|
||||
var _dialog_chooser_ui: ESCDialogManager = null
|
||||
|
||||
for _manager_class in ProjectSettings.get_setting(
|
||||
"escoria/ui/dialog_managers"
|
||||
):
|
||||
if ResourceLoader.exists(_manager_class):
|
||||
var _manager: ESCDialogManager = load(_manager_class).new()
|
||||
if _manager.has_chooser_type(type):
|
||||
_dialog_chooser_ui = _manager
|
||||
|
||||
if _dialog_chooser_ui == null:
|
||||
escoria.logger.report_errors(
|
||||
"esc_dialog_player.gd: Unknown chooser type",
|
||||
[
|
||||
"No dialog manager supports the chooser type %s" % type
|
||||
]
|
||||
)
|
||||
|
||||
_dialog_chooser_ui.choose(self, dialog)
|
||||
var option = yield(_dialog_chooser_ui, "option_chosen")
|
||||
emit_signal("option_chosen", option)
|
||||
_dialog_chooser_ui.hide_chooser()
|
||||
|
||||
|
||||
# Interrupt the currently running dialog
|
||||
func interrupt():
|
||||
if _dialog_manager != null:
|
||||
_dialog_manager.interrupt()
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
[gd_scene load_steps=5 format=2]
|
||||
|
||||
[ext_resource path="res://addons/escoria-core/ui_library/dialogs/floating_dialog_player.tscn" type="PackedScene" id=1]
|
||||
[ext_resource path="res://addons/escoria-core/ui_library/dialogs/text_dialog_chooser.tscn" type="PackedScene" id=2]
|
||||
[ext_resource path="res://addons/escoria-core/game/scenes/dialogs/esc_dialog_player.gd" type="Script" id=3]
|
||||
[ext_resource path="res://addons/escoria-core/ui_library/dialogs/avatar_dialog_player.tscn" type="PackedScene" id=4]
|
||||
|
||||
[node name="dialog_player" type="ResourcePreloader"]
|
||||
resources = [ PoolStringArray( "avatar_dialog_player", "floating_dialog_player", "text_dialog_choice" ), [ ExtResource( 4 ), ExtResource( 1 ), ExtResource( 2 ) ] ]
|
||||
script = ExtResource( 3 )
|
||||
Reference in New Issue
Block a user