worldbox-observer-mod/IdleSpectator/Story/StoryArc.cs
2026-07-21 23:46:20 -05:00

87 lines
2 KiB
C#

using System.Collections.Generic;
namespace IdleSpectator;
public enum StoryArcKind
{
None = 0,
CombatDuel = 1,
CombatMass = 2,
WarFront = 3,
Plot = 4,
Love = 5,
Grief = 6
}
public enum StoryPhase
{
None = 0,
Climax = 1,
Aftermath = 2,
Epilogue = 3,
Done = 4
}
/// <summary>
/// Short multi-beat story ownership: climax sticky/life scene → aftermath → optional epilogue.
/// </summary>
public sealed class StoryArc
{
public string Id = "";
public StoryArcKind Kind = StoryArcKind.None;
public StoryPhase Phase = StoryPhase.None;
public string AnchorKey = "";
public string ClimaxKey = "";
/// <summary>
/// Short cast/theater identity for the dossier rail (e.g. "Evil Mage vs Lona").
/// Not Kind/Phase taxonomy - who the story is about.
/// </summary>
public string Headline = "";
public float StartedAt;
public float PhaseStartedAt;
/// <summary>When parked off-camera (0 = currently watching / never parked).</summary>
public float ParkedAt;
/// <summary>True when this arc uses hard hold margins (not soft anonymous duels).</summary>
public bool HardHold;
public readonly List<long> CastIds = new List<long>(4);
public readonly Queue<StoryBeat> PendingBeats = new Queue<StoryBeat>(2);
public bool IsActive =>
Phase != StoryPhase.None && Phase != StoryPhase.Done;
public bool IsParked => ParkedAt > 0f && IsActive;
public bool ContainsCast(long unitId)
{
if (unitId == 0)
{
return false;
}
for (int i = 0; i < CastIds.Count; i++)
{
if (CastIds[i] == unitId)
{
return true;
}
}
return false;
}
public void NoteCast(long unitId)
{
if (unitId == 0 || ContainsCast(unitId))
{
return;
}
CastIds.Add(unitId);
}
public void Advance(StoryPhase next, float now)
{
Phase = next;
PhaseStartedAt = now;
}
}