feat: Made dialogs pluggable (#424)

Co-authored-by: Dennis Ploeger <develop@dieploegers.de>
This commit is contained in:
Dennis Ploeger
2021-10-27 08:34:13 +02:00
committed by GitHub
parent 29dffbcf32
commit 57ce7fbcae
26 changed files with 633 additions and 305 deletions

View File

@@ -0,0 +1,81 @@
# A simple dialog chooser that shows selectable lines of text
# Supports timeout and avatar display
extends ESCDialogOptionsChooser
export(Color, RGB) var color_normal = Color(1.0,1.0,1.0,1.0)
export(Color, RGB) var color_hover = Color(165.0,42.0,42.0, 1.0)
# Hide the chooser at the start just to be safe
func _ready() -> void:
hide_chooser()
# Process the timeout display
func _process(delta: float) -> void:
if $MarginContainer.visible and self.dialog and self.dialog.timeout > 0:
$TimerProgress.value = (
self.dialog.timeout - $Timer.time_left
) / self.dialog.timeout * 100
# Show the chooser
func show_chooser():
var _vbox = $MarginContainer/ScrollContainer/VBoxContainer
for option_node in _vbox.get_children():
_vbox.remove_child(option_node)
_remove_avatar()
for option in self.dialog.options:
var _option_node = Button.new()
_option_node.text = (option as ESCDialogOption).option
_option_node.flat = true
_option_node.add_color_override("font_color", color_normal)
_option_node.add_color_override("font_color_hover", color_hover)
_vbox.add_child(_option_node)
_option_node.connect("pressed", self, "_on_answer_selected", [option])
if self.dialog.avatar != "-":
$AvatarContainer.add_child(
ResourceLoader.load(self.dialog.avatar).instance()
)
$MarginContainer.show()
if self.dialog.timeout > 0:
$Timer.start(self.dialog.timeout)
# Hide the chooser
func hide_chooser():
$MarginContainer.hide()
# An option was choosen, emit the option
#
# #### Parameters
# - option: Option that was chosen
func _option_chosen(option: ESCDialogOption):
_remove_avatar()
$TimerProgress.value = 0
emit_signal("option_chosen", option)
# An option was chosen directly from the list
#
# #### Parameters
# - option: Option that was chosen
func _on_answer_selected(option: ESCDialogOption):
_option_chosen(option)
# The timeout came and a option was selected
func _on_Timer_timeout() -> void:
_option_chosen(self.dialog.options[self.dialog.timeout_option - 1])
# Remove the avatar
func _remove_avatar():
if $AvatarContainer.get_child_count() > 0:
$AvatarContainer.remove_child($AvatarContainer.get_child(0))

View File

@@ -0,0 +1,66 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://addons/escoria-dialog-simple/chooser/simple.gd" type="Script" id=1]
[sub_resource type="Gradient" id=1]
colors = PoolColorArray( 1, 0, 0, 1, 1, 0, 0, 1 )
[sub_resource type="GradientTexture" id=2]
gradient = SubResource( 1 )
[node name="text_dialog_choice" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="MarginContainer" type="MarginContainer" parent="."]
margin_left = 20.0
margin_top = 20.0
margin_right = 1280.0
margin_bottom = 900.0
mouse_filter = 2
custom_constants/margin_top = 20
custom_constants/margin_left = 20
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer"]
margin_left = 20.0
margin_top = 20.0
margin_right = 1260.0
margin_bottom = 880.0
scroll_horizontal_enabled = false
__meta__ = {
"_edit_use_anchors_": false
}
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/ScrollContainer"]
margin_right = 1240.0
margin_bottom = 860.0
size_flags_horizontal = 3
size_flags_vertical = 3
custom_constants/separation = 20
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Timer" type="Timer" parent="."]
one_shot = true
[node name="TimerProgress" type="TextureProgress" parent="."]
anchor_right = 1.0
rect_min_size = Vector2( 0, 20 )
texture_progress = SubResource( 2 )
nine_patch_stretch = true
__meta__ = {
"_edit_use_anchors_": false
}
[node name="AvatarContainer" type="Node2D" parent="."]
position = Vector2( 29.1458, 120.012 )
[connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"]

View File

@@ -0,0 +1,88 @@
# A simple dialog manager for Escoria
extends ESCDialogManager
class_name ESCDialogSimple
# The currently running player
var _type_player: Node = null
# Reference to the dialog player
var _dialog_player: Node = null
# 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 true if type in ["floating", "avatar"] else 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 true if type == "simple" else 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):
_dialog_player = dialog_player
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()
_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")
# 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):
var 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 dialog faster
func speedup():
if _type_player != null:
_type_player.speedup()
# The say command has been interrupted, cancel the dialog display
func interrupt():
if _dialog_player.get_children().has(_type_player):
_dialog_player.remove_child(_type_player)
emit_signal("say_finished")

View File

@@ -0,0 +1,7 @@
[plugin]
name="Escoria Simple Dialogs"
description="Very basic dialogs for Escoria based games"
author="Escoria project"
version="0.1.0"
script="plugin.gd"

View File

@@ -0,0 +1,92 @@
# A simple dialog manager for Escoria
tool
extends EditorPlugin
const MANAGER_CLASS="res://addons/escoria-dialog-simple/esc_dialog_simple.gd"
# Register ourselves after setup
func _enter_tree() -> void:
call_deferred("_register")
# Unregister ourselves
func _exit_tree() -> void:
_unregister()
# Add ourselves to the list of dialog managers
func _register():
_unregister()
var dialog_managers: Array = ProjectSettings.get_setting(
"escoria/ui/dialog_managers"
)
dialog_managers.push_back(MANAGER_CLASS)
ProjectSettings.set_setting(
"escoria/ui/dialog_managers",
dialog_managers
)
_register_setting(
"escoria/dialog_simple/avatars_path",
"",
{
"type": TYPE_STRING,
"hint": PROPERTY_HINT_DIR
}
)
_register_setting(
"escoria/dialog_simple/text_speed_per_character",
0.1,
{
"type": TYPE_REAL
}
)
_register_setting(
"escoria/dialog_simple/fast_text_speed_per_character",
0.25,
{
"type": TYPE_REAL
}
)
_register_setting(
"escoria/dialog_simple/max_time_to_disappear",
1.0,
{
"type": TYPE_REAL
}
)
# Remove ourselves from the list of dialog managers
func _unregister():
var dialog_managers = ProjectSettings.get_setting(
"escoria/ui/dialog_managers"
)
dialog_managers.erase(MANAGER_CLASS)
ProjectSettings.set_setting(
"escoria/ui/dialog_managers",
dialog_managers
)
# Register a new project setting if it hasn't been defined already
#
# #### Parameters
#
# - name: Name of the project setting
# - default: Default value
# - info: Property info for the setting
func _register_setting(name: String, default, info: Dictionary):
if not ProjectSettings.has_setting(name):
ProjectSettings.set_setting(
name,
default
)
info.name = name
ProjectSettings.add_property_info(info)

View File

@@ -0,0 +1,109 @@
# A dialog GUI showing a dialog box and character portraits
extends Popup
# Signal emitted when text has been said
signal say_finished
# The text speed per character for normal display
var _text_speed_per_character
# The text speed per character if the dialog line is skipped
var _fast_text_speed_per_character
# The time to wait before the dialog is finished
var _max_time_to_text_disappear
# Wether the current dialog is speeding up
var _is_speeding_up: bool = false
# The node holding the avatar
onready var avatar_node = $Panel/MarginContainer/HSplitContainer/VBoxContainer\
/avatar
# The node showing the text
onready var text_node = $Panel/MarginContainer/HSplitContainer/text
# The tween node for text animations
onready var tween = $Panel/MarginContainer/HSplitContainer/text/Tween
# Build up the UI
func _ready():
_text_speed_per_character = ProjectSettings.get_setting(
"escoria/dialog_simple/text_speed_per_character"
)
_fast_text_speed_per_character = ProjectSettings.get_setting(
"escoria/dialog_simple/fast_text_speed_per_character"
)
_max_time_to_text_disappear = ProjectSettings.get_setting(
"escoria/dialog_simple/max_time_to_disappear"
)
text_node.bbcode_enabled = true
tween.connect(
"tween_completed",
self,
"_on_dialog_line_typed"
)
# Switch the current character
#
# #### Parameters
# - name: The name of the current character
func set_current_character(name: String):
var avatar = "%s/%s.tres" % [
ProjectSettings.get_setting("escoria/dialog_simple/avatars_path"),
name
]
if ResourceLoader.exists(avatar):
avatar_node.texture = ResourceLoader.load(avatar)
# Make a character say something
#
# #### Parameters
# - character: The global id of the character speaking
# - line: Line to say
func say(character: String, line: String):
_is_speeding_up = false
popup_centered()
set_current_character(character)
text_node.bbcode_text = tr(line)
text_node.percent_visible = 0.0
var time_show_full_text = _text_speed_per_character * len(line)
tween.interpolate_property(text_node, "percent_visible",
0.0, 1.0, time_show_full_text,
Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
tween.start()
# Called by the dialog player when the
func speedup():
if not _is_speeding_up:
_is_speeding_up = true
tween.remove_all()
tween.interpolate_property(text_node, "percent_visible",
text_node.percent_visible, 1.0, _fast_text_speed_per_character,
Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
tween.start()
# The dialog line was printed, start the waiting time and then finish
# the dialog
func _on_dialog_line_typed(object, key):
text_node.visible_characters = -1
$Timer.start(_max_time_to_text_disappear)
$Timer.connect("timeout", self, "_on_dialog_finished")
# Ending the dialog
func _on_dialog_finished():
emit_signal("say_finished")
queue_free()

View File

@@ -0,0 +1,68 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/escoria-dialog-simple/types/avatar.gd" type="Script" id=1]
[node name="dialog_box" type="Popup"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_right = -782.0
margin_bottom = -734.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Timer" type="Timer" parent="."]
[node name="Panel" type="Panel" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="MarginContainer" type="MarginContainer" parent="Panel"]
anchor_right = 1.0
anchor_bottom = 1.0
custom_constants/margin_right = 20
custom_constants/margin_top = 20
custom_constants/margin_left = 20
custom_constants/margin_bottom = 20
__meta__ = {
"_edit_use_anchors_": false
}
[node name="HSplitContainer" type="HSplitContainer" parent="Panel/MarginContainer"]
margin_left = 20.0
margin_top = 20.0
margin_right = 478.0
margin_bottom = 146.0
custom_constants/separation = 35
dragger_visibility = 1
[node name="VBoxContainer" type="VBoxContainer" parent="Panel/MarginContainer/HSplitContainer"]
margin_right = 88.0
margin_bottom = 126.0
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.3
[node name="avatar" type="TextureRect" parent="Panel/MarginContainer/HSplitContainer/VBoxContainer"]
margin_right = 88.0
margin_bottom = 108.0
size_flags_horizontal = 3
size_flags_vertical = 3
expand = true
[node name="text" type="RichTextLabel" parent="Panel/MarginContainer/HSplitContainer"]
margin_left = 123.0
margin_right = 458.0
margin_bottom = 126.0
size_flags_horizontal = 3
bbcode_enabled = true
bbcode_text = "Here be some text"
text = "Here be some text"
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Tween" type="Tween" parent="Panel/MarginContainer/HSplitContainer/text"]

View File

@@ -0,0 +1,105 @@
# A dialog UI using a label above the head of the character
extends RichTextLabel
# Signal emitted when text has been said
signal say_finished
# The text speed per character for normal display
var _text_speed_per_character
# The text speed per character if the dialog line is skipped
var _fast_text_speed_per_character
# The time to wait before the dialog is finished
var _max_time_to_text_disappear
# Current character speaking, to keep track of reference for animation purposes
var _current_character
# Wether the current dialog is speeding up
var _is_speeding_up: bool = false
# Tween node for text animation
onready var tween: Tween = $Tween
# The node showing the text
onready var text_node: RichTextLabel = self
# Enable bbcode and catch the signal when a tween completed
func _ready():
_text_speed_per_character = ProjectSettings.get_setting(
"escoria/dialog_simple/text_speed_per_character"
)
_fast_text_speed_per_character = ProjectSettings.get_setting(
"escoria/dialog_simple/fast_text_speed_per_character"
)
_max_time_to_text_disappear = ProjectSettings.get_setting(
"escoria/dialog_simple/max_time_to_disappear"
)
bbcode_enabled = true
$Tween.connect("tween_completed", self, "_on_dialog_line_typed")
# Make a character say something
#
# #### Parameters
# - character: The global id of the character speaking
# - line: Line to say
func say(character: String, line: String) :
show()
_is_speeding_up = false
# Position the RichTextLabel on the character's dialog position, if any.
_current_character = escoria.object_manager.get_object(character).node
rect_position = _current_character.get_node("dialog_position").get_global_transform_with_canvas().origin
rect_position.x -= rect_size.x / 2
_current_character.start_talking()
# Set text color to color set in the actor
var text_color = _current_character.dialog_color
var text_color_html = text_color.to_html(false)
text_node.bbcode_text = "[center][color=#" + text_color_html + "]" \
.format([text_color_html]) + tr(line) + "[/color][center]"
text_node.percent_visible = 0.0
var time_show_full_text = _text_speed_per_character * len(line)
tween.interpolate_property(text_node, "percent_visible",
0.0, 1.0, time_show_full_text,
Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
tween.start()
# Called by the dialog player when the
func speedup():
if not _is_speeding_up:
_is_speeding_up = true
tween.remove_all()
tween.interpolate_property(text_node, "percent_visible",
text_node.percent_visible, 1.0, _fast_text_speed_per_character,
Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
tween.start()
# The dialog line was printed, start the waiting time and then finish
# the dialog
func _on_dialog_line_typed(object, key):
text_node.visible_characters = -1
$Timer.start(_max_time_to_text_disappear)
$Timer.connect("timeout", self, "_on_dialog_finished")
# Ending the dialog
func _on_dialog_finished():
# Make the speaking item animation stop talking, if it is still alive
if is_instance_valid(_current_character) and _current_character != null:
_current_character.stop_talking()
emit_signal("say_finished")

View File

@@ -0,0 +1,20 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/escoria-dialog-simple/types/floating.gd" type="Script" id=1]
[node name="dialog_label" type="RichTextLabel"]
margin_right = 643.0
margin_bottom = 60.0
bbcode_enabled = true
bbcode_text = "[center]Here be some text.[/center]"
text = "Here be some text."
fit_content_height = true
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
text_speed_per_character = 0.0
[node name="Tween" type="Tween" parent="."]
[node name="Timer" type="Timer" parent="."]