using Unity.Entities; using Unity.NetCode; namespace ProjectM.Simulation { /// /// Macro-loop state for "The Aether Cycle": which phase the run is in, the cycle number, and the server /// tick the current (timed) phase ends. Server-authoritative, maintained by CyclePhaseSystem. Currently a /// server-side singleton; the [GhostField]s below are inert until it is moved onto the runtime-spawned /// CycleDirector ghost (when the client HUD is wired), at which point the same struct replicates unchanged. /// The Defend phase is NOT timed — it ends when the base-defense wave is cleared — so PhaseEndTick is only /// meaningful in Expedition/Build (0 during Defend). /// public struct CycleState : IComponentData { /// Current phase (see ). [GhostField] public byte Phase; /// 1-based cycle counter (increments when a new Expedition begins). [GhostField] public int CycleNumber; /// Server tick the current timed phase ends (Expedition/Build only; 0 in Defend). [GhostField] public uint PhaseEndTick; } /// Phase constants for (a byte, not an enum, for trivial Burst/serialization). public static class CyclePhase { /// Out in the procedural field gathering resources (timed). public const byte Expedition = 0; /// The base is under assault by a Husk wave (ends when the wave is cleared). public const byte Defend = 1; /// Calm at base: spend resources to build/upgrade (timed). public const byte Build = 2; /// Expedition phase duration in server ticks (SimulationTickRate = 60). Tunable; short for the M6 slice. public const uint ExpeditionTicks = 3600; // ~60s cap (early return via the gate ends it sooner) /// Build phase duration in server ticks. public const uint BuildTicks = 1200; // ~20s } /// /// Server-only bookkeeping for the cycle state machine that must NOT replicate (kept separate from the /// replicated ). Records the wave number captured when the Defend phase began so /// the director can detect "this Defend's wave has now been spawned and cleared". /// public struct CycleRuntime : IComponentData { /// WaveState.WaveNumber captured at the moment the current Defend phase started. public int DefendStartWave; /// Cycle phase from the previous tick — lets ExpeditionFieldSystem edge-detect entering/leaving Expedition. public byte PrevPhase; /// CycleNumber the expedition field was last seeded for (compared by int equality, never tick math). public int LastSpawnedCycle; } }