TURNCRAFT/docs/CONTRACTS.md
jing 5a39e3a947 TURNCRAFT: contracts, docs, and all five lane deliverables (pre-integration)
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>
2026-07-13 20:50:56 +10:00

6.4 KiB
Raw Blame History

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

  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 030, 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 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-<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 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.