Docs: session log + roadmap — P1 Group B (5-Spark systems + gym tooling)

Session 2026-07-15: the 5-Spark systems, content, and enemy-test-gym CODE done
(497 EditMode green); the fresh Gym.unity scene assembly + the L3 feel gate remain.
Roadmap Phase-1 status updated. Reusable gotchas banked (reel via re-aimed
KnockbackState; GymTag bypasses the meta spawn guard; manage_scriptable_object
target-object quirk).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-15 00:01:23 -07:00
parent b92cc7196b
commit 98c8da3fd7
2 changed files with 95 additions and 0 deletions
@@ -54,6 +54,13 @@ The primary verb first. No world, no lanterns — a gym.
**Gate:** the gym is fun with two players for 15 minutes on kit alone; relevancy-light verified server-side (gamma test: nothing to see). **Gate:** the gym is fun with two players for 15 minutes on kit alone; relevancy-light verified server-side (gamma test: nothing to see).
> **Phase 1 status (2026-07-15):** ✅ Group A (4-socket kit, [[2026-07-14_Lantern_Phase1_GroupA_Combat_Sockets]]) ·
> ✅ Group B **systems + content + gym CODE** — the 5 Sparks (decoy / hook-reel / vortex / blink / light-zone),
> Aoe/Hitscan dispatch, `ReelSystem` / `ZonePulseSystem` / decoy aggro, 3 effect ghosts + 5 Spark defs, on-demand
> enemy-spawn tooling + `GymTag` clean-spawn (497 EditMode green). ◻ the fresh **`Gym.unity` scene assembly** + the
> **L3 feel gate** (operator's eyes; also closes the A1 in-game animated-pipeline confirmation) remain. See
> [[2026-07-15_Lantern_Phase1_GroupB_5Sparks_Systems]].
## Phase 2 — The dark & the pocket-graph ## Phase 2 — The dark & the pocket-graph
1. **Twilight shelf as a pocket-graph:** 35 generated dark pockets per descent on a graph; seed-fresh re-knot per descent; pocket transitions. 1. **Twilight shelf as a pocket-graph:** 35 generated dark pockets per descent on a graph; seed-fresh re-knot per descent; pocket transitions.
@@ -0,0 +1,88 @@
---
date: 2026-07-15
type: session
tags: [session, lantern, phase-1, combat, sparks, netcode, gym, implementation]
permalink: gamevault/07-sessions/2026/2026-07-15-lantern-phase1-groupb-5sparks-systems
---
# Session 2026-07-15 — LANTERN Phase 1 Group B: the 5-Spark systems + gym tooling (code done; scene pending)
> Continues [[Phase1_Combat_Gym_Build_Spec]] (adopted [[DR-050_Lantern_Phase1_Combat_Design]]) — the "remaining
> Group B" from [[2026-07-14_Lantern_Phase1_GroupA_Combat_Sockets]]. Operator chose the **full 5-Spark feel**
> (steps 3+4) and, folding in the roadmap's **Enemy-test GYM** direction, a **dedicated fresh `Gym.unity`**. All
> the systems + content + gym CODE landed and are verified headless (L1 clean, **497/497 EditMode green**). The
> physical gym SCENE assembly + the L3 feel gate remain — the operator-editor part.
## Done (L1 clean throughout; L2 497/497, +3 new)
**Part A — Spark systems (Build Spec §5/§6/§9/§10, step 3 + step-4 new-code):**
- **Blob + flags:** `AbilityDefBlob += byte EffectFlags` (bit = Reel) `+ int DurationTicks`; mirrored on
`AbilityDefinition` SO + the baker; `ProjectileEffectFlag.Reel = 2`.
- **Aoe/Hitscan dispatch** in `AbilityFireSystem` — replaced the `archetype != Projectile → continue` fall-through
with an **Aoe** branch (both-worlds cooldown advance; `ecb.Instantiate` **only inside `if (isServer)`** — the
client-never-instantiates invariant; picks `ZoneEffect` vs `DecoyTag` by PREFAB membership; seeds runtime
radius/damage/lifetime from `EffectiveSocketStats` + `adef.DurationTicks`; preserves baked scale via
`bakedLt.WithPosition`) + a minimal **Hitscan** branch (both-worlds cooldown + server-only inline beam damage;
no Phase-1 Spark uses it — present for spec-completeness). HookPull's `EffectFlags` OR'd into `projFlags`.
- **Reel = homing via re-aimed knockback (the RS-6 "new code, not the pull flag"):** `ReelState` (server-only,
baked inert on Husks via `EnemyAuthoring`) + `ReelSystem` (server, `[UpdateAfter(PredictedSimulationSystemGroup)]`
`[UpdateBefore(EnemyAISystem)]`) re-aims each reeled Husk's `KnockbackState` toward the CASTER's LIVE position
every tick — **EnemyAISystem stays the sole Position writer** (it drives the re-aimed knockback; ReelSystem only
writes Dir/Speed/UntilTick). `ProjectileDamageSystem` stamps `ReelState{CasterNetworkId,…}` on a Reel-flag hit
(skips bosses). Releases on proximity(1.6u)/leash(90t)/caster-gone.
- **`ZonePulseSystem`** — mirrors `GeyserEruptSystem` (INVERTED invalid-tick guard, `= now + period` reschedule)
but ENEMY-ONLY (no friendly fire), stamps `DamageEvent.SourceNetworkId = casterId`, NO region gate; Vortex flag
adds a gentle inward pull each tick; `DestroyEntity` on expiry. **L2: 3 tests** (enemy-only + caster-attribution,
born-0 lazy-stamp no-storm, expiry despawn).
- **Decoy aggro** — `DecoyTag` ghost gathered into `EnemyAISystem`'s Grunt target set (preferred within 30u,
overrides player/structure/core; region-agnostic); soaks contact damage; `DecoySystem` despawns on Health≤0 or
lifetime. (Grunt-pass only — Charger/Spitter decoy-aggro a noted scope edge; the gym's Drowner = Strider = Grunt.)
**Part B — content:**
- 3 interpolated ownerless-ghost prefabs via the duplicate-`ResourceNode` recipe: `EffectVortex` (Vortex flag),
`EffectLightZone`, `DecoyWisp` — authoring swapped to `ZoneAuthoring`/`DecoyAuthoring` (bake `ZoneEffect`/
`DecoyTag` inert; decoy also Health+DamageEvent, no HitRadius so shots pass through). No colliders (won't block).
- 5 `AbilityDefinition` SOs (dup of `Ability_Primary`): DecoyWisp(Aoe→decoy prefab), HookPull(Projectile+Reel→
shared `Projectile`), Vortex(Aoe→EffectVortex), Blink(Movement), LightZone(Aoe→EffectLightZone).
**Part C — gym CODE (scene pending):**
- `GymTag` + `GymEnemyRoster{byte Kind; Entity Prefab}` + `GymRosterAuthoring` (bakes both; Drowner=0/Grindylow=1).
- `DebugOp.SpawnEnemy = 14` + client `DebugCommandSendSystem.SpawnEnemy(kind,count)` + the server receive case
(spawns the roster prefab of Kind ArgA near the sender, RegionTag Base).
- `GoInGameServerSystem` **GymTag branch**: `GymTag` present ⇒ bypass the meta-catalog spawn guard (a fresh gym
has no CycleDirector) + seed the default Spark loadout `[HookPull, Blink, DecoyWisp, Vortex]` on keys 14
(LightZone off-bar). Class seeds still apply (harmless — AbilityFireSystem reads sockets).
## Wire/bake churn
Blob re-bake (config); Husk/Drowner re-bake (server-only `ReelState`, no ghost-hash change); **3 additive
interpolated-ghost prefabs**. **No new `[GhostField]` / no command-hash change** (player ghost + command collection
untouched — the SpawnEnemy op reuses the existing scalar `DebugCommandRequest` wire type).
## Reusable gotchas (candidates for CLAUDE.md / archive)
- **Reel/vortex = re-aim `KnockbackState` from a sibling system, don't add a Position-writer.** A server system in
the plain group `[UpdateBefore(EnemyAISystem)]` writing only `KnockbackState.Dir/Speed/UntilTick` (one tick,
re-stamped) gets homing/pull for free through the existing knockback mover — EnemyAISystem stays sole
Position-writer. Clean way to add forced-movement without fighting the mover.
- **A fresh (CycleDirector-less) subscene bypasses `GoInGameServerSystem`'s meta-catalog spawn guard via a baked
`GymTag`** — else the born-correct-meta guard blocks player spawn forever.
- **`manage_scriptable_object modify` needs `target` as an OBJECT** `{guid|path}` (a bare string errors); enum +
ObjectReference patches work (`{"path":"Field","value":{"guid":"…"}}`).
## Open / next
- **C1 — the fresh `Gym.unity` + `Subscenes/Gym.unity`** (operator chose fresh-minimal-clean over duplicate). Needs
the singleton set or systems silently stall: ground + Environment collider · `PlayerSpawnerAuthoring` (Player
prefab + spawn point) · `AbilityDatabaseAuthoring` (all abilities incl. the 5 Sparks + characters) ·
`NetCodePhysicsConfig` · `WorldCollisionConfig` · `GymRosterAuthoring` (GymTag + Drowner/Grindylow refs). Host
`Gym.unity` = SubScene GameObject + camera/light; add to Build Settings; editor-instant boot (open + Play).
**Fiddly + not fully verifiable headless — do with Unity focused.**
- **D — L3 feel gate (operator's eyes):** in the gym, spawn Drowner/Grindylow on demand (`DebugCommandSendSystem.
SpawnEnemy` or execute_code) → confirm they **animate in-game** (closes the open A1 pipeline confirmation) → fire
all 4 sockets (no flicker) → reel yanks a Drowner to the caster → zone ticks enemy-only damage → decoy pulls
aggro → dash+blink precedence → **server == client** on the 3 new ghosts. Then the **adversarial post-impl diff
review** (netcode/reuse/determinism) before final sign-off (standing practice; not run yet — heavy Workflow).
- Reel-convergence / decoy-aggro / client-never-instantiate / cross-socket dispatch: structurally sound + self-
reviewed, but their behavioural confirmation is L3 (need the full netcode world), not L2.
## Related
- [[Phase1_Combat_Gym_Build_Spec]] · [[DR-050_Lantern_Phase1_Combat_Design]] · [[Roadmap_Lantern_Slice]] (A1 gym
note) · [[2026-07-14_Lantern_Phase1_GroupA_Combat_Sockets]] · [[DR-023_Enemy_Animation_MonsterMash]]