diff --git a/SpecialOrdersExtended/DialogueManager.cs b/SpecialOrdersExtended/DialogueManager.cs index 2e99556..f77cad2 100644 --- a/SpecialOrdersExtended/DialogueManager.cs +++ b/SpecialOrdersExtended/DialogueManager.cs @@ -3,11 +3,54 @@ namespace SpecialOrdersExtended; +/// +/// A dialogue to delay. +/// +internal struct DelayedDialogue +{ + private readonly int time; + private readonly Dialogue dialogue; + private readonly NPC npc; + + /// + /// Initializes a new instance of the struct. + /// + /// Time to delay to. + /// Dialogue to delay. + /// Speaking NPC. + public DelayedDialogue(int time, Dialogue dialogue, NPC npc) + { + this.time = time; + this.dialogue = dialogue; + this.npc = npc; + } + + /// + /// Pushes the delayed dialogue onto the NPC's stack if it's past time to do so.. + /// + /// The current in-game time. + /// True if pushed, false otherwise. + public bool PushIfPastTime(int currenttime) + { + if (currenttime > this.time) + { + this.npc.CurrentDialogue.Push(this.dialogue); + return true; + } + return false; + } +} + /// /// Static. Handles logic, patches, and console commands related to the special order dialogues. /// internal class DialogueManager { + /// + /// A queue of delayed dialogues. + /// + private static readonly PerScreen> DelayedDialogues = new(createNewState: () => new Queue()); + /// /// Backing field for PerScreened Dialogue Logs. /// @@ -302,6 +345,29 @@ public static void PostfixCheckDialogue(ref bool __result, ref NPC __instance, i } } + /// + /// Clears the Delayed Dialogue queue. Call at end of day. + /// + public static void ClearDelayedDialogue() => DelayedDialogues.Value.Clear(); + + /// + /// Push any available dialogues to the NPC's dialogue stacks. + /// + public static void PushPossibleDelayedDialogues() + { + while (DelayedDialogues.Value.TryPeek(out DelayedDialogue result)) + { + if (result.PushIfPastTime(Game1.timeOfDay)) + { + _ = DelayedDialogues.Value.Dequeue(); + } + else + { + return; + } + } + } + /// /// Checks to see if a dialoguekey has been said already, and if not said, pushes the dialogue /// onto the dialogue stack. @@ -315,6 +381,13 @@ private static bool PushAndSaveDialogue(string dialogueKey, NPC npc) {// I have already said this dialogue return false; } + while (npc.CurrentDialogue.TryPop(out Dialogue? result)) + { + DelayedDialogues.Value.Enqueue(new DelayedDialogue( + time: Game1.timeOfDay + 100, + npc: npc, + dialogue: result)); + } npc.CurrentDialogue.Push(new Dialogue(npc.Dialogue[dialogueKey], npc) { removeOnNextMove = true }); if (ModEntry.Config.Verbose) { diff --git a/SpecialOrdersExtended/ModEntry.cs b/SpecialOrdersExtended/ModEntry.cs index 6795d79..65e5109 100644 --- a/SpecialOrdersExtended/ModEntry.cs +++ b/SpecialOrdersExtended/ModEntry.cs @@ -1,6 +1,7 @@ using System.Reflection; using HarmonyLib; using SpecialOrdersExtended.Integrations; +using StardewModdingAPI.Events; using StardewValley.GameData; namespace SpecialOrdersExtended; @@ -110,15 +111,21 @@ public override void Entry(IModHelper helper) helper.Events.GameLoop.GameLaunched += this.OnGameLaunched; helper.Events.GameLoop.SaveLoaded += this.SaveLoaded; helper.Events.GameLoop.Saving += this.Saving; + helper.Events.GameLoop.TimeChanged += this.OnTimeChanged; helper.Events.GameLoop.OneSecondUpdateTicking += this.OneSecondUpdateTicking; } - private void OneSecondUpdateTicking(object? sender, StardewModdingAPI.Events.OneSecondUpdateTickingEventArgs e) + private void OnTimeChanged(object? sender, TimeChangedEventArgs e) + { + DialogueManager.PushPossibleDelayedDialogues(); + } + + private void OneSecondUpdateTicking(object? sender, OneSecondUpdateTickingEventArgs e) { RecentSOManager.GrabNewRecentlyCompletedOrders(); } - private void OnGameLaunched(object? sender, StardewModdingAPI.Events.GameLaunchedEventArgs e) + private void OnGameLaunched(object? sender, GameLaunchedEventArgs e) { // Bind Spacecore API IModInfo spacecore = this.Helper.ModRegistry.Get("spacechase0.SpaceCore"); @@ -141,11 +148,12 @@ private void OnGameLaunched(object? sender, StardewModdingAPI.Events.GameLaunche api.RegisterToken(this.ModManifest, "RecentCompleted", new Tokens.RecentCompletedSO()); } - private void Saving(object? sender, StardewModdingAPI.Events.SavingEventArgs e) + private void Saving(object? sender, SavingEventArgs e) { this.Monitor.DebugLog("Event Saving raised"); DialogueManager.Save(); // Save dialogue + DialogueManager.ClearDelayedDialogue(); if (Context.IsSplitScreen && Context.ScreenId != 0) {// Some properties only make sense for a single player to handle in splitscreen. @@ -159,7 +167,7 @@ private void Saving(object? sender, StardewModdingAPI.Events.SavingEventArgs e) RecentSOManager.Save(); } - private void SaveLoaded(object? sender, StardewModdingAPI.Events.SaveLoadedEventArgs e) + private void SaveLoaded(object? sender, SaveLoadedEventArgs e) { this.Monitor.DebugLog("Event SaveLoaded raised"); DialogueManager.Load(Game1.player.UniqueMultiplayerID);