Slice Combat Depth (MC-3 + wiring + review fixes): Spitter aim-line + player-hit punch, rigged enemies, in-band gate (DR-041)

Completes the Combat Depth slice on top of the MC-2 server spine (56cf60cce):

MC-3 impact juice (client, observe-only):
- 7 FeelConfig fields + ResetDefaults; magnitude-scaled player-dealt-hit camera
  PunchFov on the enemy-Health-decrease edge (camera-only hit-stop, never timeScale).
- Spitter Kind==2 aim-LANE telegraph (BuildLaneMesh) — reads baked SpitterState
  client-side, falls back to a fixed length. True freeze + material flash deferred.

Content / wiring:
- SpitterProjectilePrefabAuthoring (the SpitterProjectilePrefab singleton).
- Both directors rebuilt to a 4-entry KIND-INDEXED roster [Grunt,Charger,Spitter,
  Swarmer] + mix/MaxAlive config + the SpitterProjectileConfig singleton in the subscene.
- Real rigged models: EnemySpitter (re-skinned Kaiju, ranged poker) + EnemySwarmerUndead
  (Undead-Werewolf, fast/low-HP); grunt/charger keep Werewolf/ChargerMuscle. EnemySpit =
  ownerless interpolated ghost (no Health, no collider).

Post-impl adversarial review fixes (wf_febdcfdb-665):
- [MED] in-band fire gate: the Spitter committed its telegraph from ANY range (fired while
  advancing from far). Now commits only when sInBand || sCornered (gives CorneredRange a
  real read site) — a Spitter out-of-band holds fire and repositions.
- [LOW] EnemyProjectileDamageSystem early-returns on !ServerTick.IsValid (sibling parity).
- [LOW] EnemyAuthoring bake-time guard: errors if a prefab composes both Charger+Spitter
  (would match zero AI passes -> never move).
- [LOW] tests: Spitter brain fires from Expedition (kills the Base==0 region false-green);
  a direct partition-exclusion test replaces the order-masked claim; added out-of-band +
  cornered negative tests.

388/388 EditMode green + two Play smokes (clean boot, fire, swept-hit, region, server==
client; rigged Kaiju spitter bakes + fires with zero console errors). Accepted as-is
(documented in DR-041): global spit soft-cap, co-op punch attribution.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-24 21:08:59 -07:00
parent 56cf60cce3
commit e32dadbc66
20 changed files with 5907 additions and 16 deletions
@@ -0,0 +1,33 @@
---
title: Combat Depth Slice — Enemy Variety (MC-2) + Impact (MC-3) — Build
date: 2026-06-24
tags: [session, combat, enemies, netcode, juice, slice, rukhanka]
permalink: gamevault/07-sessions/2026/2026-06-24-combat-depth-build
---
# Combat Depth (MC-2 + MC-3) — Build session
Built the combat-depth slice the operator chose after Slice 3 ("the combat needs a lot more work"). Spec + forks + the full build record are in [[DR-041_Slice_Combat_Depth_Enemy_Variety_Impact]]. This log is the build narrative.
## What shipped
- **Two new enemy questions:** the **Spitter** (ranged reposition — holds a preferred range-band, fires a telegraphed dodgeable spit) and the **Swarmer** (surround — deterministic cluster spawn). On top of the existing Grunt (walk-up melee) + Charger (committed lunge) → four distinct readable questions.
- **4-type weighted mix** in BOTH directors (expedition `ZoneEnemyDirector` + base-siege `WaveSystem`, fork-4a) sharing one pure `ZoneEnemyMath` composition function, with the mandatory `MaxAlive` cap. Legacy size/charger curve kept + parity-tested.
- **MC-3 impact:** magnitude-scaled player-dealt-hit camera punch + the Spitter aim-LANE telegraph (camera-only hit-stop, never `Time.timeScale`).
- **Real rigged enemies:** Spitter = a re-skinned rigged Kaiju, Swarmer = a rigged Undead-Werewolf (fast/low-HP); grunt/charger unchanged. Spit projectile = `EnemySpit` ownerless interpolated ghost.
## How it went (verify ladder)
- **MC-2 server spine** committed first as `56cf60cce` (already green at the time).
- **MC-3 + tests + rigging + wiring** built via MCP. **388/388 EditMode** (added Spitter-brain, swarmer-cluster, projectile, mix, and 3 review-driven guard tests).
- **Play smoke (×2):** clean two-world boot (no `ComponentSystemSorter` cycle, no Burst ICE), Spitter fires → spit sweeps → player damaged (HP 105→25) → `DamageEvent` drained, region-correct, **replicated server→client**. Re-ran with the rigged Kaiju spitter after the fix: spawns + fires + damages, **zero console errors** (Rukhanka rig baked intact).
## Post-impl adversarial review (`wf_febdcfdb-665`)
3 lenses (netcode/relevancy · determinism/prediction · reuse/test-validity) → per-finding refutation. Caught **1 MED + 5 LOW**:
- **[MED, FIXED] Spitter fired from any range** — the wind-up commit lacked the locked in-band gate, so it would telegraph+fire while still advancing from far (defeating the hold-range question). Fixed: commit only when `sInBand || sCornered` (which also gave the previously-dead `CorneredRange` a real read site). The review's value re-confirmed — a true behavioral deviation 388 green tests + a clean Play had NOT caught.
- **[LOW, FIXED]** `EnemyProjectileDamageSystem` missing `!ServerTick.IsValid` guard; bake-time guard against a Charger+Spitter prefab; two test false-greens (Base==0 region assert → fire from Expedition; overstated partition claim → a direct partition-exclusion test).
- **[LOW, DOCUMENTED]** global (not per-region) spit soft-cap; co-op camera-punch attribution. Both per the locked design / generous bounds.
## Gotchas worth remembering
- **Director rosters are KIND-INDEXED (index == Kind 0..3), NOT round-robin.** The `WaveDirector` still held the OLD round-robin pool `[Werewolf, WerewolfUndead, Kaiju, ChargerMuscle]` — under MC-2 that silently maps the charger model into the Swarmer slot etc. Always rebuild the roster as `[Grunt,Charger,Spitter,Swarmer]` when adopting `KindForSlot`.
- **A `DynamicBuffer` handle is invalidated by any later structural change** — in a test, create the prefab entities BEFORE `AddBuffer`, populate with no `CreateEntity`/`AddComponent` in between (the cluster-test setup bug).
- **Reuse rigged variants, don't rebuild rigs.** A plain `AssetDatabase.CopyAsset` duplicate of a working Rukhanka prefab + adding a marker authoring bakes clean and dodges the `EnemyRigTools`/skeleton-rebuild risk entirely.
- The fun-gate co-op playtest (Slice 3's too) is still **pending** — the open question is whether the fight is fun, not test counts.