# TURNCRAFT — Lane Contracts **Every lane reads this file and [DESIGN.md](./DESIGN.md) before writing code.** This document exists so five agents can build simultaneously without touching each other's files or breaking each other's assumptions. ## 1. The Law 1. **`src/core/` is read-only for all lanes.** It defines the shared types, block registry, constants, and event bus. If you believe a contract file has a bug or a missing field, STOP and report it in your summary instead of editing it — the integrator resolves contract changes. 2. **Own your directory, never write outside it.** Ownership map in §3. 3. **Cross-lane imports: `src/core/` types only.** Never import a concrete class from another lane's directory. Depend on `IVoxelWorld`, `IMachine`, `IPlayerView`, `KinematicCollider`, `bus` — not on implementations. In your standalone demo, mock what you need (§6). 4. **Communicate via the event bus** (`src/core/events.ts`) for anything fire-and-forget; via the core interfaces for anything synchronous. 5. **No new runtime dependencies.** `three` only. Dev deps: `vite`, `typescript`, `@types/three`. Textures are painted procedurally on canvas; audio is synthesized with WebAudio. No binary assets, no CDN loads. 6. **TypeScript strict mode must pass**: `npm run typecheck` clean at handoff. 7. Coordinate system: Three.js right-handed, **Y-up**, 1 voxel = 1 world unit. Voxel (x,y,z) occupies world space [x,x+1)×[y,y+1)×[z,z+1). World bounds and all gear placement come from `src/core/constants.ts` (`LAYOUT`) — never hardcode a magic position. ## 2. Contract Files (already written — read them) | file | contents | |---|---| | `src/core/constants.ts` | world size, chunk size, elevations, `LAYOUT` gear placement, player physics numbers, platter RPMs, quest node list | | `src/core/blocks.ts` | the full block registry: ids 0–30, solidity, transparency, emissive, tint colors, texture `pattern` names, sounds | | `src/core/types.ts` | `IVoxelWorld`, `KinematicCollider`, `IMachine`, `RayHit`, `IPlayerView`, `Vec3` | | `src/core/events.ts` | the typed global `bus` and every event name/payload | ## 3. Ownership Map | lane | owns (create/edit freely) | delivers | |---|---|---| | **A — Voxel Engine** | `src/engine/**`, `src/demo/engineDemo.ts`, `demo-engine.html` | `VoxelWorld` (implements `IVoxelWorld`), chunk mesher, texture atlas, renderer/scene/lighting setup, chunk-remesh-on-dirty | | **B — Player & Physics** | `src/player/**`, `src/demo/playerDemo.ts`, `demo-player.html` | `PlayerController` (implements `IPlayerView`): input, AABB-vs-voxel collision, kinematic platform riding, fly mode | | **C — World Builder** | `src/worldgen/**`, `src/demo/worldgenDemo.ts`, `demo-worldgen.html` | `buildBooth(world: IVoxelWorld): void` — writes the entire diorama | | **D — Machines & Interaction** | `src/machines/**`, `src/interact/**`, `src/demo/machinesDemo.ts`, `demo-machines.html` | platter/tonearm/fader/button machines (implement `IMachine`), raycaster, break/place, hotbar model, quest state machine | | **E — Audio, FX & UI** | `src/audio/**`, `src/fx/**`, `src/ui/**`, `src/demo/audioDemo.ts`, `demo-audio.html` | synthesized stem mix, positional audio, beat events, LED pulse/VU systems, particles, HUD/hotbar UI, win screen | | **integration** (after lanes land) | `src/main.ts` | final wiring per `docs/INTEGRATION.md` | Shared read-only: `src/core/**`, `index.html`, configs, `docs/**`. ## 4. Key Cross-Lane Behaviors (who does what) - **Dirty chunks**: `VoxelWorld.setBlock` (Lane A) marks the containing chunk (and neighbors when the block borders them) dirty; the engine remeshes dirty chunks, budgeted per frame. Nobody else touches meshing. - **Riding platforms**: Lane D machines expose `KinematicCollider`s with `velocityAt`. Lane B, when the player's ground contact is a kinematic collider, adds that surface velocity to the player each tick and reports it via `IPlayerView.groundedOn`. Cylinder colliders rotate about +Y only. - **Break/place**: Lane D raycasts (voxel DDA for blocks + analytic tests for machine colliders), calls `world.setBlock`, emits `block:break`/`block:place`. Lane A reacts by remeshing; Lane E reacts with sound/particles. - **Quest**: Lane D owns quest state and emits `signal:repair` / `game:win`. Lane E owns everything audible/visible that reacts to those events. - **Emissive blocks**: Lane A renders `emissive > 0` blocks with an emissive material channel. Lane E may pulse a global uniform (via a small exported hook Lane A provides: `setEmissiveBoost(v: number)` on the renderer) — Lane A must expose that one function; Lane E must not reach into A's materials. ## 5. Fixed Timestep The game loop runs physics/machines at `FIXED_DT` (1/60) with up to `MAX_SUBSTEPS` catch-up steps, render decoupled. Every lane's `update(dt)` must be stable if called with dt = FIXED_DT repeatedly. No lane creates its own `requestAnimationFrame` loop except inside its own demo. ## 6. Demo Harness Rules (how you verify alone) Each lane ships a standalone Vite page (`demo-.html` at repo root + `src/demo/Demo.ts`) that runs with **zero code from other lanes**. Mock the interfaces you consume — e.g. Lane B's demo includes a ~30-line `MockWorld implements IVoxelWorld` (flat floor + some steps) and one mock spinning-cylinder `KinematicCollider`; Lane C's demo may include a naive brute-force mesher (one box per voxel face is fine at demo scale, or `InstancedMesh`) purely to eyeball the build. Keep mocks inside your demo file(s), clearly marked `// DEMO MOCK — not for integration`. `npm run dev` then open `http://localhost:5173/demo-.html`. ## 7. Definition of Done (every lane) - [ ] `npm run typecheck` passes with your code included - [ ] Your demo page runs, and its README-style header comment says exactly what to look at / press to verify each acceptance criterion - [ ] No writes outside your owned paths; no imports of other lanes' concrete code - [ ] No console errors in a 2-minute demo session - [ ] A `HANDOFF.md` in your owned directory: what's done, what's stubbed, any contract friction you hit, and your public API surface in 10 lines ## 8. Style Match the existing core files: plain TypeScript, no classes-for-the-sake-of-it, comments only where a constraint isn't obvious from code. Keep per-frame allocations near zero in hot paths (meshing, physics): reuse arrays/vectors.