nathanhoad / godot_dialogue_manager

A powerful nonlinear dialogue system for Godot
MIT License
2.07k stars 161 forks source link

Line writer doesn't wait for async task to finish #515

Closed ProFiLeR4100 closed 6 months ago

ProFiLeR4100 commented 6 months ago

Is your feature request related to a problem? Please describe.

I wanted to create a dialog that is interrupted by sound or any other action.

Line writer pauses and hides baloon and clears it when I call method it in separate line like so:

~ start

Character: [#mood=grim]The views here are certainly beautiful, [speed=100]*sound of explosion from far away*[/speed]
do SoundWait("res://Sounds/explosion-far.mp3")
Character: [#mood=grim]but...

Line writer doesn't pauses at all when I try to use something like this:

~ start

Character: [#mood=grim]The views here are certainly beautiful, [speed=100]*sound of explosion from far away*[/speed][do SoundWait("res://Sounds/explosion-far.mp3")]but...

Not Sure if my code is correct or it doesn't supported by the DialogueManager, but here it is:

using System.Threading.Tasks;
using Godot;

[GlobalClass]
public partial class DialogueAPI: Node {
    public void Sound(string resourcePath) {
        AudioStreamPlayer streamPlayer = new();
        streamPlayer.Stream = ResourceLoader.Load<AudioStream>(resourcePath);
        streamPlayer.Connect(
            AudioStreamPlayer.SignalName.Finished,
            Callable.From(() => {
                streamPlayer.QueueFree();
            })
        );

        CallDeferred(Node.MethodName.AddChild, streamPlayer);
        streamPlayer.CallDeferred(AudioStreamPlayer.MethodName.Play);
    }

    public async Task SoundWait(string resourcePath) {
        AudioStreamPlayer streamPlayer = new();
        streamPlayer.Stream = ResourceLoader.Load<AudioStream>(resourcePath);

        CallDeferred(Node.MethodName.AddChild, streamPlayer);
        streamPlayer.CallDeferred(AudioStreamPlayer.MethodName.Play);

        await streamPlayer.ToSignal(
            streamPlayer,
            AudioStreamPlayer.SignalName.Finished
        );

        streamPlayer.QueueFree();
    }
}

Describe the solution you'd like

I expect line writer to pause until async task finishes it's job and then continue.

Describe alternatives you've considered

Option 1: Probably I've could set speed of typing to 0 when task is initiated and then return it back, but I could not find any API for this. Something like this:

public async Task LongTask() {
    float previousSpeed = DialogueManager.Speed;
    DialogueManager.Speed = 0;

    // Some task that requires few seconds for completion.

    DialogueManager.Speed = previousSpeed;
    streamPlayer.QueueFree();
}

Option 2: I can add [wait=seconds] but it will not be very usefull when guys ask me to add sounds that require translation, which could be different in length. Also if sound file will change, Dialog also must be changed.

Option 3: Add some sort of interceptor for lines, then parse it and search for SoundWait method calls, then load file calculate its length, then insert [wait=seconds] to places when method calls occures.

Additional Context

nathanhoad commented 6 months ago

I've opened a PR that should help with what you're trying to do. If you try it out and write your sound mutation to use await (eg. awaiting the finished signal on the AudioStreamPlayer) then the typing should pause until the mutation is resolved.

ProFiLeR4100 commented 6 months ago

I've tested it, everything works like a charm. Thank you.