namespace BardFrontline.Core; public sealed record RuntimeConditions(bool Enabled, bool LoggedIn, bool ChangingArea, BattlefieldMode Mode, bool DutyStarted, bool DutyCompleted, bool Bard, bool Alive, bool Mounted, bool Occupied, bool CoreReady, bool Piloting = false); public static class ExecutionPolicy { public static string? PauseReason(RuntimeConditions s) { if (!s.Enabled) return "已关闭"; if (!s.LoggedIn) return "等待角色登录"; if (s.ChangingArea) return "切换区域"; if (s.Mode is not (BattlefieldMode.Frontline or BattlefieldMode.RivalWings)) return "仅在纷争前线 / 烈羽争锋运行"; if (!s.DutyStarted || s.DutyCompleted) return "等待对局开始 / 对局已结束"; if (s.Piloting) return "驾驶机甲中,暂停诗人循环"; if (!s.Bard) return "仅支持吟游诗人"; if (!s.Alive) return "角色死亡"; if (s.Mounted) return "坐骑上暂停"; if (s.Occupied) return "角色正在交互"; return s.CoreReady ? null : "核心技能数据缺失"; } public static bool CanDispatch(bool queued, float animationLock, bool casting, bool pending, bool guard) => !queued && animationLock <= 0 && !casting && !pending && !guard; public static bool CanWeave(float sharedRecastRemaining) => sharedRecastRemaining <= 0 || sharedRecastRemaining >= .65f; // Encore is a time-limited LB follow-up. An extra GCD reservation must not starve it // under stacked haste; the runtime still enforces casting, animation lock and native recast. public static bool CanWeave(float sharedRecastRemaining, Skill skill) => skill == Skill.EncoreOfLight || CanWeave(sharedRecastRemaining); public static bool SharesGcd(int primary, int additional, int shared) => shared >= 0 && (primary == shared || additional == shared); public static float RecastRemaining(bool active, float total, float elapsed) => active && float.IsFinite(total) && float.IsFinite(elapsed) ? Math.Max(0, total - elapsed) : 0; public static bool OwnsCast(uint ownedId, ulong ownedTarget, ushort ownedSequence, uint castId, ulong castTarget, ushort usedSequence) => ownedId == castId && ownedTarget == castTarget && ownedSequence == usedSequence; public static bool ShouldCancelCast(Skill castSkill, bool owned, bool invalidTarget, bool enemyGuard, bool ignoreGuard, ActionDecision? emergency, bool nearbyThreat, bool hasControl) { if (!owned) return false; if (Actions.IsHostile(castSkill) && (invalidTarget || enemyGuard && !ignoreGuard && castSkill != Skill.EagleEyeShot)) return true; if (emergency == null) return false; // Recovering HP is the point of elixir. Do not cancel a safe drink just because Recuperate came off CD. return castSkill != Skill.Elixir || nearbyThreat || hasControl; } }