Commit Graph
7 Commits
Author SHA1 Message Date
wuwiandClaude Fable 5 99a87115b3 Four real run cycles, rolled one-call-one-row through a new _runroll.py
The old run frames were generated one at a time and it showed: 91/110/92/98 px
of body-width drift, duplicated poses drawn differently, no racket. Each
character (player m/f 6 frames, opponent m/f 8) now comes from ONE 4K call
anchored on a composed identity reference of its settled frames — the broken
run art stayed out of the reference on purpose. Stride-gated, cut with _fine,
judged by eye per roll. drawChar probes run8/run6/run4/run2.

Side effect: the male opponent's old run art faced LEFT against the
art-faces-RIGHT convention — he had been moonwalking right since install.
Fixed by drawing the new rows to the convention.

Pipeline lessons recorded in PROJECT.md (the model negotiates the count; a
back view has no direction; the racket clause goes early and loud).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 18:17:16 +02:00
wuwiandClaude Opus 5 494bab1b69 Stop the opponent shrinking, skating and sprinting on the spot
The "smaller when swinging or tossing" was an anchor bug, not art. _fine.py
scales a whole sheet so the --anchor frame is 224 px, and drawChar draws every
sprite at img.height/SPRITE_H — so anchoring opponent_play on the TOSS, with
arm and ball above the head, made 224 px cover the raised arm and shrank every
body on that sheet. o_swing1 was 162 px against a 224 px standing pose. Each
opponent batch now leads with a standing `ref` frame, which _fine.py anchors on
and _regen.py discards. Correctly anchored the toss is 278 px and the man is
full size; the swing is 221.

The receiver no longer slides 0.12 m side to side while waiting on a serve —
sliding a sprite sideways reads as skating, and the split-step swap to the run
frames (removed earlier) had already been the other half of that.

The idle bounce ball had one hardcoded offset for both men. They reach to
opposite sides of the screen — the player is drawn from behind, the opponent
from the front — so the opponent's ball sat out by his racket. dx and apex are
per-man now, and o_bounce/o_bounce2 were redrawn with a patting hand to put it
under.

Also: the opponent wears a RED headband, and a prompt of mine that said "white"
had quietly repainted it on six frames.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 11:37:16 +02:00
wuwiandClaude Opus 5 cd1c7a0cd8 Encode the splatter bed too, and give the opponent his racket back
zombie-mode: 5.9 MB of 24-bit WAV down to 287 KB. Its master is 982597 frames
at 48 kHz, so loopEnd is the un-round 20.47077 — the decoder hands back
20.4787, and those 7.9 ms of AAC padding would tick once a loop. Nothing in
assets/audio/ is a multi-megabyte WAV any more.

o_run1/o_run2 had no racket in either hand. The cause was the batch prompt:
opponent_run never said "racket", and its reference sheet was built from the
racket-less frames it was replacing, so there was nothing to copy one from. It
is a ref_only batch off o_idle1 now — same man, front-facing, racket in his
right hand — with the subject spelling out that he still carries it mid-stride.
The stride reads as well as the old one did.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:55:18 +02:00
wuwiandClaude Opus 5 ff6515670f Redraw the player and the opponent
Both full sets through the same pipeline: stances and ball-bounce, serve toss,
smash and both swing frames, the four-frame run, and the five-frame death. The
opponent's ten frames with them. Inner-edge density roughly doubles across the
board — the serve frames go 0.22-0.32 to 0.41-0.54.

_regen.py retries a rejected batch itself now (default three attempts). A
rejection is usually just a bad roll — two figures drawn close enough that the
slicer sees one frame, or a limb off the canvas — and re-rolling fixes it without
me sitting in the loop.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:47:29 +02:00
wuwiandClaude Opus 5 49d391dc64 Make it readable: 2x sprites, a flatter camera, honest hit timing
The umpire is situational again. Each line is tied to the thing that justifies
it — a new variant appearing, the court filling, one crossing your baseline, a
game lost, a horde, a changeover — behind one shared cooldown, so several
triggers at once still yield one remark. The random timer pool is gone, the
interval went 26s to 55s, and ump_out_of_order, ump_let and ump_no_appeal are
deleted.

"Visually the ball is not yet at the player but somehow the player already is
hit" was real and measurable. The hostile-ball hit test reused the parry's
hypot(dx, dz*0.8), and weighting depth shrinks it: contact fired while the ball
was still 0.68 m up-court and drawn up around his head. It has its own
unweighted test now, and contact lands at 0.25 m with the ball on his chest.
The parry keeps its weighting — a racket sweep really is wider than it is deep.

On "everything is a tad too small": measured first, because higher-resolution
sprites are only half the answer. At RES 3 the player rasterises into 170
device pixels from a 112px source — upscaled 1.5x — while a far shambler is
downscaled to 78. So near bodies needed resolution and far bodies needed the
camera.

Camera: pushed back and lengthened (camZ 42->48, camF 580->760) to flatten the
perspective so distant bodies grow relative to near ones rather than everything
zooming, camY raised for a steeper look-down, horizon re-centred. Far shambler
26->30px, boss 39->45px, player unchanged, court in the same band.

Sprites: the original sheets were still in the repo at full resolution, so
nothing needed regenerating — IDLE_TARGET_H went to 224 and every sheet was
re-sliced. The sheet-to-output mapping was never recorded, so it was recovered
by matching sliced frames to existing assets by image signature: 58 came back
from sheets, the 29 without a confident match were 2x-upscaled to stay on the
shared scale, since a sprite off that scale renders at half height.

Tennis untouched: 50 hits / 0 whiffs. 45-game soak clean, all four chapters.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 16:23:04 +02:00
wuwiandClaude Fable 5 9e6e686cff Landing rings, ball-boy fetching, 2x sprite density, assist flicker fix
- white ring shows where the incoming ball will land; yellow/red ring shows
  where your shot will land (red = going out) — Mario-Tennis-style feedback
- ball boys wait at the net posts, fetch every dead ball, and trot home
- canvas renders at 2x density, all sprites re-sliced at 112px; run/swing
  sheets regenerated in the finer style
- assist: 0.35m deadzone + facing hysteresis kills the left/right flicker;
  player reach 1.5 -> 1.7

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 13:56:18 +02:00
wuwi 7bedb2d215 initial commit 2026-07-11 09:23:01 +02:00