LANTERN purge: delete the superseded base/expedition shell (audit H1/H3/M5)

The 2026-08-06 audit found the shipping scene was still the abandoned
co-op-Hades game with LANTERN combat bolted on, and that a third of the
codebase was live code for a direction abandoned on 2026-07-13. Operator
chose deletion over freezing: "everything is saved in source control if
needed. I want the project to be clean."

DELETED (~140 source files, Scripts 335->231, Tests 77->43):
- Enemy variants + boss (H3). ChargerAuthoring / SpitterAuthoring /
  SwarmerAuthoring were attached to ZERO prefabs, so LungeState /
  SpitterState / SwarmerTag were never baked: ~272 lines of Bursted AI
  passes, BossAISystem (261 lines) and the whole MixBands escalation
  curve could not match a single chunk at runtime, while 734 lines of
  green tests certified them. Both shipping enemy prefabs were already
  byte-identical in stats.
- Run/room lifecycle: RunDirector FSM, RunInfo/RunMap/RoomPlan/RoomTag,
  route select, portal interact, ready-check, room field/teardown.
- Meta shop, prep loadout, boons (incl. KillRewardSystem and
  DashTrailDamageSystem, which existed only to serve boon flags).
- Build palette + structures, shared storage, inventory/equipment
  (already recorded PAUSED in CLAUDE.md).
- The HUD panels driving all of the above (HudSystem 1168 -> 610).

KEPT deliberately: BaseGridMath + BaseAnchor (8 systems use PlotCenter
for spawn rings, respawn and dynamic light), the resource ledger +
StorageMath, the save system, region/relevancy. Three of these were in
the delete set until I checked their consumers — worth remembering that
the file-level manifest was wrong about them.

Also folds in audit finding M5: PlayerClass was a second, server-only
copy of the byte FrameId already replicates. It existed for the meta
shop; with that gone, FrameId is the single frame identity.

Harvest is now single-sink (ledger). HarvestMath keeps its shape so
LANTERN's carried-vs-banked cargo split lands in one place, not two.

