Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added DismissOptions method to DialogueViewBase #286

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Added

- Added DismissOptions method to DialogueViewBase
- Inheriting classes can implement this method to respond to option selections from other Dialogue Views

### Changed

- Fixed an issue where, on Windows, projects would fail to automatically update when a file that belonged to them was created or edited.
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ The following people have contributed to the development of Yarn Spinner. If you
* 2023: Mitch Zais <https://github.com/Invertex>
* 2023: Thomas Ingram (https://vertx.xyz)
* 2023: Isaac Berman (https://github.com/bermanisaac)
* 2024: Josh Guerrero (https://github.com/joshgrrro)
74 changes: 64 additions & 10 deletions Runtime/DialogueRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -608,8 +608,6 @@ public void SetDialogueViews(DialogueViewBase[] views)
/// </summary>
private readonly HashSet<DialogueViewBase> ActiveDialogueViews = new HashSet<DialogueViewBase>();

Action<int> selectAction;

/// <summary>
/// The underlying object that executes Yarn instructions
/// and provides lines, options and commands.
Expand Down Expand Up @@ -741,7 +739,6 @@ Dialogue CreateDialogueInstance()
PrepareForLinesHandler = PrepareForLines
};

selectAction = SelectedOption;
return dialogue;
}

Expand Down Expand Up @@ -803,7 +800,7 @@ void HandleOptionsInternal()
IsAvailable = options.Options[i].IsAvailable,
};
}

// Don't allow selecting options on the same frame that we
// provide them
IsOptionSelectionAllowed = false;
Expand All @@ -812,7 +809,8 @@ void HandleOptionsInternal()
{
if (dialogueView == null || dialogueView.isActiveAndEnabled == false) continue;

dialogueView.RunOptions(optionSet, selectAction);
dialogueView.RunOptions(optionSet,
selectedOptionIndex => DialogueViewCompletedOptions(selectedOptionIndex, dialogueView));
}

IsOptionSelectionAllowed = true;
Expand Down Expand Up @@ -1015,10 +1013,6 @@ void InterruptLine()
/// Action{int})"/> is in the middle of being called.</exception>
void SelectedOption(int optionIndex)
{
if (IsOptionSelectionAllowed == false) {
throw new InvalidOperationException("Selecting an option on the same frame that options are provided is not allowed. Wait at least one frame before selecting an option.");
}

// Mark that this is the currently selected option in the
// Dialogue
Dialogue.SetSelectedOption(optionIndex);
Expand All @@ -1041,7 +1035,7 @@ void SelectedOption(int optionIndex)
{
ContinueDialogue();
}

}

private static IEnumerator WaitForYieldInstruction(YieldInstruction yieldInstruction, Action onSuccessfulDispatch)
Expand Down Expand Up @@ -1087,6 +1081,16 @@ private void DialogueViewCompletedInterrupt(DialogueViewBase dialogueView)
}
}

private void DialogueViewCompletedOptions(int selectedOptionIndex, DialogueViewBase dialogueView)
{
if (IsOptionSelectionAllowed == false)
{
throw new InvalidOperationException("Selecting an option on the same frame that options are provided is not allowed. Wait at least one frame before selecting an option.");
}

DismissOptionsFromViews(selectedOptionIndex, dialogueViews);
}

void ContinueDialogue(bool dontRestart = false)
{
if (dontRestart == true)
Expand Down Expand Up @@ -1176,6 +1180,56 @@ private void DialogueViewCompletedDismissal(DialogueViewBase dialogueView)
ContinueDialogue();
}
}

private void DismissOptionsFromViews(int selectedOptionIndex, IEnumerable<DialogueViewBase> dialogueViews)
{
ActiveDialogueViews.Clear();

foreach (var dialogueView in dialogueViews)
{
// Skip any dialogueView that is null or not enabled
if (dialogueView == null || dialogueView.isActiveAndEnabled == false)
{
continue;
}

// we do this in two passes - first by adding each
// dialogueView into ActiveDialogueViews, then by asking
// them to dismiss the line - because calling
// view.DismissLine might immediately call its completion
// handler (which means that we'd be repeatedly returning
// to zero active dialogue views, which means
// DialogueViewCompletedDismissal will mark the line as
// entirely done)
ActiveDialogueViews.Add(dialogueView);
}

foreach (var dialogueView in dialogueViews)
{
if (dialogueView == null || dialogueView.isActiveAndEnabled == false)
{
continue;
}

dialogueView.DismissOptions(selectedOptionIndex,
() => DialogueViewCompletedOptionsDismissal(selectedOptionIndex, dialogueView));
}
}

private void DialogueViewCompletedOptionsDismissal(int optionIndex, DialogueViewBase dialogueView)
{
// A dialogue view just completed dismissing its options. Remove
// it from the set of active views.
ActiveDialogueViews.Remove(dialogueView);

// Have all of the views completed dismissal?
if (ActiveDialogueViews.Count == 0)
{
// Then we're ready to continue to the next piece of
// content.
SelectedOption(optionIndex);
}
}
#endregion

/// <summary>
Expand Down
46 changes: 46 additions & 0 deletions Runtime/Views/DialogueViewBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,52 @@ public virtual void RunOptions(DialogueOption[] dialogueOptions, Action<int> onO
// The default implementation does nothing.
}

/// <summary>
/// Called by the <see cref="DialogueRunner"/> to signal that the view
/// should dismiss its current options from display, and clean up.
/// </summary>
/// <remarks>
/// <para>
/// This method is called when a Dialogue View attached to a Dialogue
/// Runner reports that an option has been selected. When this occurs,
/// the Dialogue Runner calls <see cref="DismissOptions"/> on all
/// Dialogue Views to tell them to clear their current options from
/// display.</para>
/// <para>Depending on how the Dialogue View presents options,
/// "dismissing" them may mean different things. For example, a
/// Dialogue View that presents on-screen text might fade the text away,
/// or a Dialogue View that presents voice-over dialogue may not need to
/// do anything at all (because audio finished playing when the line(s)
/// finished presenting.)
/// </para>
/// <para style="hint">
/// Dismissing the options can take time, but should ideally be as fast as
/// possible, because the user will be waiting for the next piece of
/// content to appear. </para>
/// <para>
/// When the options have finished dismissing, this method calls
/// onDismissalComplete to indicate that the dismissal is complete. When
/// all Dialogue Views on a Dialogue Runner have finished dismissing,
/// the Dialogue Runner moves on to the next piece of content.
/// </para>
/// <para style="note">
/// The default implementation of this method immediately calls the
/// <paramref name="onDismissalComplete"/> method (that is, it reports
/// that it has finished dismissing the options the moment that it receives
/// it), and otherwise does nothing.
/// </para>
/// </remarks>
/// <param name="selectedOptionIndex">The index of the option that was
/// selected by the user.</param>
/// <param name="onDismissalComplete">The method that should be called
/// when the view has finished dismissing the line.</param>
public virtual void DismissOptions(int selectedOptionIndex, Action onDismissalComplete)
{
// The default implementation does nothing, and immediately calls
// onDismissalComplete.
onDismissalComplete?.Invoke();
}

/// <summary>
/// Called by the <see cref="DialogueRunner"/> to signal that the
/// dialogue has ended, and no more lines will be delivered.
Expand Down