END-2: final siege + latching win/lose (SL-3)

At GoalProgress.Charge>=Target a new server-only GoalReachedSystem arms a larger final siege (x live FinalSiegeMultiplier) and flips RunPhase=FinalDefense; CyclePhaseSystem latches a REPLICATED RunOutcome (Victory on clear / Loss on Core breach) and halts the director. RunOutcome is a [GhostField] byte on the global CycleDirector ghost (the client banner observes it); RunPhase stays server-only. ThreatDirector/CoreRestore/CoreDamage halt once decided; SiegeTimeout is off during the final siege. SaveData v5 persists the outcome so a won/lost run loads finished. GoalProgress.Target 10->4. Completes Path A's spine. See DR-036.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 12:38:21 -07:00
parent 33c85c4f9a
commit 4f0b4e8087
16 changed files with 313 additions and 33 deletions
@@ -59,7 +59,7 @@ namespace ProjectM.Authoring
});
AddComponent<ResourceLedger>(entity);
AddBuffer<StorageEntry>(entity);
AddComponent(entity, new GoalProgress { Charge = 0, Target = 10 });
AddComponent(entity, new GoalProgress { Charge = 0, Target = 4 }); // END-2: 4 survived sieges -> the final siege (the 5th)
// END-1: the losable Engine Core rides this GLOBAL ghost (no new ghost / no relevancy). Born full;
// CycleDirectorSpawnSystem overrides Current with a persisted wounded value on Continue.
AddComponent(entity, new CoreIntegrity
@@ -68,6 +68,11 @@ namespace ProjectM.Authoring
Max = authoring.CoreIntegrityMax,
OverrunTick = 0u,
});
// END-2: the terminal run outcome is REPLICATED ([GhostField]) so the client HUD shows the win/loss
// banner by observing it. Baked here -> part of the ghost serializer (one re-bake). Born InProgress;
// CycleDirectorSpawnSystem overrides it with a persisted Victory/Loss on Continue.
AddComponent(entity, new RunOutcome { Value = RunOutcomeId.InProgress });
AddComponent(entity, new ThreatConfig
{