Drawn frames, not procedural fakes: brute lurch, boy stride, crowd applause

Every animation in this round gets real frames. The procedural squash-and-sway
on the wounded brute read as fake on sight, and that gait is where the comedy
lives.

- Each brute wound stage now has its own 2-frame lurch (z_gw/z_gwb, z_gw2/z_gw2b,
  ...). The generator gave every B frame a dragged pole that only stage 1
  actually carries, so it would have blinked in and out each step — stripped by
  keeping the largest blob, and where the pole touched the body, by cutting on
  column density (a torso is dense, a dragged pole is a thin diagonal).
- Frames step off DISTANCE COVERED, never elapsed time — boy.dist for ball boys,
  z/bruteStride for brutes. A time-driven cycle moonwalks the moment speed and
  frame rate disagree, which is exactly what "he slides faster than the animation
  suggests" was. Change a speed now and the legs keep up on their own.
- One boy, one slot. sendBoy no longer replaces a boy already on court: that was
  the appearing and disappearing, since every kill teleported him back to the
  wing mid-stride. New mess is appended to his round instead. He now walks for
  10.5s a crossing, and there is a lot of mopping to do.
- The applauding crowd is a real two-frame animation (seated / arms up), swapped
  per column on its own phase. The old "smooth wave" still shifted each column by
  a FRACTIONAL dy, and nearest-neighbour resampling at a fractional y re-snaps a
  column's pixel rows every frame — that boiling beside the HUD is what read as
  the scoreboard flickering. The board art is 99.9% opaque; nothing was ever
  showing through it. With the crowd calm the frame delta is now 0.0%.

Benchmarks: 136/150 serve returns (90.7% ±2.4%, the 11/12 bar is inside one
sigma), 30 human-tapped points = 50 hits / 0 whiffs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-07-27 10:20:43 +03:00
1 parent ff77922e85
commit 32401366fe
13 files changed
+77 -36

No files matched your search

+27
View File
@@ -155,6 +155,33 @@ New foundations you must know before touching anything:
exists, but only in tennis mode where there is nothing on the grass but a ball. `BOY_H` 1.2 → 1.5.
- `TUNE.burnSpread` is a T-panel knob if Oliver wants fire hotter still.
## Second feedback round — "do it properly" (2026-07-27)
Oliver rejected the procedural fakes on sight. The rule this round: **animation gets drawn frames.**
- **Every brute wound stage now has a 2-frame lurch** (`z_gw`/`z_gwb`, `z_gw2`/`z_gw2b`, …). The
procedural squash-and-sway that replaced them read as fake immediately — "that's where the comedy
comes from". Generated by handing the generator a composite of the four A-frames
(`assets/_sheet_gwound_a.png`, built with PIL) and asking for the stride swapped.
**Gotcha:** the generator gave every B frame a dragged pole, but only stage 1's A frame has one —
it would have blinked in and out each step. Stripped by keeping the largest connected blob
(stages 2–3) and, where the pole touched the body (stage 4), by cutting on column density: the
torso is dense, a dragged pole is a thin diagonal of ~7 px per column.
- **Frames step off DISTANCE COVERED, never elapsed time** — `boy.dist` for ball boys,
`zb.z / TUNE.bruteStride` for brutes. Time-driven cycles moonwalk whenever speed and frame rate
disagree, which is exactly what "he slides faster than the animation suggests" was.
Height for a wound stage ALWAYS comes from the A frame or the sprite pops size every step.
- **One boy, one slot.** `sendBoy` no longer replaces a boy who is already out — that was the
"appears and disappears": every kill teleported him back to the wing, so a kill streak restarted
him several times a second. New work is appended to his round (`jobs`). One crossing is now 10.5s.
- **The applauding crowd is a real two-frame animation** (`x_crowd` seated / `x_crowd2` arms up),
swapped per column on its own phase. The previous "smooth wave" still shifted each column by a
FRACTIONAL dy, and nearest-neighbour resampling of a scaled image at a fractional y re-snaps its
pixel rows every frame — that boiling beside the HUD was the "scoreboard flicker". The board art
is 99.9% opaque, so nothing was ever showing *through* it. **Test:** render twice without
stepping — identical pixels means nothing in the draw path rolls dice; and with the crowd calm
the frame-to-frame pixel delta must be 0.0%.
- Editing a full-scene image (`x_crowd` → `x_crowd2`) preserved the layout far better than expected:
mean difference 3.2/255, railing and tier rows unmoved. Worth reusing for other 2-frame swaps.
## Gotchas learned the hard way
- Browser-pane verification: python http.server sends no cache headers, so edited ES
modules often load STALE even after location.reload(). Reliable fix: preview_stop +