Commit Graph

8 Commits

Author SHA1 Message Date
type-two
190a6e27d3 feat(paint): METEOR blend + yellow ZAP — the palette is complete
Three GDD §6 features land together, plus the design bug their verification
exposed.

METEOR (red+orange both >=20%): "smash light barriers" made literal. New
createSmashWall part — a brick-seamed light barrier, solid to everyone, but a
METEOR blob that RAMS it (>=5 u/s) shatters it into 8 real dynamic brick
shards that carry your momentum; rebuilds on race:respawn. Spec kind
'smashwall' with needColors for data courses. HUD shows "☄ METEOR" replacing
the pair's BURN/BOUNCE rows (GDD: the blend replaces its pair) and the ember
trail burns 2.2x while blended.

ZAP (yellow >=20%): "trigger colour-keyed machinery at range". New
machine/zap.ts — a charged blob pulses every 3s and fires any declared zap
target's machine:signal within radius, with an expanding arc ring. Breakfast
declares the fork's spring boot: purple earns it by WEIGHT on the plate,
yellow HOTWIRES it — verified the full arc: pulse -> boot telegraph -> kick
vy 10.6 -> lands on the finish podium. Yellow bubbles by the fork are the
source. Spec courses declare `zapTargets`. HUD shows " ZAP".

THE BUG THE WALL EXPOSED: the finale gate sat at z-59, but the finish box
reaches z-57.2 (podium-face bonks must count) and the podium collider face is
at z-58 — so in a LIVE race you finished before ever touching the gate; it
only ever blocked test blobs with the race idle. The whole gauntlet now lives
at z-56, in FRONT of the line: centre = green gate, left = METEOR smash wall,
right gap plugged (the MINI tunnel roof already gates x3..10), far left = open
detour. The cleanse arch moves to x-6.5/z-54.5 — at x-4.5 its zone overlapped
the METEOR approach and stripped the blend one step before the wall.

Verified in-browser (atomic calls; the wall-clock coverage cache and the
background sim poisoned split observations): clean blob bonks with race
running; METEOR blob shatters (8 shards, signal, drives through rubble to
"you finished 29% ORANGE"); wall rebuilds on R; zap fires exactly once per
cooldown. hasMeteor/hasZap are pure + unit-tested (58 assertions, 10 suites).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 22:22:24 +10:00
type-two
2777a580c9 feat(paint): ORANGE = BOUNCE — plug in the buff that was wired but dead
`jumpMul` has been plumbed end-to-end since Lane A — the controller multiplies
take-off speed by it, BuffSystem copies it every step — but computeModifiers
hardcoded `jumpMul: 1`, so a finished mechanic sat unplugged and 2 of the 7
palette colours (orange, yellow) had neither an effect nor a single source in
any course. Orange now drives it: jumpMul 1.0 -> 1.5 at full strength
(BOUNCE_JUMP, GDD §6 "rubberized"), same buffStrength curve as every other buff.

Paint source: orange bubbles on Breakfast Rush's high shelf, right before the
gap-jump lip — the one place in the game where jump height IS the problem, so
the buff has an obvious purpose instead of being decoration. Deliberately
opt-in and deliberately costly: they sit inside the GREEN grip zone, so
grabbing orange dilutes your green. Route for the jump or route for the grip.

Tuned during verification: the field uses impulse 1, not the default 3 —
at full strength the pops stacked and launched the blob 3.3u above the shelf
right at the lip, throwing off a jump this course is finely tuned around.

Verified end-to-end: orange 61% -> jumpMul 1.424 (matches buffStrength exactly),
take-off 7.10 -> 10.30, jump height 1.95 -> 3.87 (nearly double); HUD reads
"BOUNCE ×1.41" in orange; rolling the shelf pops 4 bubbles per pass. New test
assertions cover the 19%/45%/70% curve AND pin it to BOUNCE_JUMP, so a
regression to a constant 1 fails loudly instead of silently removing the buff
(53 assertions, 10 suites green).

Palette now 6/7 usable. Yellow ZAP is the remaining gap and is real design work
(triggering machinery at range), not a one-liner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 23:08:51 +10:00
type-two
1fa069729a feat(ui): paint identity polish — quiet HUD, loud finish
The HUD now whispers and the finish line celebrates. PaintHUD hides entirely
while you're clean (the body is the display — GDD pillar 2; an all-zero
seven-row table was dev noise) and shows rows only for colours you actually
carry. Added the missing MEGA/MINI buff labels — purple/pink have been live
since Lane G but the buff line never learned their names.

The finish banner now states your colour identity — "you finished 53% GREEN",
tinted and glowing in that colour ("squeaky clean" if you somehow finish
white) — and fires a confetti burst of falling paint chips whose colours are
weighted by YOUR coverage mix. The run's identity is the celebration.

Verified via world.tick: clean blob -> no HUD; green douse + cannon red ->
exactly those two rows + GRIP label; teleport finish -> banner "you finished
53% GREEN" + 36 mix-tinted chips. tsc + build green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 17:41:41 +10:00
Claude
e34773d35f harden asset runtime: guard the swap path, bound the boot, reconcile slots
The three that stopped a pack shipping:
 - a mesh-less GLB (armature-only export) fired onSwap anyway, hiding the
   primitive and adding nothing — an invisible prop with a live collider.
   Mesh count is now checked BEFORE the scene is touched, in one guard that
   covers every consumer including slotObject/blob.face.
 - the fulfilment path had no try/catch, so a throw became an unhandled
   rejection. Split in two: a build failure bails out before onSwap can hide
   anything; an onSwap failure keeps the replacement parented, because
   consumers hide their primitive on onSwap's first line and removing the fit
   node there would manufacture the one forbidden state.
 - preload() awaited every url forever. Each is now raced against a 10s
   deadline with allSettled semantics: a stalled host costs one warning and a
   fallback prop, not a game that never boots.

