Commit Graph
5 Commits
Author SHA1 Message Date
wuwiandClaude Opus 5 c1f7268a04 The flick stops losing the point, and the corpse falls and serves
THE FLICK. "when returning a ball with a spin it seems too sensitive, 8/10
times the ball lands out." Arithmetic, not feel. The singles line is 4.115 m;
an aimed shot targeted a flat ax*3.3 and THEN added err() on top — up to
+-2.4 m at quality 0 — so a flicked return landed as wide as 5.7 m, and the
only clamp in the path sat at +-5, outside the court. An UNAIMED return
targets 0 + the same scatter and is therefore always in: aiming was the thing
losing the point, which is backwards for the mode's one act of skill.

TUNE.flickReach scales the aim width by the same quality the scatter shrinks
with, so the two can never sum past the line — worst case 1.49+2.4 = 3.89, and
a perfectly timed flick still gets the full 3.3 with nothing left to scatter.
The test asserts that sum across the whole quality range rather than sampling
rallies. Not named `aimReach`: that key already exists further down TUNE (the
slingshot's 22 m aim ray) and the duplicate silently won until a test caught it.

The placement ring carries `q` now and is the flick's receipt — tight and hot
when your timing earned the full width, wide and dim when you barely got a
racket on it. Without it the rule is invisible: the shot just goes less wide.

THE FALL (handoff item 4). A point that takes a limb knocks the body down and
it drags itself up short of it. The three collapse frames wear the stage it
STILL WAS and the rise wears the stage it has BECOME — that order is the whole
beat, and it is what turns a popping wound stage into something the point did.
Own clock beside the horde's warning, for the reason this file keeps relearning
(a beat that moves the phase under a latch strands it); the 'over' beat it sets
outlives the fall so it cannot expire into setupPoint() mid-collapse. Only
fires when the stage actually moved — a quarter off a full pool is 0.75, so
point one has no limb to explain and a second of collapse for nothing is a beat
the player waits out.

THE OPPONENT SERVES (handoff item 7), and it was mostly switched off rather
than missing. setupPoint already placed a non-player server behind its own
baseline and armed aiServeT; update() already tossed and struck for O. Both
were gated on `G.mode === 'tennis'` and the match runs in splatter under a
permanent interlude — one condition. Genuinely new: service alternates at each
game boundary (the match calls advancePoint directly and the flip lives in
tennis's pointTo, which it bypasses — the same shape as the G.serveNum bug),
the zombie takes O's mark so picture and ballistics agree on where it stands,
and the serve poses are named onto it because O is invisible here.

racketStage() moves to config: drawZombie and the fall must not answer the
same question twice, which is how the boss's skins and its blood drifted apart.

Docs: PROJECT.md gains THE ZOMBIE FAMILY PIPELINE — the measured API contract
(4K is a parameter and the default is 1K; n is 1 so "return both" is not a
thing; aspect is an enum so pad to it), why magenta stays despite green being
the documented key, and the two things _zsheet.py must do globally.

Verified in the browser: brute in the match, z_brfall2 -> z_brw1rise on a
wounding point, and bounce -> toss -> serve off the opponent's own baseline.
58 tests green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 19:08:02 +02:00
wuwiandClaude Opus 5 a568d75aed Clear the last of the "ladder" vocabulary out of the handoff and the comments
Left over from the rename: the handoff still described opponent 2 as "rung N"
and pointed at matchRung, and three comments in gameplay.js still called the
opponent curve a ladder. One word per thing, everywhere now — OPPONENTS, WOUND
STAGES, a sprite FAMILY — with a note in the handoff saying which word replaced
what, so the next reader is not left translating.

(The barrel weapons keep their own "rungs of the same ladder" line: that one is
about the WEAPON payout order and was never part of the confusion.)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 14:54:54 +02:00
wuwiandClaude Opus 5 e674677f4e PROJECT.md: the art pipeline, corrected — and the brute stays parked
A day went into sprite consistency and most of it was spent solving the problem
in the wrong place. Written down so the next session starts from the answer
instead of rediscovering it.

The boundary everything else follows:
  the generator, GIVEN REFERENCE IMAGES  ->  identity, pose, composition
  the pipeline, DETERMINISTICALLY        ->  exact colour
  the gates                              ->  layout accidents only

With the proof, because it is the part that is hard to believe until measured:
the brute's serve sheet came back with his build, his garments and his DARK
TONES pixel-identical to the reference and his big pale shirt two shades
brighter. The model was looking straight at the character. Every call
re-renders and re-rendering re-lights. No prompt fixes that.

And the rule underneath all of it — A REFERENCE BEATS A DESCRIPTION, at every
level. The racket zombie's sheet was made from the character as an IMAGE plus
the poses as IMAGES and landed first time; the brute's three sheets each
re-derived the character from the same paragraph and produced three different
brutes. Corollary worth having: every corrective sentence in a prompt is
compensating for a reference you did not supply. When a prompt grows a third
"do NOT" clause, add an image instead.

Also written down: correct-then-check, calibrate-before-you-gate (with the
numbers every threshold was calibrated on), the five gates and what each one
actually measures, six traps that each shipped something silently before they
were caught, and three things still open — including that `changed` passed a
headless brute that still had its head, because it knows something moved and
not what.

The brute is PARKED: out of ZFAMS, 31 frames on disk, nine batches in the JSON,
one line away from live. Its healthy eleven are good; its wound stages are not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 14:54:09 +02:00
wuwiandClaude Opus 5 19ee62067a HANDOFF-MATCH.md: what landed, and the record shape a future session must not re-derive
Slices 1, 2 and 5 struck through with what they actually became; 3, 4, 6 and 7
left standing, with a note on 3 that the wound-stage art now exists for it and
on 4 that the racket_* batches are its template. The parts of slice 2 that are
still open (shots[] and the PARRY — the cheapest depth in the mode) are called
out rather than buried under a DONE.

Added: the record shape table (which of g/s/dead each mode uses and why), the
classic-tennis mode, the second ending, and four new traps — the stats banked
flag that no-kill modes never clear, the magenta key that ate racket strings,
the read-only PIL image that made the fix look like a no-op, and the detail
bar that ratchets against what it is replacing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 09:39:04 +02:00
wuwiandClaude Opus 5 a54cfa5f4f HANDOFF-MATCH.md: scoring model A, and the slice to make the match a real mode
Temporary file, written because a session ran out of context mid-slice — fold
into PROJECT.md and delete when the work lands. Not in the Dockerfile COPY;
docs are not served.

Records the decision (A: blood-only, measure becomes opponents felled) and
the finding behind it: Oliver believed losing a match killed him, when he was
bleeding out during deuce wars. That misreading is itself the bug — model A
only works if the player can see they are bleeding, not losing.

Also carries the build order (summary+measure, named ladder with
personalities, wound-driven play, the fall animation, menu, flick teaching,
opponent serve games), the flight-recorder recipe that ended four rounds of
guesswork, the traps this mode already sprung, and the do-nots.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 08:28:19 +02:00