The core invention: paint lands on the blob body where projectiles hit, live
per-colour coverage % is computed from a quantized UV mask, and coverage drives
buffs per GDD §6 MVP subset (RED speed+embers, GREEN grip, BLUE waterproof,
total→mass, 20% activate / 70% super+glow).
- src/paint/skin.ts PaintSkin: Canvas-2D mask on mesh UVs → CanvasTexture;
splatAtPoint raycasts impact→face UV; dual soft-canvas
+ hard quantized-index mask so coverage is exact
integer bucketing (never getImageData); 250ms cache;
scrub-hole cleanse; U-seam wrap.
- src/paint/coverage-math.ts pure, unit-tested: countBuckets, coverage report,
buffStrength, computeModifiers.
- src/paint/cannon.ts PaintCannon: gravity-arc glob projectiles (manual
swept sphere test), target-leading ballistic solve,
fires on interval AND machine:signal; emits paint:splatted.
- src/paint/buffs.ts BuffSystem: coverage→modifiers each step + ember trail
(additive Points) + super-state emissive pulse.
- src/paint/hud.ts PaintHUD: per-colour bars + active buff kit (throttled).
- src/paint/index.ts exports + installPaint() wiring the inbound Lane C
protocol (request-splat proximity-gated / request-cleanse).
- src/demo/lane-b.ts stub ball orbiting a RED and GREEN cannon; HUD live;
debug keys for fire/signal/flood/cleanse/pause.
- src/paint/coverage-math.test.ts 32 assertions (node --experimental-strip-types).
build: tsc strict + vite pass. No edits outside owned paths; contracts frozen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 lines
1.1 KiB
JavaScript
2 lines
1.1 KiB
JavaScript
import"./modulepreload-polyfill-B5Qt9EMX.js";import{d as p,k as m,M as h,c as f,j as u}from"./world-KhDGgEOv.js";function x(s){const{scene:y,physics:i,rapier:d}=s,c=new p(new m(60,1,60),new h({color:"#e8d9b8"}));c.position.y=-.5,c.receiveShadow=!0,y.add(c),i.createCollider(d.ColliderDesc.cuboid(30,.5,30).setTranslation(0,-.5,0));const o=new p(new f(.6,32,24),new h({color:"#F5F5F7",roughness:.35}));o.castShadow=!0,y.add(o);const a=i.createRigidBody(d.RigidBodyDesc.dynamic().setTranslation(0,3,0));i.createCollider(d.ColliderDesc.ball(.6).setRestitution(.4).setFriction(1),a);const e=new Set;addEventListener("keydown",n=>e.add(n.code)),addEventListener("keyup",n=>e.delete(n.code)),s.addSystem({update(){const t={x:0,z:0};(e.has("KeyW")||e.has("ArrowUp"))&&(t.z-=18),(e.has("KeyS")||e.has("ArrowDown"))&&(t.z+=18),(e.has("KeyA")||e.has("ArrowLeft"))&&(t.x-=18),(e.has("KeyD")||e.has("ArrowRight"))&&(t.x+=18),a.applyImpulse({x:t.x/60,y:0,z:t.z/60},!0);const l=a.translation();o.position.set(l.x,l.y,l.z);const r=a.rotation();o.quaternion.set(r.x,r.y,r.z,r.w)}}),s.onFrame(()=>{s.camera.lookAt(o.position)})}const w=await u(document.getElementById("app"));x(w);w.start();
|