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
@@ -5,99 +5,33 @@ using UnityEngine;
namespace ProjectM.Authoring
{
/// <summary>
/// Authoring for the GLOBAL cycle-director ghost prefab: an ownerless INTERPOLATED ghost (the
/// Authoring for the GLOBAL director ghost prefab: an ownerless INTERPOLATED ghost (the
/// GhostAuthoringComponent is inherited when this prefab is duplicated from UpgradePickup.prefab) that
/// carries the replicated macro-loop state (<see cref="CycleState"/>) and the shared resource ledger
/// (a <see cref="StorageEntry"/> buffer marked by <see cref="ResourceLedger"/>). It is GLOBAL — it must
/// carries the shared resource ledger (a <see cref="StorageEntry"/> buffer marked by
/// <see cref="ResourceLedger"/>), the replicated run-lifecycle FSM (<see cref="RunInfo"/>), the
/// expedition-objective readout, and the per-class permanent-meta tier buffer. It is GLOBAL — it must
/// carry NO <see cref="RegionTag"/> so GhostRelevancy keeps it relevant to every connection regardless of
/// region. The server CycleDirectorSpawnSystem overrides the baked CycleState at spawn (real PhaseEndTick)
/// and adds the server-only CycleRuntime.
/// region. (The old cycle/siege/goal/core state is retired — LANTERN purge.)
/// </summary>
public class CycleDirectorAuthoring : MonoBehaviour
{
[Header("Threat — post-expedition retaliation")]
[Tooltip("A completed expedition (a player returning to base) can draw a retaliation siege.")]
public bool PostExpeditionEnabled = true;
[Tooltip("Telegraph/arming delay (server ticks @60) between the return and the siege spawning.")]
public uint PostExpeditionDelayTicks = 300;
[Tooltip("Siege size floor (Husk count) for a post-expedition retaliation.")]
[Min(0)] public int SiegeSizeBase = 5;
[Tooltip("Extra Husks per unit of resources hauled back this run (0 = flat SiegeSizeBase).")]
[Min(0)] public int SiegeSizePerResource = 0;
[Tooltip("Max server ticks a siege may run before it auto-collapses (no soft-lock). 0 = no cap.")]
public uint SiegeTimeoutTicks = 3600;
[Header("Threat — scheduled base sieges (DR-042: DISABLED — reserved/inert hook)")]
[Tooltip("DR-042: OFF. A blind timed cadence was the AFK win path (auto-armed sieges the SiegeTimeout auto-cleared). The win-driver is now expedition clears; base sieges are post-expedition retaliation only. Code path kept as a config-inert reserved hook.")]
public bool ScheduleEnabled = false;
[Tooltip("Server ticks (@60) between scheduled base sieges. First fire is one interval out (mine/build grace).")]
public uint ScheduleIntervalTicks = 2700;
[Tooltip("Extra Husks per surviving wave (siege size = SiegeSizeBase + this * WaveNumber). 0 = flat.")]
[Min(0)] public int ScheduleSizePerWave = 1;
[Header("Endgame — Engine Core (END-1)")]
[Tooltip("Baked integrity ceiling of the losable Engine Core. Current is born full (or the persisted wounded value on Continue).")]
[Min(1)] public int CoreIntegrityMax = 100;
private class CycleDirectorBaker : Baker<CycleDirectorAuthoring>
{
public override void Bake(CycleDirectorAuthoring authoring)
{
var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);
AddComponent(entity, new CycleState
{
Phase = CyclePhase.Calm,
CycleNumber = 1,
PhaseEndTick = 0u,
});
AddComponent<ResourceLedger>(entity);
AddBuffer<StorageEntry>(entity);
AddComponent(entity, new GoalProgress { Charge = 0, Target = 2 }); // DR-044 demo pacing: 2 expedition clears -> the climactic final siege (~10-20 min hands-off run)
// 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
{
Current = authoring.CoreIntegrityMax,
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 });
// DR-042 C7b: replicated expedition-objective summary (the HUD 'enemies remaining / cleared' readout).
// Born Idle; ZoneEnemyDirectorSystem is the sole writer. New [GhostField] component -> re-hashes the
// runtime-spawned director ghost (server + client bake the same prefab -> hash matches), like CoreIntegrity.
// Born Idle; RoomEnemyDirectorSystem is the sole writer.
AddComponent(entity, new ExpeditionObjective { State = ExpeditionObjectiveState.Idle, Remaining = 0 });
// Expedition redesign: the replicated run-lifecycle FSM (RunInfo, 17 [GhostField]s) + the per-class
// permanent-meta tier buffer (MetaTierState) BOTH land in this ONE coordinated re-bake (front-loaded
// ghost layout — the writer systems arrive across Steps 213 while the state sits inert/default).
// Born Staging/empty; server RunDirectorSystem / MetaSpendSystem are the sole writers.
// Expedition redesign: the replicated run-lifecycle FSM (RunInfo) + the per-class permanent-meta
// tier buffer (MetaTierState). Born Staging/empty; server RunDirectorSystem / MetaSpendSystem are
// the sole writers.
AddComponent(entity, new RunInfo { Lifecycle = RunLifecycle.Staging });
AddBuffer<MetaTierState>(entity);
AddComponent(entity, new ThreatConfig
{
PostExpeditionEnabled = (byte)(authoring.PostExpeditionEnabled ? 1 : 0),
PostExpeditionDelayTicks = authoring.PostExpeditionDelayTicks,
SizeBase = authoring.SiegeSizeBase,
SizePerExpeditionResource = authoring.SiegeSizePerResource,
StartCondition = ThreatStartCondition.Immediate,
SiegeTimeoutTicks = authoring.SiegeTimeoutTicks,
ScheduleEnabled = (byte)(authoring.ScheduleEnabled ? 1 : 0),
ScheduleIntervalTicks = authoring.ScheduleIntervalTicks,
ScheduleSizePerWave = authoring.ScheduleSizePerWave,
});
}
}
}