Also:
 - instantiate/instanceSync never throw — createBlob's call site is frozen and
   unguarded, so a throw there is a black screen.
 - ghost and blob share fitBodyToRadius(); a borrowed blob.body was rendering
   the ghost 2.17x oversized with the farm mesh.
 - skinned blob.body is rejected loudly instead of silently half-working; the
   idle-clip mixer is gone (it animated an orphaned skeleton in the fixed step).
 - paintableInfo counts UV islands: blobbo-base.glb passes every other check
   and still paints wrong at 1140 charts. Warning, not rejection.
 - slots.ts gains cannon.base, course.finish, course.tunnel, course.tramp, all
   hooked except tramp (built in frozen game.ts).
 - manifest ignores _-prefixed metadata keys instead of calling them typos.

Empty-manifest parity verified byte-for-byte: scripts/sacred-parity.check.ts
fingerprints the built scene and reports 49df4f20 on main and on this branch.
2026-07-19 12:12:07 +10:00
Claude
324d7d4568 Lane I: asset runtime — manifest, registry, slot hooks, IndexedDB overrides
Custom GLBs drop into 14 named slots without code changes; an empty manifest
produces today's game by construction (the fallback builders are the original
code moved into a closure, and an empty registry returns the caller's own
object by identity).

- src/assets/{slots,manifest,idb,registry,blobBody}.ts — schema + validation,
  GLTF cache with per-instance material cloning, fit nodes, IndexedDB override
  layer, paintability report.
- Slot hooks in createBlob, parts, cannon, greybox, puddles, ghost. Mesh-only:
  no collider, physics or logic line is touched. Animated sub-parts (plate cap,
  belt chevrons, fan blades, cannon pivot) stay procedural so a custom model
  cannot stop them moving.
- public/assets/ — the build had NO asset copy step at all, so every asset URL
  would have 404'd in production. public/ is vite's default publicDir, so this
  needs no vite.config change.
- Tests: 63 headless checks + a farm-GLB audit that fires PaintSkin's own
  raycast against the fitted body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 11:37:08 +10:00
type-two
0f2aca542b Wave 2 integration: zones staged, fork machines own the district, tuning pass
- installZones + cannonConfigs through the single cannon path; old centre
  plate/boot removed (fork versions supersede); belt+bucket to centre lane
  (sunk flush — proud face curbed the line); arch punishes failed MEGA runs
- puddles: distance-based stamping (speed-independent coverage), rate 4/u,
  contact tolerance 1.0 (bounce hops starved strips), fork strips widened
  toward the racing line + neutral-centre red top-up strip
- under-shelf volumes filled solid in the centre (side corridors left open);
  launcher catch widened to the full gap
- verified: naive hold-W finishes ~12s, ghost records+replays, MINI (0.77)
  passes the tunnel with 47% pink, buffs activate (GRIP live at 29% green),
  78% total coverage on a wandering run

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 12:18:30 +10:00
type-two
3699a1635a Lane G: colour zones, paint puddles, MEGA/MINI scale buffs
Fixes the live finding that a racer can finish with 0% paint. Paint is now
ROUTE-guaranteed by ground puddles, and colours come in single-colour zones so
buffs actually cross the 20% threshold.

- paint/puddles.ts: PaintPuddle + installPuddles. Flat coloured strips (no
  collider) that stamp the blob's underside via skin.splatAtPoint on a ~6/s
  cadence while rolling through, emitting paint:splatted. The body doesn't
  physically tumble (rotations locked), so an accumulated roll phase walks the
  contact point along the travel great circle — a straight run paints a stripe
  and coverage climbs instead of piling on the south pole.
- paint/coverage-math.ts: extend computeModifiers with purple MEGA (size→1.45 +
  heavier) and pink MINI (size→0.62 + lighter than clean at high %). They fight:
  net size uses (purple − pink) strength; mass floored so MINI stays positive.
  (Lives here, not buffs.ts, because the pure mapping must stay THREE-free for
  the bare-node test; BuffSystem already forwards mods.size/massMul unchanged.)
- paint/coverage-math.test.ts: +MEGA/MINI/fight cases (48 assertions pass).
- course/zones.ts: installZones lays RED/GREEN/BLUE puddles down the centre line
  and the PURPLE(left)/PINK(right) fork onto the machine district — a pressure
  plate (baseMass*1.25) + boot on the MEGA lane, a visibly-low pink tunnel
  (underside y≈1.12) on the MINI lane. Returns cannon configs for integration.
- demo (demos/lane-g.html + src/demo/lane-g.ts): drive the zones, F = hands-free
  hold-forward, 1-4 teleport to each zone, live coverage/size/mass + plate HUD.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 12:00:54 +10:00
type-two
eed2eb2646 Lane B (Paint): splat pipeline, coverage compute, buff/threshold framework
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>
2026-07-17 20:13:30 +10:00