LANTERN purge B3+B5: delete the cycle/core/win-lose spine + onboarding; save epoch v7

Deletes CyclePhaseSystem, GoalReachedSystem, CoreDamage/CoreRestore, ThreatDirector,
CoreIntegrity/GoalProgress/RunPhase/RunOutcome/ThreatState components,
CoreVisualFeedbackSystem, and the whole Client/Onboarding slice (+6 test files).

Keepers reworked: RunDirectorSystem (UpdateBefore attr + launch guard + goal/threat
bank removed; sole SaveRequest raiser now), CycleDirectorSpawnSystem (ledger/meta
host only), WaveSystem UNGATED (waves run wherever a WaveDirector is baked),
EnemyAISystem core-fallback stripped, AmbientAudioSystem reworked (bed + run cues;
no CycleState gate), MusicSystem RunInfo-only, HudSystem big trim (goal meter, core
bar, siege banner, terminal banner, outcome flash, onboarding hook all gone),
MetaShop/ClassPrep/AimReticle siege gates dropped, DebugOverlay/ops re-meant
(SpawnWave=force next wave, EndSiege=quiet arena; SetCalm/AdvanceGoal/SetHeat
retired, bytes reserved), TuningConfig Core knobs retired (ids 20-23 reserved),
StorageMath.DrainFraction deleted, HowToPlay copy rewritten.

Save epoch v7 (fresh epoch, operator-approved): SaveData drops goal/core/outcome +
conveyor/machine-IO fields; MinLoadableVersion=7; PendingSave/PendingStructure
trimmed; RollTerminalCampaignForward deleted; SaveStructureScan signature slimmed.

390 tests green; Play world-creation clean (player + waves live, no exceptions).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 15:27:12 -07:00
parent 835dace213
commit b34945c2d2
74 changed files with 233 additions and 3955 deletions
@@ -3,75 +3,15 @@ using Unity.NetCode;
namespace ProjectM.Simulation
{
/// <summary>
/// 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).
/// </summary>
public struct CycleState : IComponentData
{
/// <summary>Current phase (see <see cref="CyclePhase"/>).</summary>
[GhostField] public byte Phase;
// NOTE (LANTERN purge): CycleState/CyclePhase/CycleRuntime (the Calm↔Siege macro-loop) are DELETED.
// ExpeditionObjective below is the surviving replicated room-objective readout (live consumers:
// RoomEnemyDirectorSystem writes it; RunDirectorSystem/HudSystem read it).
/// <summary>1-based cycle counter (increments when a new Expedition begins).</summary>
[GhostField] public int CycleNumber;
/// <summary>Server tick the current timed phase ends (Expedition/Build only; 0 in Defend).</summary>
[GhostField] public uint PhaseEndTick;
/// <summary>Live Husk wave number during Defend, synced from the server-only WaveState by CyclePhaseSystem so the replicated-state-only HUD can show it (holds the last wave number outside Defend; the HUD gates the display to the Defend phase).</summary>
[GhostField] public int WaveNumber;
}
/// <summary>Phase constants for <see cref="CycleState.Phase"/> — the GLOBAL shared posture (a byte, not an enum, for trivial Burst/serialization). Being out on an expedition is per-player presence (server-only RegionTag), NOT a global phase.</summary>
public static class CyclePhase
{
// Re-meaned IN PLACE — the byte VALUES are unchanged from the old Expedition/Defend/Build, so the
// [GhostField] serializer layout is identical (the const re-mean alone forces no re-bake). slot 0 (was
// Expedition) -> Calm; slot 1 (was Defend) -> Siege; slot 2 (was Build) -> retired.
/// <summary>The persistent, unhurried home base — the DEFAULT posture. No countdown; build/prep at your pace.</summary>
public const byte Calm = 0;
/// <summary>The base is under assault by a Husk wave (event-triggered; ends when the wave is cleared).</summary>
public const byte Siege = 1;
}
/// <summary>
/// Server-only bookkeeping for the run-state machine that must NOT replicate (kept separate from the
/// replicated <see cref="CycleState"/>). Records the wave number captured when the current Siege began plus
/// the procedural-expedition-field session epoch (bumped when the expedition region goes empty-&gt;occupied so
/// the field reseeds per sortie).
/// </summary>
public struct CycleRuntime : IComponentData
{
/// <summary>WaveState.WaveNumber captured the moment the current Siege started (DefendCleared tests &gt; this).</summary>
public int DefendStartWave;
/// <summary>Monotonic expedition-field session counter; bumped on the expedition region's empty-&gt;occupied edge so each sortie reseeds. RNG seed (never tick math; never 0, via max(1, ...)).</summary>
public int ExpeditionEpoch;
/// <summary>The <see cref="ExpeditionEpoch"/> the field was last seeded for (compared by int equality).</summary>
public int LastSpawnedEpoch;
/// <summary>Previous-tick expedition occupancy (1 = at least one player out), for the empty&lt;-&gt;occupied edge.</summary>
public byte PrevExpeditionOccupied;
/// <summary>The <see cref="ExpeditionEpoch"/> a zone-clear Ore reward was last banked for — gates the once-per-epoch reward so two same-tick co-op returners pay once and gate re-entry can't farm (int equality, never tick math).</summary>
public int LastRewardedEpoch;
/// <summary>1 once the current epoch's expedition wave has FULLY spawned and been cleared to zero live zone enemies; reset to 0 on the empty-&gt;occupied epoch bump. The reward fires only on a REAL clear.</summary>
public byte ClearedThisEpoch;
}
/// <summary>
/// DR-042 C7b — a SMALL replicated summary of the current expedition objective so the client HUD can show an
/// "enemies remaining / cleared — return to claim" readout. Rides the GLOBAL UNTAGGED CycleDirector ghost
/// (alongside <see cref="CycleState"/> / GoalProgress) so GhostRelevancy.SetIsIrrelevant never hides it
/// "enemies remaining / cleared — return to claim" readout. Rides the GLOBAL UNTAGGED director ghost so
/// GhostRelevancy.SetIsIrrelevant never hides it
/// cross-region — a base teammate can't see the expedition's own (region-tagged, relevancy-hidden) enemy
/// ghosts. SOLE writer: ZoneEnemyDirectorSystem (server, plain group), written ABOVE its early-returns
/// (snapshot-above-early-return) so the readout never freezes stale. byte/short, never enum (writer is [BurstCompile]).