using System; using System.Collections.Generic; namespace IdleSpectator; /// /// Durable sticky scoreboard for a multi-actor live scene (combat first; war/plot/family later). /// Locks opposing side identity once, tracks member ids, and holds peak scale across dips. /// Owned by ; cleared when the scene goes cold. /// public sealed class LiveSceneStickyState { public EnsembleKind Kind = EnsembleKind.Combat; public EnsembleFrame Frame = EnsembleFrame.CountOnly; public int PeakParticipants; public float ScaleDropSince = -999f; /// Last tip tier presented; held briefly across Skirmish↔Battle↔Mass flaps. public bool HasPresentedScale; public EnsembleScale PresentedScale; public float ScaleChangeSince = -999f; /// Last structurally committed combat reason; separate from live headcounts. public string PresentedReason = ""; public string PendingReason = ""; public float ReasonChangeSince = -999f; /// Combat must stay cold briefly before its headline is cleared. public float CombatColdSince = -999f; /// /// After a camp hits 0, stay in mop-up until the thin side recovers to /// (stops 0↔1 Mass↔Battle thrash). /// public bool MopUpActive; public float MopUpSince = -999f; public string SideAKey = ""; public string SideADisplay = ""; public string SideAKingdom = ""; public int SideACount; public readonly List SideAIds = new List(8); public string SideBKey = ""; public string SideBDisplay = ""; public string SideBKingdom = ""; public int SideBCount; public readonly List SideBIds = new List(8); /// /// True when sticky identity is locked. Opposing camps need A+B; FamilyPack locks Side A only. /// public bool HasOpposingSides => !string.IsNullOrEmpty(SideAKey) && ( Kind == EnsembleKind.FamilyPack || Kind == EnsembleKind.StatusOutbreak || (!string.IsNullOrEmpty(SideBKey) && (Frame == EnsembleFrame.SpeciesVsSpecies || Frame == EnsembleFrame.KingdomVsKingdom))); public int TotalCount => Math.Max(0, SideACount) + Math.Max(0, SideBCount); public void Clear() { Kind = EnsembleKind.Combat; Frame = EnsembleFrame.CountOnly; PeakParticipants = 0; ScaleDropSince = -999f; HasPresentedScale = false; PresentedScale = EnsembleScale.Pair; ScaleChangeSince = -999f; PresentedReason = ""; PendingReason = ""; ReasonChangeSince = -999f; CombatColdSince = -999f; MopUpActive = false; MopUpSince = -999f; SideAKey = ""; SideADisplay = ""; SideAKingdom = ""; SideACount = 0; SideAIds.Clear(); SideBKey = ""; SideBDisplay = ""; SideBKingdom = ""; SideBCount = 0; SideBIds.Clear(); } public void CopyTo(LiveSceneStickyState other) { if (other == null) { return; } other.Clear(); other.Kind = Kind; other.Frame = Frame; other.PeakParticipants = PeakParticipants; other.ScaleDropSince = ScaleDropSince; other.HasPresentedScale = HasPresentedScale; other.PresentedScale = PresentedScale; other.ScaleChangeSince = ScaleChangeSince; other.PresentedReason = PresentedReason; other.PendingReason = PendingReason; other.ReasonChangeSince = ReasonChangeSince; other.CombatColdSince = CombatColdSince; other.MopUpActive = MopUpActive; other.MopUpSince = MopUpSince; other.SideAKey = SideAKey; other.SideADisplay = SideADisplay; other.SideAKingdom = SideAKingdom; other.SideACount = SideACount; other.SideBKey = SideBKey; other.SideBDisplay = SideBDisplay; other.SideBKingdom = SideBKingdom; other.SideBCount = SideBCount; for (int i = 0; i < SideAIds.Count; i++) { other.SideAIds.Add(SideAIds[i]); } for (int i = 0; i < SideBIds.Count; i++) { other.SideBIds.Add(SideBIds[i]); } } }