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>
105 lines
6.4 KiB
Markdown
105 lines
6.4 KiB
Markdown
# 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-<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.
|