295/295 EditMode green, zero compile errors. Subscene re-bake and Play
validation follow in the next commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-07 12:59:39 -07:00
parent 6a412fe3e7
commit 62e48a3b0b
304 changed files with 260 additions and 14591 deletions
@@ -98,7 +98,6 @@ namespace ProjectM.Client
EntityManager.CompleteDependencyBeforeRO<AttackWindup>();
EntityManager.CompleteDependencyBeforeRO<EnemyStats>();
EntityManager.CompleteDependencyBeforeRO<EnemyTelegraph>();
EntityManager.CompleteDependencyBeforeRO<IsLunging>();
// Local player (strike-beep proximity gate).
_localPlayer = Entity.Null;
@@ -122,7 +121,6 @@ namespace ProjectM.Client
Unity.NetCode.NetworkTick serverTick = SystemAPI.TryGetSingleton<NetworkTime>(out var nt) ? nt.ServerTick : default;
_dangerSeen.Clear();
_enemySeen.Clear();
bool bossRoom = SystemAPI.TryGetSingleton<RunInfo>(out var dangerRi) && dangerRi.Lifecycle == RunLifecycle.InRoom && dangerRi.CurrentRoomType == RoomTypeId.Boss; // A7: in a Boss room the Charger-kind enemy IS the boss (adds are swarmers)
if (serverTick.IsValid)
{
@@ -151,26 +149,16 @@ namespace ProjectM.Client
}
_prevWindup[entity] = until;
// Feature D: a committed Charger lunge keeps the cue ALIVE past windup (AttackWindup zeroes at commit).
bool lunging = SystemAPI.HasComponent<IsLunging>(entity) && SystemAPI.IsComponentEnabled<IsLunging>(entity);
bool isBoss = bossRoom && tele.ValueRO.Kind == ZoneEnemyMath.KindCharger; // A7: boss radial SLAM telegraph
if (until == 0u) continue;
if (until == 0u && !lunging) continue;
float intensity;
if (lunging)
{
intensity = 1f; // mid-lunge: max danger, persistent until IsLunging clears
}
else
{
var untilTick = new Unity.NetCode.NetworkTick(until);
if (!untilTick.IsValid || !untilTick.IsNewerThan(serverTick)) continue; // windup already elapsed
int remaining = untilTick.TicksSince(serverTick);
// Feature C: per-enemy windup duration (baked, client-safe) -> ramps 0->1 ending AT impact for
// any windup length (fixes the Charger plateauing early under the old hard-coded 22).
float windupDur = isBoss ? Tuning.BossSlamWindupTicks : math.max(1f, tele.ValueRO.WindupTicks); // A7: ramp over the boss's real slam wind-up
intensity = math.saturate(1f - remaining / windupDur);
var untilTick = new Unity.NetCode.NetworkTick(until);
if (!untilTick.IsValid || !untilTick.IsNewerThan(serverTick)) continue; // windup already elapsed
int remaining = untilTick.TicksSince(serverTick);
// Feature C: per-enemy windup duration (baked, client-safe) -> ramps 0->1 ending AT impact for
// any windup length.
float windupDur = math.max(1f, tele.ValueRO.WindupTicks);
float intensity = math.saturate(1f - remaining / windupDur);
{;
// Near-impact strike beep (deferred-items pass): a "dodge NOW" cue once per windup, gated to
// enemies near the local player (the danger cone already proves it's winding up to strike).
@@ -211,33 +199,9 @@ namespace ProjectM.Client
_dangerZones[entity] = go;
}
float coneRange = math.max(1f, stats.ValueRO.AttackRange + 0.6f);
if (lunging) coneRange += 1.5f; // forward-stretch the wedge to read the committed travel
if (isBoss && !lunging)
{
// A7: the boss SLAM is RADIAL (Tuning.BossSlamRadius) -> paint a FULL ground ring so the tell
// matches the hit area (a forward wedge sized to melee reach would lie about a radial AoE).
BuildDangerMesh(go.GetComponent<MeshFilter>().sharedMesh, Tuning.BossSlamRadius, 3.14159f, intensity);
}
else if (isBoss)
{
// B4: the boss LUNGE is a committed forward gap-closer (IsLunging bit on through windup +
// travel) - a radial ring would lie about the threat shape; paint a long narrow travel wedge.
BuildDangerMesh(go.GetComponent<MeshFilter>().sharedMesh, math.max(coneRange, 8f), 0.45f, intensity);
}
else if (tele.ValueRO.Kind == ZoneEnemyMath.KindSpitter)
{
// MC-3: a Spitter is a RANGED threat — a melee wedge at its feet is useless. Paint a thin aim
// LANE along its (face-locked) facing out to projectile reach during wind-up, brightening as the
// shot nears so the player reads the line to dodge/dash across it.
float laneLen = 12f;
if (SystemAPI.HasComponent<SpitterState>(entity))
{
var ss = SystemAPI.GetComponent<SpitterState>(entity);
laneLen = math.max(4f, ss.PreferredRange + ss.RangeTolerance + 2f);
}
BuildLaneMesh(go.GetComponent<MeshFilter>().sharedMesh, laneLen, 0.28f, intensity);
}
else BuildDangerMesh(go.GetComponent<MeshFilter>().sharedMesh, coneRange, 0.7f, intensity);
// 2026-08-07 audit purge: the boss radial-slam ring, the boss lunge wedge and the Spitter aim
// lane are gone with BossState / IsLunging / SpitterState. One enemy kind, one melee wedge.
BuildDangerMesh(go.GetComponent<MeshFilter>().sharedMesh, coneRange, 0.7f, intensity);
float2 fwd = AnimParamMath.PlanarForward(xf.ValueRO.Rotation);
var tr = go.transform;
tr.position = (Vector3)xf.ValueRO.Position + Vector3.up * 0.06f;