feat: Made dialogs pluggable (#424)
Co-authored-by: Dennis Ploeger <develop@dieploegers.de>
This commit is contained in:
@@ -1,111 +0,0 @@
|
||||
# A dialog GUI showing a dialog box and character portraits
|
||||
extends Popup
|
||||
|
||||
|
||||
# Signal emitted when a dialog line has started
|
||||
signal dialog_line_started
|
||||
|
||||
# Signal emitted when a dialog line has finished
|
||||
signal dialog_line_finished
|
||||
|
||||
|
||||
# The currently speaking character
|
||||
export(String) var current_character setget set_current_character
|
||||
|
||||
# The text speed per character for normal display
|
||||
export(float, 0.0, 0.3) var text_speed_per_character = 0.1
|
||||
|
||||
# The text speed per character if the dialog line is skipped
|
||||
export(float) var fast_text_speed_per_character = 0.25
|
||||
|
||||
# The time to wait before the dialog is finished
|
||||
export(float) var max_time_to_text_disappear = 1.0
|
||||
|
||||
|
||||
# The node holding the avatar
|
||||
onready var avatar_node = $Panel/MarginContainer/HSplitContainer/VBoxContainer\
|
||||
/avatar
|
||||
|
||||
# The node holding the player name
|
||||
onready var name_node = $Panel/MarginContainer/HSplitContainer/VBoxContainer\
|
||||
/name
|
||||
|
||||
# The node showing the text
|
||||
onready var text_node = $Panel/MarginContainer/HSplitContainer/text
|
||||
|
||||
# The tween node for text animations
|
||||
onready var tween = text_node.get_node("Tween")
|
||||
|
||||
|
||||
# Build up the UI
|
||||
func _ready():
|
||||
var centered_position_on_screen = Vector2(
|
||||
ProjectSettings.get_setting("display/window/size/width") / 2,
|
||||
ProjectSettings.get_setting("display/window/size/height") / 2
|
||||
) - rect_size / 2
|
||||
rect_position = centered_position_on_screen
|
||||
text_node.bbcode_enabled = true
|
||||
$Panel/MarginContainer/HSplitContainer/text/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):
|
||||
# TODO: Make this configurable in #47
|
||||
var avatar = "res://game/dialog_avatars/%s.tres" % name
|
||||
if ResourceLoader.exists(avatar):
|
||||
$Panel/MarginContainer/HSplitContainer/VBoxContainer/avatar.texture = \
|
||||
ResourceLoader.load(avatar)
|
||||
current_character = name
|
||||
|
||||
|
||||
# Make a character say something
|
||||
#
|
||||
# #### Parameters
|
||||
# - character: The global id of the character speaking
|
||||
# - line: Line to say
|
||||
func say(character: String, line: String) :
|
||||
popup_centered()
|
||||
emit_signal("dialog_line_started")
|
||||
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 finish_fast():
|
||||
tween.stop(text_node)
|
||||
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("dialog_line_finished")
|
||||
queue_free()
|
||||
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/escoria-core/ui_library/dialogs/avatar_dialog_player.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="name" type="Label" parent="Panel/MarginContainer/HSplitContainer/VBoxContainer"]
|
||||
margin_top = 112.0
|
||||
margin_right = 88.0
|
||||
margin_bottom = 126.0
|
||||
valign = 1
|
||||
|
||||
[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"]
|
||||
@@ -1,94 +0,0 @@
|
||||
# A dialog UI using a label above the head of the character
|
||||
extends RichTextLabel
|
||||
|
||||
|
||||
# Signal emitted when a dialog line has started
|
||||
signal dialog_line_started
|
||||
|
||||
# Signal emitted when a dialog line has finished
|
||||
signal dialog_line_finished
|
||||
|
||||
|
||||
# The text speed per character for normal display
|
||||
export(float, 0.0, 0.3) var text_speed_per_character = 0.1
|
||||
|
||||
# The text speed per character if the dialog line is skipped
|
||||
export(float) var fast_text_speed_per_character = 0.25
|
||||
|
||||
# The time to wait before the dialog is finished
|
||||
export(float) var max_time_to_text_disappear = 2.0
|
||||
|
||||
|
||||
# Current character speaking, to keep track of reference for animation purposes
|
||||
var current_character
|
||||
|
||||
|
||||
# Tween node for text animation
|
||||
onready var tween = $Tween
|
||||
|
||||
# The node showing the text
|
||||
onready var text_node = self
|
||||
|
||||
|
||||
# Enable bbcode and catch the signal when a tween completed
|
||||
func _ready():
|
||||
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()
|
||||
|
||||
emit_signal("dialog_line_started")
|
||||
|
||||
# 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 finish_fast():
|
||||
tween.stop(text_node)
|
||||
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():
|
||||
current_character.stop_talking()
|
||||
emit_signal("dialog_line_finished")
|
||||
escoria.dialog_player.is_speaking = false
|
||||
queue_free()
|
||||
@@ -1,20 +0,0 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/escoria-core/ui_library/dialogs/floating_dialog_player.gd" type="Script" id=2]
|
||||
|
||||
[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( 2 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
text_speed_per_character = 0.0
|
||||
|
||||
[node name="Tween" type="Tween" parent="."]
|
||||
|
||||
[node name="Timer" type="Timer" parent="."]
|
||||
@@ -1,83 +0,0 @@
|
||||
# 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)
|
||||
export(Font) var font
|
||||
|
||||
|
||||
# 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)
|
||||
_option_node.add_font_override("font", font)
|
||||
_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))
|
||||
@@ -1,61 +0,0 @@
|
||||
[gd_scene load_steps=4 format=2]
|
||||
|
||||
[ext_resource path="res://addons/escoria-core/ui_library/dialogs/text_dialog_chooser.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="Node"]
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[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"]
|
||||
Reference in New Issue
Block a user