worldbox-observer-mod/IdleSpectator/Narrative/NarrativeModels.cs
DazedAnon ff4b40d00e feat(narrative): enhance narrative probes and presentation coverage.
- Introduce new narrative probes for episode grammar, ensemble balance, and prose redundancy
- Implement narrative presentation coverage tracking for better event visibility
- Update harness scenarios to include new narrative ingestion and persistence tests
- Refactor event emission to include narrative development IDs and presentations
- Expand InterestCandidate to support narrative presentation models and roles
2026-07-23 14:12:23 -05:00

290 lines
7 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 enum EpisodeShotRole
{
Unknown,
Establish,
Development,
Pressure,
TurningPoint,
Payoff,
Consequence,
Reintroduction,
WorldContext
}
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 long LastSubjectId;
public int RepeatedSubjectCount;
public int CombatShotCount;
public bool GoalMet;
public string CompletionReason = "";
public readonly List<EpisodeShotRole> CoveredRoles = new List<EpisodeShotRole>(8);
public readonly List<EpisodeShotRole> PendingRoles = new List<EpisodeShotRole>(8);
public readonly List<string> RecentSemanticKeys = new List<string>(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;
}