TURNCRAFT/src/worldgen/HANDOFF.md
jing 9c668d340c Headshell Workshop + Glow-Up phase: assembly minigame, art pass, review fixes
- Workshop (WORKSHOP_CARTRIDGE): five-stage cartridge assembly at Deck A —
  seat/square, crimp four tag-wires, torque screws, ride-the-arm counterweight
  balance, needle-drop diagnostic with per-fault audio + scope. Relay-synced
  (per-field co-op merge: held screw + carried wire survive remote state).
- Glow-Up (G1-G5): 32px atlas with per-voxel variants, selective LED bloom
  (quality-gated), screen-print decal system + party flyers, mixer/PCB worldgen
  density pass, record groove-sheen side texture.
- 10 confirmed multi-agent review fixes, incl. co-op screw-stomp soft-lock,
  ride-snap collider-identity (magnet feet / eaten record-fling), workshop SFX
  exact-match map (rca_seated hijack), beam ride colliders to the head,
  double-crimp guard, WIRING INCOMPLETE diagnosis mode, skate-abort timer,
  completedState wiring, per-tick material churn, trackingHeavy platter drag.
- Crossfader playtest fix: slew-limited sled (3.4 v/s) + ribbed grip caps with
  amber index — the sled reads as a heavy handle, not a teleporting wall.
- Demo harnesses: machinesDemo hold-key wiring, playerDemo seesaw phase
  continuity, audioDemo incomplete fault button.
- Workshop sync: exact 1.0 screw endpoint gets its own emit signature.

Verified: typecheck + vite build clean, live solo quest smoke, two-client
co-op relay smoke (simultaneous torque, no rewind, exact convergence).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 21:32:07 +10:00

8.9 KiB
Raw Blame History

Lane C — World Builder · HANDOFF

Status: complete. npm run typecheck clean, demo runs with no console errors, build is deterministic and fully validated (see below).


Glow-Up G4 — worldgen density pass (2026-07-14)

