TURNCRAFT/docs/INTEGRATION_NOTES.md
jing f88be60e60 Integrate all five lanes: wire main.ts, fix record height contract, verify live
- src/main.ts: full wiring per docs/INTEGRATION.md (renderer/world/booth/
  chunks/machines/player/interaction/audio/fx/hud + fixed-step loop),
  QuestPositions adapter (Lane C nested -> Lane D flat), attachPlayer step,
  pointer-lock-gated input, debug handle for smoke tests.
- core/constants: Y_RECORD_TOP 85 -> 81 (record was an unclimbable 5-voxel
  cliff; now 1 proud of the plinth, auto-steppable) + PLATTER.wellDepth.
- machines/platter: body + collider now fill Lane C's well down to y=74.
- fx/layout: VU sprites aligned to Lane C's physical LED towers.
- interact: per-material break times (dust 0.12s) so the crossfader jam
  isn't a 96-second mine.
- docs/INTEGRATION_NOTES.md: every glue decision + live verification results
  (120 fps, ride/mine/quest/win all green, zero console errors).

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

4.7 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).

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.