using Cthangover.Core.Scenes; using Cthangover.Mods.Dialog; using Cthangover.Core.Utils; using Godot; using Cthangover.Core.UI; using Cthangover.Mods.Interface.Base.Lists; using Cthangover.Core.UI.Dialog; using Cthangover.Core.UI.Event; using Cthangover.Core.UI.Executable; using Cthangover.Core.Scenarios; using Cthangover.Core.Interactive; namespace Cthangover.Mods.Dialog.Executable { /// /// Sequential list-based chain: rescans GetNext() only when the condition /// state changes (bumped ), on the /// end of its own running dialog, or on the first scan — with a debounce. /// When an event runs, it's removed from the list first, so it's consumed and /// the chain shrinks until empty (no self-loop is possible, so no skip-guard /// is needed). Respects IsOneRun (skips already-run events) and /// CheckConditions (skips events whose conditions fail). Use for ordered /// event sequences where each event should play once in order. /// public partial class ExecutableEventListChain : ExecutableEventChainBase { private float checkDelay = 1f; private float timestamp; private long _lastScanVersion = long.MinValue; private bool _pendingDialog; private bool _wakeup; /// Debounced chain driver: waits out the running dialog, rescans when the condition state changed and starts the next unconsumed event. public override void _Process(double delta) { if (!isActive || chain.Count == 0) { IsActive = false; return; } if (_pendingDialog) { if (dialogBox == null || dialogBox.Locker != null) return; _pendingDialog = false; _lastScanVersion = ConditionStateMonitor.Version; _wakeup = true; return; } double time = Godot.Time.GetTicksUsec() / 1_000_000.0; if(time - timestamp < checkDelay) return; timestamp = (float)time; var version = ConditionStateMonitor.Version; if (version == _lastScanVersion && !_wakeup) return; _wakeup = false; _lastScanVersion = version; var next = GetNext(); if(next == null) return; chain.Remove(next); next.RunDialog(); _pendingDialog = true; } protected override ExecutableEvent GetNext() { if (Lists.IsEmpty(chain)) return null; foreach (var additional in chain) { if(additional.IsOneRun && !additional.IsFirstRun) continue; if (additional.CheckConditions) return additional; } return null; } } }