using System.Collections.Generic; namespace IdleSpectator; public enum NarrativeDevelopmentKind { Unknown, BondFormed, BondBroken, ChildBorn, FriendFormed, Death, Kill, RoleGained, RoleLost, HomeFounded, HomeGained, HomeLost, WarJoined, WarEnded, PlotJoined, PlotEnded, RivalEncounter, Transformation, Recovery } public enum NarrativeFunction { Noise, CharacterTexture, WorldContext, ThreadOpener, Development, Escalation, TurningPoint, Resolution, Consequence } public enum NarrativeThreadKind { Unknown, Courtship, Partnership, Lineage, SeparationGrief, RiseToRule, Reign, Succession, Founding, HomeLoss, Rivalry, PersonalCombat, WarInvolvement, PlotBetrayal, Transformation, Recovery } public enum NarrativePhase { Setup, Pressure, Escalation, TurningPoint, Outcome, Consequence } public enum NarrativeThreadStatus { Emerging, Active, Dormant, Resolved, Abandoned } public enum CharacterConsequenceKind { Unknown, FoundLove, LostPartner, BecameParent, WasBorn, LostFamily, LostLife, TookLife, RoseToRole, LostRole, FoundedHome, LostHome, EnteredWar, SurvivedWar, JoinedPlot, PlotResolved, Transformed, Recovered, FoundHome } /// Evidence-backed semantic state transition. Contains no final player-facing prose. public sealed class NarrativeDevelopment { public string Id = ""; public NarrativeDevelopmentKind Kind; public NarrativeFunction Function; public long SubjectId; public LifeSagaIdentity Subject; public long OtherId; public LifeSagaIdentity Other; public readonly List OtherIds = new List(4); public string CorrelationKey = ""; public string FamilyKey = ""; public string CityKey = ""; public string KingdomKey = ""; public string WarKey = ""; public string PlotKey = ""; public string PreviousState = ""; public string NewState = ""; public string Outcome = ""; public string EvidenceSource = ""; public string DateLabel = ""; public float OccurredAt; public float Magnitude; public float Confidence = 1f; public bool Presentable = true; public bool IsNewStateChange = true; } public sealed class NarrativeCastRole { public long CharacterId; public string Role = ""; } /// Durable unresolved question centered on one life. public sealed class NarrativeThread { private const int MaxRetainedCast = 16; public string Id = ""; public NarrativeThreadKind Kind; public long ProtagonistId; public readonly List Cast = new List(6); public readonly List DevelopmentIds = new List(12); public string AnchorKey = ""; public string OpenedByDevelopmentId = ""; public string LatestMeaningfulChangeId = ""; public string CentralQuestion = ""; public string PressureEvidence = ""; public string Resolution = ""; public NarrativePhase Phase; public NarrativeThreadStatus Status; public float OpenedAt; public float UpdatedAt; public float Momentum; public float Urgency; public float Coherence; public float Novelty; public float CoverageDebt; public bool IsOpen => Status == NarrativeThreadStatus.Emerging || Status == NarrativeThreadStatus.Active || Status == NarrativeThreadStatus.Dormant; public bool HasCast(long id) { if (id == 0) return false; for (int i = 0; i < Cast.Count; i++) { if (Cast[i] != null && Cast[i].CharacterId == id) return true; } return false; } public void AddCast(long id, string role) { if (id == 0 || HasCast(id)) return; Cast.Add(new NarrativeCastRole { CharacterId = id, Role = role ?? "" }); while (Cast.Count > MaxRetainedCast) { int removeAt = 0; while (removeAt < Cast.Count && Cast[removeAt] != null && Cast[removeAt].CharacterId == ProtagonistId) { removeAt++; } if (removeAt >= Cast.Count) break; Cast.RemoveAt(removeAt); } } } public sealed class CharacterConsequence { public string Id = ""; public long CharacterId; public string ThreadId = ""; public string DevelopmentId = ""; public CharacterConsequenceKind Kind; public string Role = ""; public long OtherId; public LifeSagaIdentity Other; public string Context = ""; public string Outcome = ""; public string DateLabel = ""; public float OccurredAt; public float Importance; public float Confidence = 1f; } public sealed class CharacterStoryState { public long CharacterId; public LifeSagaIdentity Identity; public readonly List OpenThreadIds = new List(6); public readonly List ResolvedThreadIds = new List(8); public readonly List RecentDevelopmentIds = new List(16); public readonly List ConsequenceIds = new List(16); public float StoryMomentum; public float StoryPotential; public float LastMeaningfulChangeAt; } public sealed class NarrativeChapter { public string Id = ""; public string ThreadId = ""; public string Line = ""; public string DateLabel = ""; public float At; public float Importance; } public enum EpisodePurpose { Establish, Develop, Payoff, Consequence, Reintroduce } public enum EpisodeShotRole { Unknown, Establish, Development, Pressure, TurningPoint, Payoff, Consequence, Reintroduction, WorldContext } public sealed class EpisodePlan { public string ThreadId = ""; public long ProtagonistId; public readonly List EligibleCastIds = new List(6); public string EntryDevelopmentId = ""; public EpisodePurpose Purpose; public string InformationGoal = ""; public float StartedAt; public float LastProgressAt; public float InterruptedAt = -1f; public string LastShotKey = ""; public long LastSubjectId; public int RepeatedSubjectCount; public int CombatShotCount; public bool GoalMet; public string CompletionReason = ""; public readonly List CoveredRoles = new List(8); public readonly List PendingRoles = new List(8); public readonly List RecentSemanticKeys = new List(10); public bool HasCovered(EpisodeShotRole role) => CoveredRoles.Contains(role); public void Cover(EpisodeShotRole role) { if (role == EpisodeShotRole.Unknown || role == EpisodeShotRole.WorldContext) return; if (!CoveredRoles.Contains(role)) CoveredRoles.Add(role); PendingRoles.Remove(role); } } public sealed class NarrativeBeatModel { public long PerspectiveCharacterId; public string DevelopmentId = ""; public string ThreadId = ""; public NarrativeDevelopmentKind ActionKind; public long OtherId; public LifeSagaIdentity Other; public string Change = ""; public string Outcome = ""; public NarrativePhase ThreadPhase; public string ContextEvidence = ""; public float Confidence = 1f; }