worldbox-observer-mod/IdleSpectator/Narrative/NarrativeModels.cs
DazedAnon 739cc6492c feat(spectator): make narrative threads own camera direction
- Route confirmed life events into evidence-based threads, consequences, and Legacy chapters
- Schedule episode shots with critical interrupts, replay guards, and a combat budget
- Admit emerging main characters through story momentum instead of spectacle
- Remove causal heat, hard-arc memory writes, and synthetic epilogue continuity
- Expand narrative harness coverage, soak auditing, and product documentation
2026-07-23 12:17:18 -05:00

262 lines
6.1 KiB
C#

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
}
/// <summary>Evidence-backed semantic state transition. Contains no final player-facing prose.</summary>
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<long> OtherIds = new List<long>(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 = "";
}
/// <summary>Durable unresolved question centered on one life.</summary>
public sealed class NarrativeThread
{
public string Id = "";
public NarrativeThreadKind Kind;
public long ProtagonistId;
public readonly List<NarrativeCastRole> Cast = new List<NarrativeCastRole>(6);
public readonly List<string> DevelopmentIds = new List<string>(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 ?? "" });
}
}
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<string> OpenThreadIds = new List<string>(6);
public readonly List<string> ResolvedThreadIds = new List<string>(8);
public readonly List<string> RecentDevelopmentIds = new List<string>(16);
public readonly List<string> ConsequenceIds = new List<string>(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 sealed class EpisodePlan
{
public string ThreadId = "";
public long ProtagonistId;
public readonly List<long> EligibleCastIds = new List<long>(6);
public string EntryDevelopmentId = "";
public EpisodePurpose Purpose;
public string InformationGoal = "";
public float StartedAt;
public float LastProgressAt;
public float InterruptedAt = -1f;
public string LastShotKey = "";
public int CombatShotCount;
public bool GoalMet;
}
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;
}