Implemented the src/worldgen/** slice of docs/briefs/GLOWUP_ART.md (task G4). The atlas (G1), bloom (G2), decals (G3) and record texture (G5) are engine/fx work and are NOT in this lane — left for their sessions.

Files touched (this pass): src/worldgen/buildBooth.ts, src/demo/worldgenDemo.ts. Nothing else.

Merge points (shared with the Workshop session): only src/worldgen/buildBooth.ts (main.ts is untouched here). My edits are confined to buildMixer, buildUnderTable (+ new pagoda/buildPcbDetail), and the deck-vent loop — all disjoint from the Workshop brief's Deck A pedestal "workbench dressing", so a merge is clean.

What it adds (additive; every pre-existing zone stays byte-identical):

  • Mixer face — dense mushroom knobs (1×1 stem + 2×2 cap, mixed heights, 6 rows × 3 cols × 4 channels, club-mixer palette) replacing the sparse specks; flush rubber panel-seam grooves sectioning the four channels; amber/green LED depth pips at each fader-alley floor and flanking; an amber glow behind each speaker-mesh vent.
  • PCB Depths — walk-under IC-chip pagodas (matte_black slabs on solder pin-legs, stacked tiers, copper pin-1 dot, green status LED), ribbon-cable walls (deck rooms), extra copper trace inlays. Kept ≥15 voxels clear of every quest pickup.
  • Deck vents — an amber pip at each shaft bottom so the drop reads as deep.

Determinism: the mixer detail is index-based (no rng()), and buildPcbDetail uses its OWN seed (WORLD_SEED ^ 0x9e3779b1), so the main PRNG stream is untouched and all other zones are identical. Two full builds are byte-identical (verified headless).

Anchors: no LAYOUT/QUEST_POS value moved. All 8 quest anchors still land on the correct block (re-verified). Build 2130 ms (« 800 ms budget). Chunk count unchanged.

One demo fix: the concept-art pass added an intentional shallow well step-ring (outside PLATTER.radius); my old "well empty" assertion checked the full well and went red. Retitled to "platter footprint clear" and scoped to PLATTER.radius — the region Lane D's platter actually needs. Green again.

Note (not mine to fix): at this checkpoint the shared tree briefly failed global tsc on the Workshop session's in-progress src/audio/testSignal.ts (a Float32Array<ArrayBuffer> generic mismatch); it cleared on their next save. My files typecheck clean in isolation.


Public API surface (import from src/worldgen)

buildBooth(world: IVoxelWorld): void          // writes the ENTIRE static diorama
buildShell, buildDeckA, buildDeckB, buildMixer, buildCableCanyon,
buildPatchBay, buildUnderTable                // individually testable sub-builders
SPAWN: Vec3                                    // deck A plinth top, faces the mixer
SPAWN_LOOK: Vec3                               // suggested look-at (mixer face centre)
WORLD_SEED = 1210                              // fixed seed → identical booth every run
QUEST_POS: QuestPositions                      // {stylus,rca,crossfader,fuse,power} for Lane D
QUEST_REACH_POINTS, hasAdjacentAir(w, p)       // reachability helpers (used by the demo)
DECK_A, DECK_B, MIXER, UNDER, PATCH, RCA_SOCKET, RCA_PLUG  // LAYOUT-derived anchors

What's done

Every zone in DESIGN §4 is built and visually confirmed in demo-worldgen.html: shell + tabletop (front lip, vent slots, crate hatch + stair), both steel-grey plinths (feet, alu border, empty platter well + chrome spindle stub, start/stop

  • 33/45 recesses, pitch canyon w/ green detent, tonearm base pedestal, strobe lamp), Deck B dust drifts + half-open glass dust cover, the mixer massif (body, speaker-mesh climb vents, 4 channel strips = knob-stem grids + empty fader alleys, crossfader tram with the dust-jam plug, two VU towers, power-switch recess, fuse chimney), the cable canyon (5 splined RCA/power cables to the patch bay + the quest cable ending short with a loose gold plug + power strip), the patch-bay wall (alu panel, 2×6 gold socket grid, the empty quest socket with red blink, two doorway rooms w/ copper busbars + blue glow), and the PCB Depths (green board floors, copper traces, capacitor pillars, solder stalagmites, sparse LEDs, the fuse box with a blown fuse, the parts bin holding the fresh-fuse + stylus pickups, and the record crate of blue/black vinyl slabs on edge with a leaning-slab ramp).

What's intentionally NOT built (yours, Lane D)

The moving parts sit in the sockets I left for them: the rotating platter/record (the empty circular well, radius PLATTER.radius+2, 6 deep, spindle stub at bottom), the tonearm arm + headshell (base pedestal + empty socket anchor), the fader caps/sleds (empty alley slots + crossfader tram), and the buttons/switches (start/stop, 33/45, power — all left as recesses). Read QUEST_POS for exact voxels.

Quest anchor convention (important for Lane D)

Each QUEST_POS voxel is a reachable interaction anchor (has an adjacent air voxel — validated). Semantics per node:

  • stylus.socket — empty voxel on the Deck A tonearm pedestal; drop the stylus here.
  • stylus.pickup — the chrome stylus block in the parts bin (blue-spotlit).
  • rca.socket — empty gold ring on the patch-bay wall face (air; push the plug in).
  • rca.plug — the loose gold plug lying on the canyon floor (top is open air).
  • crossfader.jamtop-centre of the dust plug in the tram (mine downward).
  • fuse.box — the blown fuse (rubber block, chrome caps) in the wall fuse box.
  • fuse.pickup — the fresh fuse (glass + chrome caps) in the parts bin.
  • power.switch — recess on the mixer rear top (dark until the other four are done).

Validation (headless + in-demo + cross-lane, all green)

build 14 ms (< 500) · deterministic (two runs byte-identical) · 0 OOB writes · both platter wells empty · spawn on solid steel with headroom · 8/8 quest points reachable · fuse/stylus anchors land on the intended block · record crate populated · 29/30 non-air block types · ~4.5 M non-air voxels (24.6% fill).

Cross-lane smoke test (buildBooth against Lane A's real VoxelWorld, not a mock): fillBox fast path ENABLED · 30.7 ms build · all acceptance checks still pass.

Post-review fixes (adversarial multi-agent pass, all verified)

  • [HIGH] record crate placed zero vinyl slabs (footprint collapsed to ~2 voxels because it was bounded by deckA.minX); re-anchored to the open under-ledge volume in front of Deck A — now 89 records on edge. Added a regression guard.
  • [MED] QUEST_POS.fuse.box pointed at the alu frame, one voxel off the fuse; the blown fuse now sits exactly at that anchor (frame moved behind). Guarded.
  • [MED] VU-tower LED ladders were sealed inside a closed casing; the front (DJ-facing) face is now open so the meters read.
  • [LOW] added the "resistor beams" the brief lists for the PCB rooms.
  • [LOW] the demo's "Record Crate" teleport aimed at x=12 (inside the left plywood wall) and at the crate's old location; re-aimed at the crate's real centre.
  • (A fillBox doc-comment nit was a false positive; tightened it regardless.)

Contract friction / notes for the integrator

  1. Mixer height. The brief says "tabletop → topY+40", but constants.ts (mixer.topY = 67, "27 voxels tall") and DESIGN §3 both put the mixer face plate TOP surface at topY = 67. I built to face-plate-top = 67 (body 4066) with a raised rear ridge + 12-tall VU towers for the "tallest mesa" silhouette. If the design truly wanted a 107-tall mixer, that's a one-line change in buildMixer.
  2. fillBox fast path — RESOLVED. tools.box() uses world.fillBox(x0,y0,z0,x1,y1,z1,id) (INCLUSIVE bounds) if present, else a setBlock loop. Confirmed against Lane A's shipped src/engine/VoxelWorld.ts:117: the signature and inclusive-clamp semantics match exactly, so the fast path engages at integration (verified — see cross-lane smoke test above). No action needed.
  3. Elevation model. A Y_* constant is the TOP-SURFACE PLANE: the topmost solid block of that stratum is at index Y_*-1; blocks placed on the surface sit at Y_*. Documented in anchors.ts. Platter well is carved 6 down from Y_PLINTH_TOP so Lane D's platter (top at Y_RECORD_TOP=85) rises cleanly out of it.
  4. No duplicated coords. Every spindle/mixer/patch-bay position derives from LAYOUT via anchors.ts (single source). Lane D should read positions from QUEST_POS / the exported anchors, never re-hardcode.
  5. Structural blocks are breakable:false already in blocks.ts (plywood, cables) — the booth keeps its shape; nothing here relies on that beyond honoring it.