Files
Project-M/Docs/Vault/06_Roadmap/Iteration_2026-07_CoopHades.md
T

238 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Iteration 2026-07 — Co-op Hades Conversion (Trunk + Hub-ification + Depth + Blender)
date: 2026-07-06
tags: [roadmap, iteration, coop-hades, direction, plan]
permalink: gamevault/06-roadmap/iteration-2026-07-coophades
---
# Iteration 2026-07 — Co-op Hades Conversion
**Operator decision (2026-07-06, dev-loop iteration session):** commit the *Fix-the-Fight-in-Place trunk* PLUS the full
*Co-op Hades* direction — cut/mothball the tower-defense layer, base becomes a Hades-house hub, boons become
mechanic-changers, kit gains a second ability, real node types, second boss, co-op revive. **Depth folds in now** (not
after a playtest gate). **Blender scope: hero props + decals.** No time estimates (operator directive). Grounded by a
9-agent analysis workflow (`wf_8afc6e5a-b9e`, 9/9 clean) + a live end-to-end play tour with screenshots
(`Assets/Screenshots/iter_*.png`).
## Evidence base (why this direction)
- Live tour findings: half of all rooms (sub-slot X+1500) have **no ground/colliders/decor**; enemies interpenetrate
into a blob and spawn converges on the player; HUD header reads "AT BASE" inside expedition rooms; boon offer
contained a strictly-dominated pair (-25% vs -15% ability cooldown); portal beacon renders as a blown-out white egg;
storage crate floats; **save-persisted `RunRuntime.WasAllReady=1` deadlocks the next session's launch** (un-ready →
re-ready is the only escape).
- Critique consensus: every reward is a flat stat mod (zero mechanic-changers/synergies); kit = 3 verbs/class with 1
distinct ability; run 2 ≡ run 1; threat is walk-out-able (boss slam escape needs 4.07 u/s vs 5.1 available; melee
knockback stunlocks all trash; death = free heal + re-conscription); the boss is a 1.6× Charger with one attack.
The code-side feel layer (cones, slash arcs, hit-stop, flashes) is STRONG — the gap is the asset/scene layer under it.
- Market: Windblown's chart-topping demo = 1 biome + 1 boss (loop is demo-sized already); Cult of the Lamb = the
base↔run tie done right, Moonlighter = the cautionary tale; HoH2's retention lever = every run banks something;
Synty-look ceiling proven by Death Scourges / RoR2 / Soulstone Survivors (dynamic-light-first, dark ambient).
## Phase 0 — Stabilize (bugs found live, do first)
1. Commit the working-tree portal fix (incl. the `_portalMat` comment-swallow NRE repair applied 2026-07-06 — the NRE
killed CombatFeedbackSystem every session and auto-paused the editor via Error Pause). Re-run 456 EditMode first.
2. ~~`WasAllReady` save deadlock~~ **RETRACTED (2026-07-06):** misdiagnosis caused by frozen-world polling (the
`execute_code` main-thread-sleep hazard) — `CycleDirectorSpawnSystem`'s restore builds `RunRuntime` fresh (HostSalt
only, WasAllReady=0) and nothing persists it; the observed "deadlock" was a run that had already launched between
MCP calls. Lesson reinforced: never sleep-poll; check `isPaused` first.
3. Lifecycle-aware HUD header (kill "AT BASE" in rooms; kill stale "choose your boon" after pick — the location-line
switch had NO RoomExplore case, so the RoomReward text stuck through the loot window).
4. Ground the floating storage crate (**root cause: `GridOrigin.y=1` is the CC capsule-center plane** —
`CellToWorld` returns it; center-pivot meshes (Turret/Wall) look grounded there, base-pivot meshes (crate) float
1 u. Watch this on every future grid-placed base-pivot mesh); tame the portal-beacon HDR (2.6/3.4 bloomed to a
white egg) + ground the beacon (its `pos.y` was the capsule plane too).
## Phase 1 — Trunk (direction-agnostic physical/feel/teaching layer)
1. **Rooms become places:** fix the X+1500 void (ground + collider ring + decor for sub-slot 1); seed 36 collidable
Environment-layer props per room so cover/LOS/Charger-baiting exist; readable room edge.
2. **Enemy separation** (server-side repulsion, MaxAlive=12 so O(n²) is fine) + soft player pushout; no standing
inside the boss. Verify spawn ring staggering reads (spawn VFX).
3. **Animation triage:** Synty Sword/Bow Combat anim packs (shared Generic skeleton, no retarget) — death clips +
corpse window, spawn, hit-react, ranged-fire, dash; creature locomotion for monsters (stop human jog on Husks).
4. **Threat repair:** poise byte (light hits stop stunlocking trash); boss second attack (re-enable its baked Charger
lunge) + party-size HP scaling.
5. **Live knobs:** promote all Boss*/enemy/grace-timer knobs into the TuningConfig overlay + values-to-clipboard.
6. **Teaching:** RMB class ability + dash coach-marks; fix stale copy (StartingOre 50→90 etc.); run-failed banner.
7. **Clarity swaps:** distinct spit/pickup/harvest meshes (Synty interim), class weapon meshes on the hand bone,
ground darkening + character rim/outline, point lights on projectiles (RoR2 trick).
**Gate:** one honest solo campaign to Victory + one 2-instance LAN lap with the overlay open.
> **Gate outcome (2026-07-07 operator playtest):** trunk holds mechanically, but the verdict is *"not cohesive, very
> static, visually boring — comparable games' environments are dynamic and interactable"* + ability upgrades boring +
> the 07-07 harvest shrink/pop never showed on-screen (root cause later pinned same day: a swallowed
> `[RuntimeInitializeOnLoadMethod]` attribute — NOT the pre-flagged PTM-on-root risk, which live-verified correct).
> → **Phase 1.5 (World Alive) inserted below; mechanic-changer boons PULLED FORWARD from Phase 3 (Phase 1.7).**
> Explicit operator framing: make the game **look better**, not more "juice" — the code-side hit-feel layer stays as-is.
### Slice B folded design (design-review `wf_fd177263-b67`, 2026-07-06 — 3 lenses done, 15 verify verdicts ALL isReal=true; synthesis died on session limit, folded by hand from the verified findings)
- **B3 Dying window:** server-only `Dying{uint UntilTick}` (TickUtil.NonZero, stamp ONCE on the 0-crossing in
HealthApplyDamageSystem for EnemyTag; destroy after ~54 ticks via Temp ECB). At the mark: zero the REPLICATED
`AttackWindup`, disable `IsLunging`, zero `KnockbackState` (else corpses keep live telegraphs). CONFIRMED: no AI
pass self-health-gates today and EVERY count edge counts entities — gate the actor passes (EnemyAI/BossAI move,
windup, contact) and make every count LIVING-only: RoomEnemyDirectorSystem :48/:68 (room clear + MaxAlive fit),
ExpeditionObjective.Remaining, boss-summon cap, base-siege cleared checks, aggro snapshots, ProjectileDamageSystem
:83-91 target snapshot + gamepad auto-aim (no Health>0 filter today — corpses become shields/aim magnets). Client:
EnemyAnimationDriveSystem writes `IsDead = Health.Current<=0` (param exists, never written); CombatFeedbackSystem
fires the kill CRUNCH at the 0-crossing (today it fires at prune — would land ~1 s late), prune keeps only a
cleanup puff; hide health bars at <=0.
- **B1 separation:** a pass INSIDE EnemyAISystem (sole-position-writer invariant; BossAISystem only has
[UpdateAfter(EnemyAISystem)]). Pairwise among LIVING enemies, skip lunging/knocked/Dying receivers, boss immovable
(others push out of its radius), displacement through the swept EnemyMoveUtil move (no wall-shoving). Player
pushout radius SMALL (< melee range, ~HitRadius+0.35) or grunts can't strike + Spitter cornered-hold re-breaks.
- **B2 poise:** the windup-cancel is the knockback CONTINUE branch (EnemyAISystem :119-133 grunt, :234-248 charger).
Threshold on the EXISTING `KnockbackState.Speed` channel (no per-kind data): below it, kb velocity nudges but the
windup/lunge survives; at/above it, stagger as today. Make the finisher + Warrior cone stamp a higher kb Speed so
the channel actually distinguishes (light 6 / heavy 9, threshold ~7).
- **B4 boss lunge:** BossState gains a server-only attack byte; lunge = reposition move (no unique damage — the
slam stays the damage beat). Telegraph rides `AttackWindup` + the boss's already-baked `IsLunging` ghost bit;
the client boss-ring override SKIPS the slam ring while IsLunging (draws the standard Charger lunge tell). The
windup-elapse branch keys on the state byte (today it would deal SLAM AoE on any elapse).
- **B5 party HP scale:** in the RoomEnemyDirectorSystem boss branch (sole spawn site), scale Health by LIVING
EXPEDITION players (RegionTag+Health>0), NOT RunParticipant (dead-respawned keep the tag at base).
- **B6 run-failed banner:** client-only; cache Charge + a wentInRun flag at launch; on the (in-run)->Staging
transition with Charge unchanged -> "EXPEDITION FAILED" banner. NEVER key on Returning (1-tick transient, and the
bank lands one tick after it). Launching->Staging aborts don't count (wentInRun gate).
- **B7 fire-anim pulse:** stateless absolute window derived from AbilityCooldown (the SwingActive idiom) — no
cached edges (rollback/join-unsafe).
- **Hygiene:** bytes not enums in Bursted systems; no new [UpdateBefore/After] edges; ~54-tick window + stagger
threshold + separation strength as live TuningConfig knobs. ⚠ Bursted query-set edits on an UNFOCUSED editor can
leave a stale Burst binary — have the editor focused for the compile, or expect the restart cure.
## Phase 1.5 — World Alive (inserted 2026-07-07 from playtest verdict; visual + dynamism, explicitly NOT juice)
Operator interactivity scope (all four selected + two write-ins): breakable clutter with drops · environmental
hazards · cosmetic reactivity + ambient motion · destructible cover · **collision precision** · **more distinct
attacks/animations/effects**. Art direction deliberately UNDECIDED — lighting pass first, then pick (see gate).
### Stabilize first (bugs, do before the pass)
1. ~~Node harvest feedback fix~~ **FIXED (2026-07-07 session B).** Root cause was NOT the pre-flagged
PTM-on-root risk — live inspection proved the Model child properly parented and root-PTM propagation exact
(root PTM 0.783 → child LTW 1.723 = 2.2×0.783). The 07-07 knob edit had **swallowed the
`[RuntimeInitializeOnLoadMethod]` attribute** off `WorldFeelConfig.ResetDefaults``Enabled=false` → the WHOLE
feedback slice (shrink/pop + chips + SFX + micro-punch) silently dead. One-line restore; verified on-screen +
numerically on a live server-spawned node; 455/455. Operator still eyeballs a real harvest in the next playtest.
2. ~~Collision precision pass~~ **DONE (2026-07-09, gate-approved incl. missing-collider adds).**
`ColliderFitTools` (Editor, audit/apply) refit everything to the Game.unity visuals: rings rebuilt
16/24/24→54/82/82 fine segments fitted to torso-band cliff vertices; cover = convex MeshColliders of the
actual rock meshes (needed Read/Write on 2 PNB meshes — new CLAUDE.md gotcha); landmarks hint-matched OBB
fits; 11 solid-prop colliders added (tree/turbine/crates/dish/solar). Verified in the baked CollisionWorld
(ring closure, cover 8/8 solid, server==client parity) + enemy backstop re-validated live (embedded enemy
depenetrates out of a cover hull and keeps moving). Operator feel-lap still pending. See
[[2026-07-09_B4d_Tail_Collision_Grounding]].
### Short-term wins (asset/scene/client layer)
3. ~~Lighting/atmosphere pass~~ **DONE (2026-07-09, `414f9ff62`)** — dark ambient + DynamicLightSystem
(projectile/portal/node/impact-flash lights) + landmark mood lights + per-biome dark palettes; found+fixed
the dead Linear-fog density knob. **ART LOOK LOCKED (operator, 2026-07-09): CLEAN DARK-SYNTY — pixel filter
OFF** ("everything feels smoother & sharper without it"); the PixelOutline feature stays dev-toggleable (F3)
with `_MasterEnabled=0` shipped. Operator is open to experimenting with OTHER fullscreen/shader effects
later (sharpen/vignette/grade variants — backlog). Every later visual choice keys off clean dark-Synty.
4. **Ambient motion layer (client-only, zero netcode):** foliage sway/rustle on walk-through, per-biome dust/embers/fog
drift particles, critters that scatter, flags/banners. Biggest bang-per-effort for "not static".
5. ~~Breakable clutter with drops~~ **DONE (2026-07-09, `53bc21143`)** — the BlightClutter chain already
existed end-to-end (seeding/unified-sweep/drops); the gap was feel: barrels (SM_Prop_Barrel_01) replace the
rock chunk, ONE-hit pop (Remaining 8→2), density 6→8/room. Live-verified pop + drop credit.
6. **Attack distinctness — CODE-SIDE DONE (2026-07-09, `a335ca3f2`):** per-kind telegraph colours (shapes
were already per-kind), per-kind windup voices, ownership-keyed projectile lights. The anim-side depth
(per-kind clips, creature locomotion — still open from Phase 1.3) rides the parallel Blender workstream.
### Long-term (server-sim; adversarial design-review REQUIRED per slice)
7. **Environmental hazards — v1 SHIPPED (2026-07-10, `cd607ae15`, review-first `wf_2cb10454-fdf`):**
exploding barrels — ~25% of clutter seeds explosive; pop lights a ~0.6s FUSE (replicated Remaining=0 =
the client cue, strobing red glow) then radius-damages BOTH sides (bait mechanic; escapable). Zero new
replication. Geysers/trap tiles = later hazard variants on the same HazardExplosionSystem chassis.
See [[Exploding_Barrels_Build_Spec]].
8. **Destructible cover — SHIPPED (2026-07-10, `bbf418e77`, review-first `wf_e14dd739-069`): PHASE 1.5 IS
COMPLETE.** Cover = BlightClutter ghosts (Variant 4) with baked env-layer colliders — genuinely block
(the anti-stuck nudge is now cover-aware) until carved (8 hits, zero yield — deposit decoupled from
durability); no cover in Boss rooms; backstop re-validated live (block/depenetrate/carve all proven).
Follow-ups: Charger smashes cover; true projectile-blocking. See [[Destructible_Cover_Build_Spec]].
### Phase 1.5b — World Alive II (operator expansion, 2026-07-10; ALL four bundles locked)
Post-1.5 operator verdict: barrels fixed (radius disc + 1.5 s fuse, `eb7f57216`) but **"the ground is very
plain"** — expand 1.5 before 1.7. Locked bundles, sequenced ground-first:
1.**Ground bundle** (SHIPPED `2fc5ae9a1`): (a) material pass — real tiling (22×/26×) + Synty normals +
generated LINEAR macro-noise detail textures w/ baked rim vignette (★ an sRGB-imported detail map mul2x
DARKENS 2.3× — import detail albedo LINEAR); per-room-biome ground tint via MPB inside
`WorldAtmosphereSystem`; (b) `RoomDressingSystem`+`RoomDressingConfig` — 26 seeded biome-flavoured props
per room inside the room's ACTUAL shape (hash stream 0xD2E55; 47 build-safe prefab refs), room-torn-down;
live-verified incl. teardown + exact Meadow tint convergence; (c) worn paths storage→warpgate/base-gate;
(d) 357 base flora re-enabled (buildings/pond/FX stay parked).
2. **Decals**: dynamic explosion scorch marks (fading quad pool at boom sites), static POI scorch/cracks,
cover damage-crack states (URPMaterialPropertyBaseColor darkening keyed on replicated Remaining).
3. **Hazard variant**: Blight geyser (periodic both-sides AoE on the HazardExplosion + telegraph-disc
chassis) — mini design review first (new replicated erupt-phase surface).
4. **Critters + weather beats**: fleeing bugs/birds; cloud-shadow drifts; Blight lightning flicker.
Gate: the same "no longer static" lap, now with the ground judged explicitly.
**Gate:** operator playtest says "no longer static"; node harvest feedback visibly works; art direction LOCKED.
## Phase 1.7 — Boon overhaul (PULLED FORWARD from Phase 3, operator call 2026-07-07)
- **Boons → ~12 mechanic-changers** on existing hooks: dash damage trail, finisher AoE detonation, projectile
pierce/fork/chain, ability-SWAP boons (replicated AbilityRef byte via the ClassSwapUtil pattern), knockback→pull…
plus synergy tags, duplicate/dominated-offer protection. Design-review before coding (replicated ability swaps).
**Gate:** two consecutive runs feel different from boons alone.
## Phase 2 — Hub-ification (structural; adversarial design-review REQUIRED before coding)
- **Mothball, don't delete:** sieges/CoreIntegrity/turret-ammo retired from the flow via baked toggles (the
`ScheduleEnabled` pattern); code + tests keep compiling for reversibility.
- **Win condition moves fully into the expedition** (N boss-clears / final-boss Victory; `GoalReachedSystem` +
`RunOutcome` rework; SaveData v7 additive).
- **Base = hub:** class/prep/meta/ready + deposit; visible growth (unlocks at Charge milestones); staging HUD →
progressive disclosure (5 dense panels → contextual).
- **Economy simplification** (4 currencies is bureaucracy; turret-ammo Charge frees up) — scope at the design review.
**Gate:** returning player goes staging→launch in <60 s; hub reads as prep+growth, not friction.
## Phase 3 — Depth (folded in now; design-review per netcode-heavy slice)
- **Kit:** second ability slot + one new active per class (reuse AbilityRef catalog + predicted-input review).
- ~~Boons~~ **MOVED to Phase 1.7** (pulled forward 2026-07-07 — operator playtest called flat-stat upgrades boring).
- **Real node types:** Elite (affixed alphas via the boss spawn-stamp pattern), Shrine (combat-free choice), Survive,
Cursed.
- **Second boss chassis** (Spitter artillery) so lap 2 differs.
- **Co-op:** downed/revive (ex-MC-5), duo boons, party-scaled spawns/HP; death-cost design lands here (with revive in,
death = run stakes, killing the suicide-heal exploit).
**Gate:** two consecutive runs feel different (build + path + boss); a duo playtest yields a clutch-revive moment.
## Phase 4 — Blender workstream (parallel from Phase 1; hero props + decals)
Portal gate ring · hub Core crystal centerpiece · boss bone-socket armor/spike kitbash (no new rig) + second-boss
silhouette parts · distinct projectile/pickup/harvest meshes · class weapons · ground decal/telegraph quad set.
Pipeline (validated + researched): UV new geometry onto the existing Synty atlas for style match (or Imphenzia palette
PNG); FBX for anything skinned (glTFast Mecanim-clip issues), glb via gltfast for statics; Apply Scalings = FBX Units
Scale; Key All Bones for clips; realize geo-nodes instances before export; Play-verify materials (dark-frame hazard).
## Phase 5 — Dev tooling (woven through)
New DebugOps: KillRoom (death-path kill — ClearEnemies destroys without kill credit), SkipToRoom/SpawnBoss, GrantBoon,
run-state line in the overlay · pause-proof screenshot rig (MCP bridge `ScreenshotUtility` calls `EditorApplication.Step()`
and leaves the editor PAUSED — unpause after every capture; never `Thread.Sleep`-poll in `execute_code`, it freezes the
sim) · encounter-composition buttons in DevSandbox · WasAllReady regression test.
## Consolidation notes
Supersedes the DR-042 "final beat = base siege" lock (win moves fully expedition-side). DR-034 Core / EB-1/2 siege
systems → mothballed, not deleted. The "present forks" ritual re-runs at each phase's design review.
**2026-07-07 restructure (Phase 1 gate playtest):** Phase 0 + Phase 1 SHIPPED (commits `7c1fee097``cc2e7ad95`, incl.
Blender clips, sword combo, pixel-art render style, enemy-stuck fix, markers, harvest feedback). Gate playtest verdict
= static/boring world → **Phase 1.5 World Alive** inserted (stabilize: node-feedback fix + collision precision; short:
lighting→art-look gate, ambient motion, breakables, attack distinctness; long: hazards + destructible cover, review-first)
and **boons pulled forward to Phase 1.7**. Phase 2 (hub) now follows 1.7. Order: 1.5 → 1.7 → 2 → 3 → 4/5 parallel.