Draw the whole serve right-handed, and let the pull wind it up

The racket kept changing hands because the art disagreed with itself: p_idle,
p_bounce and p_toss held it in his LEFT hand while p_smash and p_swing used his
right, so every new frame copied whichever reference it was handed. The idle, the
bounce pair, the toss and the four wind-up frames are all regenerated with the
side named — drawn from behind, his right hand is the right-hand side of the
image, and only the racket HEAD crosses behind his back.

What separates the four wind-up stages is how far the racket has fallen, and that
needed the racket head pinned to a body landmark per frame — above his head,
beside his ear, between his shoulder blades, at the back of his thighs. Described
as "further into it each time" it drew four near-identical poses twice running.
They are wired late in the pull, Oliver's call: frame 1 owns the first half and
the three coiled frames are packed into the top half.

assets/_derive.py is deleted. Fixing generated art by mangling it afterwards is
always worse than regenerating; three guards in _regen.py turn the bad rolls that
used to install silently into automatic re-rolls — a portrait reference canvas
(the generator answered with a 2x2 grid), a non-magenta background (a green sheet
keyed away four racket heads), and a mirrored frame.

The bounce pair is one drawing from one sheet now, differing only in the free
hand, so BOUNCE_IDLE moved to his left side and was re-measured off the sprite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-01 00:49:28 +02:00
1 parent 231cd8650a
commit bcff17812f
17 files changed
+181 -170

No files matched your search

+4 -3
View File
@@ -333,9 +333,10 @@ export const POSE_TIME = 0.38;
// the server's idle ball bounce: sideways offset to the free hand, apex height, tempo.
// tempo drives the ball, the hand sprite AND the pock sound — one value, always in sync.
// Measured off the sprite, not guessed: in p_bounce2 the raised palm sits 0.63 m above his feet and
// 0.43 m to the side of his centre line, so the ball tops out just under the hand instead of beside it.
export const BOUNCE_IDLE = { dx: 0.43, apex: 0.6, tempo: 3.6 };
// Measured off the sprite, not guessed: the player is RIGHT-handed, so he pats the ball with his
// LEFT hand — the left-hand side of the image — whose palm sits ~0.69 m above his feet and ~0.36 m
// off his centre line. The ball tops out just under that hand instead of beside it.
export const BOUNCE_IDLE = { dx: 0.36, apex: 0.68, tempo: 3.6 };
// generated sprite sheets: the idle pose is this many pixels tall; all scales derive from it
export const SPRITE_H = 224; // 2x remaster (was 112). RES is 3, so the player at the baseline