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:
@@ -58,6 +58,9 @@ namespace ProjectM.Server
|
||||
});
|
||||
ecb.AddComponent(director, new CycleRuntime { DefendStartWave = 0 });
|
||||
ecb.AddComponent(director, new ThreatState());
|
||||
// END-2: server-only run-phase marker (Normal until the goal cap arms the final siege). Added at
|
||||
// spawn like CycleRuntime/ThreatState (never on the ghost serializer). RunOutcome is baked on the prefab.
|
||||
ecb.AddComponent(director, new RunPhase { Value = RunPhaseId.Normal });
|
||||
|
||||
// Born-correct load: if the menu staged a save (Continue), apply it AT SPAWN so the director
|
||||
// ghost never serializes a default GoalProgress / empty ledger to clients (no replication flicker).
|
||||
@@ -66,7 +69,13 @@ namespace ProjectM.Server
|
||||
var pending = SystemAPI.GetComponent<PendingSave>(pendingEntity);
|
||||
if (pending.HasData != 0)
|
||||
{
|
||||
ecb.SetComponent(director, new GoalProgress { Charge = pending.GoalCharge, Target = pending.GoalTarget });
|
||||
// END-2: clamp the restored Target to the baked run-length so a pre-v5 save carrying the old
|
||||
// Target=10 still honours the slice's baked Target=4 (the final siege stays reachable).
|
||||
int bakedTarget = SystemAPI.HasComponent<GoalProgress>(spawner.Prefab)
|
||||
? SystemAPI.GetComponent<GoalProgress>(spawner.Prefab).Target : pending.GoalTarget;
|
||||
int restoredTarget = pending.GoalTarget > 0 && pending.GoalTarget < bakedTarget
|
||||
? pending.GoalTarget : bakedTarget;
|
||||
ecb.SetComponent(director, new GoalProgress { Charge = pending.GoalCharge, Target = restoredTarget });
|
||||
var srcLedger = SystemAPI.GetBuffer<PendingSaveLedgerRow>(pendingEntity);
|
||||
var destLedger = ecb.SetBuffer<StorageEntry>(director);
|
||||
SaveApply.WriteLedger(srcLedger, destLedger);
|
||||
@@ -82,6 +91,10 @@ namespace ProjectM.Server
|
||||
ecb.SetComponent(director, new CoreIntegrity { Current = restoredCore, Max = bakedCore.Max, OverrunTick = 0u });
|
||||
}
|
||||
|
||||
// END-2: born-correct the terminal run outcome (a won/lost run loads finished + halted; a pre-v5
|
||||
// save / New Game = 0 -> InProgress). Independent of the Core -> NOT nested in the CoreIntegrity guard.
|
||||
ecb.SetComponent(director, new RunOutcome { Value = pending.RunOutcome });
|
||||
|
||||
}
|
||||
ecb.DestroyEntity(pendingEntity);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user