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>
161 lines
8.6 KiB
Markdown
161 lines
8.6 KiB
Markdown
# 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 at `backWallZ`. `ply_edge` striping on every
|
||
exposed wall top/cut edge (that laminate look). Two chrome `screw`+
|
||
`brushed_alu` hinge plates on the back wall like the photo.
|
||
- Tabletop slab at `Y_TABLETOP` spanning 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_grey` box from tabletop to `Y_PLINTH_TOP` over the
|
||
LAYOUT footprint, 2-voxel `rubber` feet at corners, a `brushed_alu` top
|
||
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_green` center-detent dot at its midpoint, tonearm base mount —
|
||
a 10×10 `brushed_alu` raised pedestal at back-right with a 3-voxel chrome
|
||
post stub, and a `strobe_dot` + `led_red` strobe lamp pillar at front-left
|
||
corner (the classic Technics pop-up light).
|
||
- Deck B only: drifts of `dust` blocks banked on top and around; its glass
|
||
dust cover half-open — a `glass` slab 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_black` body over LAYOUT.mixer footprint, tabletop → `topY`+40
|
||
(i.e. face plate at y=67... use `LAYOUT.mixer.topY`); `speaker_mesh`
|
||
side-vent ladders (climbable = 1-voxel ledges every 2), `brushed_alu`
|
||
face plate border.
|
||
- Face plate furniture: 4 channel strips, each with a 3×3 grid of knob *stems*
|
||
(1-voxel `knob_black` stubs — 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 12 `dust` blocks (the quest jam). Back edge: two **VU towers** —
|
||
12-tall columns, alternating `led_green`×8 / `led_amber`×3 / `led_red`×1,
|
||
plus a `matte_black` casing 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_black` tube 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 sheathed `cable_red` /
|
||
`cable_white` near the ends with `rca_gold` plug 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_amber` pilot light) feeding cables.
|
||
|
||
### C5. Patch Bay (back wall)
|
||
- Over `LAYOUT.patchBay`: a `brushed_alu` panel proud of the wall by 2, with
|
||
a 2×6 grid of round `rca_gold` sockets (3-voxel-diameter rings, 2 deep);
|
||
most plugged with cable-colored plugs, one conspicuously EMPTY with an
|
||
`led_red` blink 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_blue` glow — a secret.
|
||
|
||
### C6. PCB Depths & the crate (under-table, y 0–40)
|
||
- Under each deck and the mixer: rooms with `pcb_green` floors, `copper`
|
||
trace paths inlaid (1-wide runs connecting solder-blob clusters), solder
|
||
stalagmites, capacitor pillars (cylinders: `matte_black` body, `brushed_alu`
|
||
top 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_alu` frame
|
||
holding one blackened fuse (use `rubber` block flanked by `chrome` caps to
|
||
read as "blown"), and a parts bin nearby: an open plywood tray holding a
|
||
fresh fuse (`glass` block with `chrome` caps), plus the **stylus block**
|
||
(single `chrome` block on a felt pad, spotlit by one `led_blue`) — the two
|
||
quest pickups. Coordinate: place pickups exactly at positions exported as
|
||
`QUEST_POS` (export a const with stylus/fuse/plug/socket world coords so
|
||
Lane D reads positions from you at integration — put it in
|
||
`src/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_blue` with `label_cream` centers), 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_cream` sticker 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
|
||
|
||
- [ ] `buildBooth` writes only through `IVoxelWorld`, 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.ts` exports all quest coords; validation asserts pass
|
||
- [ ] Build < 500 ms; `npm run typecheck` clean; `HANDOFF.md` written
|
||
|
||
## Out of scope
|
||
|
||
Anything that moves (Lane D), real meshing (Lane A), lighting/audio (E),
|
||
player (B).
|