godot_dialogue_manager
godot_dialogue_manager copied to clipboard
DialogueLabel: possible race condition between `finished_typing` event and `type_out` method
Describe the bug I am having an issue at the end of this dialogue script:
~ this_is_a_node_title
Nathan: [[Hi|Hello|Howdy]] ! What's your name??
- Kenny
- Ken
- Bob
Nathan: And I...[wait=1.0] am...[wait=1.0] Nathan! Which option ?
- First one
Nathan: You picked the first option.
- Start again => this_is_a_node_title
- End the conversation => END
Nathan: For more information see the online documentation.
=> END
Here is my code:
extends Node2D
var dialog_option = preload("res://DialogOption.tscn")
@onready var prompt = $Prompt # Prompt is a DialogueLabel
var resource = load("res://dialogue/main.dialogue")
var next_dialog_id := "this_is_a_node_title"
var dialogue_line : DialogueLine
# Called when the node enters the scene tree for the first time.
func _ready():
Events.connect("phrase_spoken", self._on_Events_phrase_spoken)
prompt.connect("finished_typing", self._on_prompt_finished)
_generate_dialogs()
func _generate_dialogs():
dialogue_line = await resource.get_next_dialogue_line(next_dialog_id)
if dialogue_line:
dialogue_line.text = dialogue_line.text
prompt.dialogue_line = dialogue_line
prompt.type_out()
next_dialog_id = dialogue_line.next_id
for i in len(dialogue_line.responses):
var speech = dialog_option.instantiate()
add_child(speech)
speech.set_text(dialogue_line.responses[i].text)
speech.next_id = dialogue_line.responses[i].next_id
func _on_Events_phrase_spoken(next_id: String) -> void:
next_dialog_id = next_id
_generate_dialogs()
func _on_prompt_finished() -> void:
if not dialogue_line.responses:
_generate_dialogs()
(phrase_spoken
is triggered when a response has been picked by the user. When there are no responses to be picked, I want to trigger the next dialogue line directly.)
The issue
The issue I am having is that when the "You picked the first option"
line finishes typing out, this bit of code in dialogue_label.gd triggers in order to set is_typing
to false
:
var is_typing: bool = false:
set(value):
if is_typing != value and value == false:
finished_typing.emit()
is_typing = value
What occurs then, is that my code catches the finished_typing
event that was just emitted and manages to call type_out()
again before is_typing = value
is ran in the setter function. What happens then is that is_typing
is finally set to false
in the original setter execution (the one that emitted the event) and the last dialogue line is never typed.
Workaround
I managed to fix this issue in my project by moving the emit to the end of the setter function:
var is_typing: bool = false:
set(value):
var has_finished_typing = is_typing != value and value == false
is_typing = value
if has_finished_typing:
finished_typing.emit()
I am definitely not sure this is the best solution, but I just figured it would give an idea of my issue
Affected version
- Dialogue Manager version: 2.34.0 and 2.35.0 at least
- Godot version: 4.2.1
To Reproduce See my code up there
Expected behavior There should not be a race condition maybe?