Update
This commit is contained in:
56
demos/10. dynamic save and load/dynamic_save_dialogue.gd
Normal file
56
demos/10. dynamic save and load/dynamic_save_dialogue.gd
Normal file
@@ -0,0 +1,56 @@
|
||||
extends DialogueEngine
|
||||
|
||||
|
||||
const SAVE_PATH : String = "user://save.dat"
|
||||
var counter : int = 0
|
||||
var start_counting_id : int = 0
|
||||
var log_history : Array = []
|
||||
|
||||
|
||||
func _setup() -> void:
|
||||
add_text_entry("This is an example of an infinite dynamically generated/saved/loaded dialogue.")
|
||||
add_text_entry("You can save the dialogue progress at any time by clicking the save button above.")
|
||||
add_text_entry("And when you restart this scene, the dialogue will continue from where it left off.")
|
||||
add_text_entry("As the dialogue progresses, the graph in the debugger will update automatically as well.")
|
||||
add_text_entry("Let's count to infinity!!")
|
||||
dialogue_continued.connect(__log_history)
|
||||
dialogue_about_to_finish.connect(__continue_counting)
|
||||
|
||||
# Load previous state if any
|
||||
load_state()
|
||||
|
||||
|
||||
func __log_history(p_dialogue_entry : DialogueEntry) -> void:
|
||||
# Always track the log history:
|
||||
log_history.push_back(p_dialogue_entry.get_formatted_text())
|
||||
|
||||
func __continue_counting() -> void:
|
||||
counter += 1
|
||||
add_text_entry(str(counter))
|
||||
|
||||
|
||||
func save_state() -> void:
|
||||
var file_handle: FileAccess = FileAccess.open(SAVE_PATH, FileAccess.WRITE)
|
||||
file_handle.store_var(counter)
|
||||
file_handle.store_var(get_current_entry().get_id())
|
||||
file_handle.store_var(log_history)
|
||||
print("State Saved")
|
||||
|
||||
|
||||
func load_state() -> void:
|
||||
if FileAccess.file_exists(SAVE_PATH):
|
||||
var file_handle: FileAccess = FileAccess.open(SAVE_PATH, FileAccess.READ)
|
||||
counter = file_handle.get_var()
|
||||
var entry_id : int = file_handle.get_var()
|
||||
if has_entry_id(entry_id):
|
||||
set_current_entry(entry_id)
|
||||
else:
|
||||
set_current_entry(add_text_entry("Let's continue counting!!").get_id())
|
||||
log_history = file_handle.get_var()
|
||||
print("State Loaded")
|
||||
|
||||
|
||||
func clear_state() -> void:
|
||||
if FileAccess.file_exists(SAVE_PATH):
|
||||
DirAccess.remove_absolute(SAVE_PATH)
|
||||
print("State Cleared")
|
||||
140
demos/10. dynamic save and load/dynamic_save_load.gd
Normal file
140
demos/10. dynamic save and load/dynamic_save_load.gd
Normal file
@@ -0,0 +1,140 @@
|
||||
extends Control
|
||||
|
||||
|
||||
@export var dialogue_gdscript : GDScript = null
|
||||
var dialogue_engine : DialogueEngine = null
|
||||
|
||||
@onready var dialogue : VBoxContainer = $VBox/Dialogue
|
||||
@onready var history : CenterContainer = $History
|
||||
@onready var history_log : VBoxContainer = $History/Panel/Margin/HistoryLog
|
||||
@onready var animator : AnimationPlayer = $Animator
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
dialogue_engine = dialogue_gdscript.new()
|
||||
dialogue_engine.dialogue_started.connect(__on_dialogue_started)
|
||||
dialogue_engine.dialogue_continued.connect(__on_dialogue_continued)
|
||||
dialogue_engine.dialogue_finished.connect(__on_dialogue_finished)
|
||||
dialogue_engine.dialogue_cancelled.connect(__on_dialogue_cancelled)
|
||||
|
||||
|
||||
func _input(p_input_event : InputEvent) -> void:
|
||||
if p_input_event.is_action_pressed(&"ui_accept"):
|
||||
if animator.is_playing():
|
||||
# Player is inpatient -- auto-advance the text
|
||||
var animation_name : StringName = animator.get_current_animation()
|
||||
var animation : Animation = animator.get_animation(animation_name)
|
||||
animator.advance(animation.get_length()) # this will fire the animation_finished signal automatically else:
|
||||
else:
|
||||
# Advance current entry
|
||||
dialogue_engine.advance()
|
||||
accept_event() # accepting input event here to stop it from traversing into into buttons possibly added through the interaction
|
||||
|
||||
|
||||
func __on_dialogue_started() -> void:
|
||||
print("Dialogue Started!")
|
||||
|
||||
|
||||
func __on_dialogue_continued(p_dialogue_entry : DialogueEntry) -> void:
|
||||
# Add the text to the log:
|
||||
var label : RichTextLabel = RichTextLabel.new()
|
||||
label.set_use_bbcode(true)
|
||||
label.set_fit_content(true)
|
||||
if p_dialogue_entry.has_metadata("author"):
|
||||
var author : String = p_dialogue_entry.get_metadata("author")
|
||||
label.set_text(" > " + author + ": " + p_dialogue_entry.get_formatted_text())
|
||||
else:
|
||||
label.set_text(" > " + p_dialogue_entry.get_formatted_text())
|
||||
dialogue.add_child(label, true)
|
||||
|
||||
# Setup the animation:
|
||||
animator.stop(true) # internally some timers do not reset properly unless we do this
|
||||
if not animator.has_animation_library(&"demo"):
|
||||
var new_animation_library : AnimationLibrary = AnimationLibrary.new()
|
||||
animator.add_animation_library(&"demo", new_animation_library)
|
||||
var animation_library : AnimationLibrary = animator.get_animation_library(&"demo")
|
||||
var animation : Animation = create_visible_characters_animation_per_character(label.get_text(), 0.045, true)
|
||||
animator.set_root_node(label.get_path())
|
||||
animation_library.add_animation(&"dialogue", animation)
|
||||
animator.play(&"demo/dialogue")
|
||||
|
||||
|
||||
func __on_dialogue_finished() -> void:
|
||||
print("Dialogue Finished! Exiting...")
|
||||
get_tree().quit()
|
||||
|
||||
|
||||
func __on_dialogue_cancelled() -> void:
|
||||
print("Dialogue Cancelled! Exiting...")
|
||||
get_tree().quit()
|
||||
|
||||
|
||||
func __on_animation_started(p_animation_name : StringName) -> void:
|
||||
print("Animation started:", p_animation_name)
|
||||
|
||||
|
||||
func create_visible_characters_animation_per_character(p_text : String, p_time_per_character : float, p_instant_first_character : bool = false, p_time_whitespace : bool = false) -> Animation:
|
||||
# Do initial calculations
|
||||
var whitespace_regex : RegEx
|
||||
if not p_time_whitespace or p_instant_first_character:
|
||||
whitespace_regex = RegEx.new()
|
||||
whitespace_regex.compile("\\s")
|
||||
|
||||
# Create animation and track
|
||||
var animation : Animation = Animation.new()
|
||||
var track_index : int = animation.add_track(Animation.TYPE_VALUE)
|
||||
animation.track_set_path(track_index, ".:visible_characters")
|
||||
animation.track_set_interpolation_type(track_index, Animation.INTERPOLATION_LINEAR)
|
||||
|
||||
# Configure keys
|
||||
var total_time : float = 0.0
|
||||
var total_visible_characters : int = 0
|
||||
var whitespace_time_offset : float = 0.0
|
||||
animation.track_insert_key(track_index, total_time, 0)
|
||||
var total_animation_length : float = 0.0
|
||||
for character : String in p_text:
|
||||
total_time += p_time_per_character
|
||||
total_visible_characters += 1
|
||||
if not p_time_whitespace and whitespace_regex.sub(character, "", true).is_empty():
|
||||
whitespace_time_offset += p_time_per_character
|
||||
continue
|
||||
total_animation_length = total_time - whitespace_time_offset
|
||||
animation.track_insert_key(track_index, total_animation_length, total_visible_characters)
|
||||
animation.set_length(total_animation_length)
|
||||
|
||||
if p_instant_first_character:
|
||||
if animation.track_get_key_count(track_index) > 0:
|
||||
# Shift all the keys back in time according to the time it took per character
|
||||
for key_index : int in animation.track_get_key_count(track_index):
|
||||
var key_time : float = animation.track_get_key_time(track_index, key_index)
|
||||
animation.track_set_key_time(track_index, key_index, key_time - p_time_per_character)
|
||||
animation.set_length(total_animation_length - p_time_per_character)
|
||||
return animation
|
||||
|
||||
|
||||
func _on_history_log_toggled(p_should_be_visible : bool) -> void:
|
||||
# Free all previous history labels
|
||||
for child: Node in history_log.get_children():
|
||||
child.queue_free()
|
||||
|
||||
history.visible = p_should_be_visible
|
||||
|
||||
if p_should_be_visible:
|
||||
@warning_ignore("unsafe_method_access")
|
||||
var log_history : Array = dialogue_engine.get_log_history()
|
||||
for text : String in log_history:
|
||||
var label: Label = Label.new()
|
||||
label.text = text
|
||||
history_log.add_child(label)
|
||||
|
||||
|
||||
func _on_save_pressed() -> void:
|
||||
@warning_ignore("unsafe_method_access")
|
||||
dialogue_engine.save_state()
|
||||
get_tree().quit()
|
||||
|
||||
|
||||
func _on_clear_pressed() -> void:
|
||||
@warning_ignore("unsafe_method_access")
|
||||
dialogue_engine.clear_state()
|
||||
get_tree().quit()
|
||||
84
demos/10. dynamic save and load/dynamic_save_load.tscn
Normal file
84
demos/10. dynamic save and load/dynamic_save_load.tscn
Normal file
@@ -0,0 +1,84 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://b0gsgxefiut4g"]
|
||||
|
||||
[ext_resource type="Script" path="res://demos/10. dynamic save and load/dynamic_save_load.gd" id="1_75aob"]
|
||||
[ext_resource type="Script" path="res://demos/10. dynamic save and load/dynamic_save_dialogue.gd" id="2_lqhmn"]
|
||||
|
||||
[node name="DynamicSaveLoad" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_75aob")
|
||||
dialogue_gdscript = ExtResource("2_lqhmn")
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="DialogueEngineDemoQuickStartPanelContainer" type="PanelContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="DialogueEngineDemoQuickStartLabel" type="Label" parent="VBox/DialogueEngineDemoQuickStartPanelContainer"]
|
||||
layout_mode = 2
|
||||
text = "Press <Enter> or <Space> to progress the dialogue."
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="Buttons" type="HBoxContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Save" type="Button" parent="VBox/Buttons"]
|
||||
layout_mode = 2
|
||||
text = "Save and Quit"
|
||||
|
||||
[node name="Clear" type="Button" parent="VBox/Buttons"]
|
||||
layout_mode = 2
|
||||
text = "Clear State and Quit"
|
||||
|
||||
[node name="HistoryLog" type="Button" parent="VBox/Buttons"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
text = "History Log"
|
||||
|
||||
[node name="Dialogue" type="VBoxContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="LogRichTextLabel" type="RichTextLabel" parent="VBox/Dialogue"]
|
||||
layout_mode = 2
|
||||
text = "Log:"
|
||||
fit_content = true
|
||||
|
||||
[node name="History" type="CenterContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="Panel" type="PanelContainer" parent="History"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Margin" type="MarginContainer" parent="History/Panel"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 16
|
||||
theme_override_constants/margin_top = 16
|
||||
theme_override_constants/margin_right = 16
|
||||
theme_override_constants/margin_bottom = 16
|
||||
|
||||
[node name="HistoryLog" type="VBoxContainer" parent="History/Panel/Margin"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Animator" type="AnimationPlayer" parent="."]
|
||||
root_node = NodePath("../VBox")
|
||||
|
||||
[connection signal="pressed" from="VBox/Buttons/Save" to="." method="_on_save_pressed"]
|
||||
[connection signal="pressed" from="VBox/Buttons/Clear" to="." method="_on_clear_pressed"]
|
||||
[connection signal="toggled" from="VBox/Buttons/HistoryLog" to="." method="_on_history_log_toggled"]
|
||||
Reference in New Issue
Block a user