Lanes A (engine), B (player), C (worldgen), D (machines/quest), E (audio/fx/ui) as landed, each with HANDOFF.md + update docs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
8.6 KiB
LANE C — World Builder (the Booth)
Read first: docs/DESIGN.md (especially §3–§4), docs/CONTRACTS.md,
everything in src/core/.
You own: src/worldgen/**, src/demo/worldgenDemo.ts, demo-worldgen.html.
You may not touch anything else.
Mission
Author the entire diorama in voxels: buildBooth(world: IVoxelWorld): void
writes every static block of the DJ booth — plywood shell, tabletop, two
turntable plinths, the mixer massif, cable canyon, patch-bay wall, and the
PCB Depths under the table. This is the level-design lane: you are building a
place, and the inspiration photo (plywood console, silver 1200s, black
4-channel mixer, blue records, wall patch bay, visible hinges) is your mood
board. Deterministic output: same world every run (seedable PRNG, fixed seed).
What you do NOT build: the platter, record disc, tonearm, fader caps/sleds,
and buttons — those are moving machines (Lane D). You build the sockets they
sit in: the platter well (a recessed circular pit around each spindle), the
tonearm base mount, empty fader slots, button recesses. Use LAYOUT from
constants for every position Lane D also needs.
Provides
buildBooth(world: IVoxelWorld): void— main entry, composed of exported sub-builders so they're individually testable:buildShell,buildDeck(A|B),buildMixer,buildCableCanyon,buildPatchBay,buildUnderTable.SPAWN: Vec3— export the player spawn (on Deck A's plinth top, near the platter well, facing the mixer).
Consumes
IVoxelWorld interface only. Prefer fillBox when available (Lane A adds it;
code against the interface plus an optional duck-typed
(world as any).fillBox?.(…) ?? loop fallback so your demo's simple mock
still works).
Tasks & structure specs
Work top-down; keep each builder a pure function of (world, LAYOUT, prng).
A tiny toolkit first (src/worldgen/tools.ts): box, hollowBox,
cylinderY (disc/annulus fill), line3 (thick voxel line), spline
(Catmull-Rom sampled → spheres stamped along it, for cables), mulberry32
PRNG.
C1. Shell & tabletop
- Plywood floor at world bottom, walls (rimThickness) up to
Y_BOOTH_WALL_TOP, back wall atbackWallZ.ply_edgestriping on every exposed wall top/cut edge (that laminate look). Two chromescrew+brushed_aluhinge plates on the back wall like the photo. - Tabletop slab at
Y_TABLETOPspanning inside the rim, with: a rectangular vent slot behind each deck (the way down to PCB Depths), and a 6×6 open hatch near the front-left with a plywood step-stair down into the crate.
C2. Decks A & B (steel-grey mesas)
- Plinth: filled
steel_greybox from tabletop toY_PLINTH_TOPover the LAYOUT footprint, 2-voxelrubberfeet at corners, abrushed_alutop face border. Rounded-ish corners (chamfer 2–3 voxels). - Platter well: recessed circular pit centered on the spindle, radius
PLATTER.radius + 2, depth 6 below plinth top — leave it EMPTY (Lane D's rotating platter fills it). Chrome 1-voxel spindle stub at center bottom. - Deck-top furniture (all voxel, non-moving): start/stop button recess
(4×4×1 pit, front-left), two small round 33/45 button recesses beside it,
pitch fader canyon — an 18-long, 3-wide, 4-deep slot on the right side
with
led_greencenter-detent dot at its midpoint, tonearm base mount — a 10×10brushed_aluraised pedestal at back-right with a 3-voxel chrome post stub, and astrobe_dot+led_redstrobe lamp pillar at front-left corner (the classic Technics pop-up light). - Deck B only: drifts of
dustblocks banked on top and around; its glass dust cover half-open — aglassslab canopy over the rear half at y ≈Y_PLINTH_TOP+18, hinged at the back wall side, forming the crystal cave (2-voxel-thick, walkable on top).
C3. Mixer Massif
matte_blackbody over LAYOUT.mixer footprint, tabletop →topY+40 (i.e. face plate at y=67... useLAYOUT.mixer.topY);speaker_meshside-vent ladders (climbable = 1-voxel ledges every 2),brushed_aluface plate border.- Face plate furniture: 4 channel strips, each with a 3×3 grid of knob stems
(1-voxel
knob_blackstubs — caps are Lane D machines) and a fader alley: 14×3 slot, 5 deep. Front edge: the crossfader tram — a 40-long, 4-wide, 6-deep slot running left–right; fill its center with a plug of 12dustblocks (the quest jam). Back edge: two VU towers — 12-tall columns, alternatingled_green×8 /led_amber×3 /led_red×1, plus amatte_blackcasing open on the front face. - Rear face: master power switch recess (Lane D machine) and a fuse-door slot leading down a chimney into the PCB Depths fuse room.
C4. Cable Canyon (z 140–200)
- 4–6 cables, each a 2×2
cable_blacktube along a drooping Catmull-Rom spline from gear rear panels (RCA heights on deck/mixer backs) over the canyon floor to the patch-bay wall; two of them sheathedcable_red/cable_whitenear the ends withrca_goldplug heads (3×3×4). - One cable is THE quest cable: it ends 8 voxels short of the patch bay, its gold plug resting on the canyon floor, aligned with an empty socket.
- Canyon floor: tabletop plywood with dust drifts, a few vinyl slab
off-cuts leaning on walls (walkable ramps), power-strip block (matte_black
box with
led_amberpilot light) feeding cables.
C5. Patch Bay (back wall)
- Over
LAYOUT.patchBay: abrushed_alupanel proud of the wall by 2, with a 2×6 grid of roundrca_goldsockets (3-voxel-diameter rings, 2 deep); most plugged with cable-colored plugs, one conspicuously EMPTY with anled_redblink block above it (the quest socket — Lane D puts the interactive socket machine here; you build the hole). - Two sockets are real doorways: 3-deep tunnels opening into a small room
inside the wall with copper busbars and a
led_blueglow — a secret.
C6. PCB Depths & the crate (under-table, y 0–40)
- Under each deck and the mixer: rooms with
pcb_greenfloors,coppertrace paths inlaid (1-wide runs connecting solder-blob clusters), solder stalagmites, capacitor pillars (cylinders:matte_blackbody,brushed_alutop with cross-scored cap), resistor beams bridging gaps. LED sprinkles (led_green/amber, sparse, per DESIGN's "dim until repaired" — just place them; Lane E handles dimming). - Fuse room under the mixer: fuse box on the wall — a
brushed_aluframe holding one blackened fuse (userubberblock flanked bychromecaps to read as "blown"), and a parts bin nearby: an open plywood tray holding a fresh fuse (glassblock withchromecaps), plus the stylus block (singlechromeblock on a felt pad, spotlit by oneled_blue) — the two quest pickups. Coordinate: place pickups exactly at positions exported asQUEST_POS(export a const with stylus/fuse/plug/socket world coords so Lane D reads positions from you at integration — put it insrc/worldgen/questPositions.ts, derived from LAYOUT, no magic numbers elsewhere). - Record crate (front-left, under the hatch): plywood cavern holding 8–10
giant vinyl slabs (2-thick discs on edge, alternating
vinyl_black/vinyl_bluewithlabel_creamcenters), gaps walkable, one leaning slab as a ramp back up.
C7. Set dressing pass
- The booth should feel used: dust in corners, one screw block half-out,
scattered
label_creamsticker blocks on the shell, cable stubs. Restrained — this is a tidy booth, per the photo.
Demo (demo-worldgen.html)
Build the full booth into a simple array-backed mock world, render with a
naive InstancedMesh-per-block-id visualizer (flat colors from blocks.ts
tints — fine at this scale), OrbitControls, per-zone teleport buttons, and a
stats line: total non-air voxels, per-id counts, build time (< 500 ms).
Validation asserts (console.error on fail): platter wells empty, spawn stands
on solid, quest positions non-air-adjacent-reachable (has an adjacent air
voxel), world edges sealed (no non-air outside bounds… i.e. no writes OOB).
Acceptance
buildBoothwrites only throughIVoxelWorld, deterministic across runs- Every zone from DESIGN §4 exists and is visually recognizable in the demo
- All LAYOUT-adjacent geometry derives from
constants.ts(grep-proof: no duplicated literal for spindle/mixer coords) - Platter wells, fader slots, button recesses left empty for Lane D
questPositions.tsexports all quest coords; validation asserts pass- Build < 500 ms;
npm run typecheckclean;HANDOFF.mdwritten
Out of scope
Anything that moves (Lane D), real meshing (Lane A), lighting/audio (E), player (B).