Fixed-step world runtime, event bus wire protocol between lanes, integration seam (game.ts) with placeholder ball. Lanes A/B/C own disjoint dirs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3.1 KiB
3.1 KiB
Lane B — Paint
Owns: src/paint/, demos/lane-b.html, src/demo/lane-b.ts.
Never touches: src/contracts.ts, src/world.ts, src/game.ts, src/main.ts,
package.json, other lanes' dirs. No new deps — three + rapier only.
Mission
The core invention: paint lands on the blob's body, live per-colour coverage % is computed, and coverage drives buffs (GDD §5.1, §6). MVP colours: RED, GREEN, BLUE (design for all 7 — palette is in contracts).
Deliverables
src/paint/skin.ts—PaintSkinclass, attachable to ANY mesh with clean UVs (implements thePaintSkininterface in contracts):- Offscreen canvas (256×256) as the blob's colour mask → THREE.CanvasTexture as the material map over the white base.
applySplat(uv, color, radius)stamps a soft-edged irregular splat (quantize stored pixels to exact palette colours so counting is exact).splatAtPoint(mesh, worldPoint, color, radius)— raycast/closest-face → UV → applySplat. Handle UV seams tolerantly (a stamp near seam may wrap).coverage(): CoverageReport— recount every ≤250ms (cached between): bucket pixels by palette colour; fractions of TOTAL surface (unpainted counts in denominator).cleanse(fraction)— erase that fraction of painted pixels (random holes, looks scrubbed), 1 = full clean.
src/paint/cannon.ts—PaintCannon: placed emitter that fires paint-glob projectiles (small coloured spheres, gravity arc, Rapier sensor or manual sweep). On hit: blob →splatAtPoint; anything else → despawn (ground decals are V1). Fire on interval AND on eventmachine:signal {id}matching its configured trigger id. Emitspaint:splatted {color, target}.src/paint/buffs.ts—BuffSystem(aSystem): every frame maps the blob'scoverage()→blob.modifiersper GDD §6, MVP subset:- threshold 20%, strength scales linearly 20%→70%;
- RED
speedMulup to 1.6 + ember particle trail (cheap sprite points); - GREEN
gripup to 1.0; - BLUE
waterproof=true+ slick trail flag (slipperydecal V1 — flag ok); - total coverage →
massMul1.0→1.8 (paint = weight, GDD §5.2); - ≥70% one colour = super: modifier at max +
glow(emissive pulse).
- HUD
src/paint/hud.ts— tiny DOM overlay: per-colour % bars + active buffs. - Demo
demos/lane-b.html+src/demo/lane-b.ts: a plain rolling test ball (your own stub — do NOT import Lane A) circling between two cannons (red, green) + a cleanse key (C). HUD live. Prove: paint accumulates where hit, %s are sane, buffs kick in at 20%, super at 70%, cleanse works.
Acceptance
npm run buildpasses; demo shows visible splats accumulating AT hit locations, live HUD %, buff activation visibly changes the test ball (speed/trail/glow), cleanse visibly scrubs.- Coverage recount ≤250ms cadence, no per-frame full readback (perf).
- No edits outside owned paths.
Contracts (frozen — read src/contracts.ts first)
Listen: machine:signal, paint:request-splat {point, color, radius} (Lane C's
bucket dumps emit this). Emit: paint:splatted. Palette: PALETTE in contracts.