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>
6.4 KiB
TURNCRAFT — Lane Contracts
Every lane reads this file and 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
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.- Own your directory, never write outside it. Ownership map in §3.
- Cross-lane imports:
src/core/types only. Never import a concrete class from another lane's directory. Depend onIVoxelWorld,IMachine,IPlayerView,KinematicCollider,bus— not on implementations. In your standalone demo, mock what you need (§6). - Communicate via the event bus (
src/core/events.ts) for anything fire-and-forget; via the core interfaces for anything synchronous. - No new runtime dependencies.
threeonly. Dev deps:vite,typescript,@types/three. Textures are painted procedurally on canvas; audio is synthesized with WebAudio. No binary assets, no CDN loads. - TypeScript strict mode must pass:
npm run typecheckclean at handoff. - 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
KinematicColliders withvelocityAt. Lane B, when the player's ground contact is a kinematic collider, adds that surface velocity to the player each tick and reports it viaIPlayerView.groundedOn. Cylinder colliders rotate about +Y only. - Break/place: Lane D raycasts (voxel DDA for blocks + analytic tests for
machine colliders), calls
world.setBlock, emitsblock: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 > 0blocks 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-<lane>.html at repo root +
src/demo/<lane>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-<lane>.html.
7. Definition of Done (every lane)
npm run typecheckpasses 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.mdin 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.