TURNCRAFT/docs/INTEGRATION_NOTES.md
jing 49cab1f69b Concept-art pass: terraced records, spindle towers, warm lit room
- Records are stepped groove amphitheaters (3 vinyl tiers + 4-tier spindle
  tower, stacked rotating cylinder colliders) - you climb a spinning
  staircase. Tonearm clearances raised to match.
- Player physics: cylinder contacts resolve by minimum penetration (fixes a
  latent radial-slingshot bug), collider lips auto-step like voxel ledges,
  rim-standing radius grace.
- Well seam ring raised (was a 7-deep inescapable trench, now a 1-step
  Technics seam).
- Plywood ceiling with amber lamp panels + warm point lights; brighter warm
  lighting rig; colorful mixer knob caps; rim-crumble debris.
- window.TURNCRAFT_CINE camera override for screenshots/trailers.

Verified live: rim->plateau spiral climb at 33rpm, tower climb 83->87,
seam escape, quest/win chain, 121 fps.

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

6.6 KiB
Raw Blame History

TURNCRAFT — Integration Notes (what the integrator changed and why)

Integration performed per INTEGRATION.md after all five lanes landed. Every cross-lane glue decision is listed here.

Contract changes (core is integrator-owned)

  1. Y_RECORD_TOP: 85 → 81 (src/core/constants.ts). At 85 the vinyl surface was a 5-voxel cliff above the plinth (jump height ≈ 1.3 — the record was unreachable), and Lane D's platter stack floated on the plinth top, ignoring the 6-deep well Lane C carved. Now the record sits 1 proud of the plinth (auto-steppable, like a real 1200) and the platter body fills the well. Everything downstream (tonearm heights, audio panner positions, FX rim sparkle, D's quest defaults) derives from the constant and moved automatically. Added PLATTER.wellDepth = 6 alongside.
  2. Lane B's requested velocityAt out-param overload was NOT taken: Lane D already returns a reused per-collider scratch array (no allocation), which satisfies the hot-path concern with no contract change.

Cross-lane glue (in src/main.ts)

  • QuestPositions adapter: Lane C's nested QUEST_POS is mapped to Lane D's flat QuestPositions; the crossfader slot span is derived from C's MIXER anchors (centerX ± halfLen, faceTopYdepth+1 … faceTopY1, z 1…+2) so D's slide gate covers exactly the tram volume C carved.
  • machines.attachPlayer(player) called after the player is built (Lane D's added step "6.5") — enables walk-push faders and walk-press plates.
  • Hotbar: D's Hotbar structurally satisfies E's HotbarModel; passed directly as getHotbar, with hotbar.onChange(() => hud.refreshHotbar()).
  • Input wiring: LMB hold = mine, RMB = place, E = use, 19/wheel = hotbar, all gated on pointer lock (matching Lane B's own key gating). Start splash click → audio.init() + pointer lock; resume click → re-lock only.
  • Fixed-step loop: machines → player → interaction → fx at FIXED_DT, with MAX_SUBSTEPS catch-up and an accumulator clamp so a backgrounded tab doesn't spiral; audio.updateListener + chunks.update() + render per frame.
  • Debug handle window.TURNCRAFT (world/player/machines/quest/hotbar/audio/ fx/hud/chunks/interaction) — used by the smoke tests, harmless in play.

Lane-file adjustments (documented per CONTRACTS "integrator resolves")

  • src/machines/platter.ts — platter body now spans the well floor to 1 below the vinyl (visuals) and its collider spans well floor → Y_RECORD_TOP so the spinning body also pushes the player out of the well's 2-voxel gap ring (which is now a real, intentional fall-in hazard).
  • src/fx/layout.ts — VU overlay sprites aligned to Lane C's physical LED towers (x = mixer minX+18 / maxX18, 12 segments, in front of the open casing face) per Lane E's friction note #5.
  • src/interact/interaction.ts — per-material break times (soft 0.12 s, glass 0.25 s, default 0.4 s). The crossfader jam is ~240 dust blocks; at a flat 0.4 s it was a 96-second chore.
  • docs/DESIGN.md — elevation table updated for the Y_RECORD_TOP change.

Deviations accepted as-is

  • 45 speed available pre-win (D's reading; DESIGN's "45 unlocked at win" treated as flavor). Platter.unlocked45 exists if we ever gate it.
  • fader_cross does not drive audio (E maps pitch + channel digits only); crossfader is quest/physical-only in v1.
  • Both deck pitch faders drive the one global mix pitch (E has a single transport; fine until per-deck stems exist).
  • Tonearm pedestal (C's voxels) vs D's arm pivot are ~11 voxels apart — cosmetic misalignment, queued for polish, gameplay unaffected.

Verified at integration (live browser, real code paths)

  • Boot: booth builds + meshes in ~670 ms dev / 46 modules build in 0.5 s; fillBox fast path ENABLED; 120 fps; zero console errors all session.
  • Ride: standing on the record at 33 → clean circle, r drift 20.00→20.02 over 4 s, y = 81 exactly, spin-up ramp visible; at 45 near the rim → 10.7 v/s (= ω×r), jump lands back on the moving disc.
  • Mining: real DDA aim → dust breaks into hotbar; crossfader stays gated while dust remains.
  • Quest chain: stems stack 0→5 per signal:repair, power switch arms exactly after the 4th repair, real button press → game:win once, both platters auto-start, HUD banner + tracker 5/5, AudioContext running.
  • The platter-well gap ring behaves as a trench (collider pushes, no clipping).

Concept-art pass (post-integration, from John's Gemini mockup)

  • Terraced records: the flat disc became a stepped "groove amphitheater" — 3 vinyl tiers rising 1 voxel each to the label plateau, then a 4-tier stepped spindle tower with an amber lamp (all in TIERS, platter.ts). Each tier is its own stacked kinematic cylinder; you walk/spiral up a SPINNING staircase. Tonearm raised (HEAD_Y +2.6) and R_INNER pulled to 16 so the needle clears the terraces and never hits the tower.
  • Player physics upgrades this required (PlayerController.ts): cylinder contacts now resolve by minimum penetration (up vs radial), which fixes a latent slingshot bug (feet dipping below a disc's top while deep inside its footprint used to teleport the player radially to the rim); 1-voxel collider lips auto-step like voxel ledges; cylinder tops have a half-foot radius grace so you can stand on rims.
  • Well seam: the platter well's outer ring was a 7-deep inescapable trench; floor raised to 1 below the plinth top (worldgen) — now a Technics seam you step out of.
  • Room feel: plywood ceiling with six recessed amber lamp panels (worldgen) + matching warm PointLights (main.ts), warmer/brighter hemi/ambient/fog (renderer.ts).
  • Mixer color pop: knob caps get a deterministic palette scatter (red/ white/gold/LED) in buildMixer. Rim crumble: vinyl chips/dust/screws scattered around the well mouths in buildDeck.
  • Dev nicety: window.TURNCRAFT_CINE = { pos, look } detaches the camera for screenshots/trailers; window.TURNCRAFT exposes camera/render too.
  • Verified: walk-in from the rim spirals up to the label plateau at 33 rpm (fighting the spin is intentional physics — stop the deck for an easy climb); tower fully climbable (83→87 by walking); seam escape 1-step; 121 fps with ceiling + lights; quest/win chain re-verified end-to-end.

Not yet done (deploy phase)

Run /ship-check before exposing anything publicly; deploy per deploy-map conventions (static dist/ — 156 KB gzipped). Cloudflare/host wiring is a separate step, to be done with the token holder present.