Wind the serve up with the pull, and stop the frames flickering

Four new frames (p_pull1..4) turn the slingshot drag into the serve's wind-up:
racket above the head, behind the head, behind the back, fully coiled, picked off
pullFrac — the same 0..1 that sets the shot's power — so the gesture and the man
are one movement and the release snaps to the smash.

The two bounce frames are one drawing now. p_bounce2 is p_bounce with the free
arm sheared up 12px and nothing else touched, so the swap cannot flicker, and it
follows the ball instead of its rising phase; BOUNCE_IDLE was re-measured off the
sprite so the ball tops out under the palm. p_toss had the tossed ball drawn into
it — cut, canvas height kept, because the game scales him off img.height.

The runner: hip centres sat -2.0/+7.1/-6.0/-0.1 px off the sprite centre, a 13px
lateral jump at 12 frames a second, and Oliver's diagnosis was right that all four
frames leaned the same way and lifted the same leg. Frames 3 and 4 mirrored, all
four padded to one width with the hip on the centre line.

assets/_derive.py holds the three derivations, since "the same drawing with one
small difference" is the one thing the generator cannot draw.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-07-31 23:51:30 +02:00
1 parent f71d9293fc
commit b1f8ea3240
16 files changed
+559 -419

No files matched your search

+41 -1
View File
@@ -5,7 +5,7 @@ everything they specified is shipped, and what still matters from them is record
long history, `~/.claude/projects/.../memory/splatter-tennis-history.md` is auto-loaded; for the
detail of any individual change, read the git log.
Last updated 2026-07-31. **Start with "How to work on this", "OPEN — leftovers" and "LATER —
Last updated 2026-07-31 (serve wind-up pass). **Start with "How to work on this", "OPEN — leftovers" and "LATER —
shipping it"** (all three just above the CLOSED sections) — the first tells you how Oliver's feedback
should be handled, the second is everything known to be outstanding in the game, the third is the
demo/store/campaign plan, which is decided but entirely unbuilt. All four playtest rounds are closed
@@ -384,6 +384,18 @@ Clear the intro first: `document.getElementById('intro')?.remove()`.
survives as a rim on a green court.
12. **Two-row "style reference / redraw this" input does NOT work.** The model returns both rows and
invents a background. Describe the target detail level in words instead.
13. **`assets/_derive.py` is where a frame that must MATCH another frame is made.** Three of them
live there now: `p_bounce2` (a per-column shear of `p_bounce` — the free arm bends, nothing else
moves), `p_toss` (the drawn-in ball cut out, canvas height KEPT so drawChar keeps his scale) and
the runner's hip registration. "Same drawing, one small difference" is the one request the
generator cannot fill: it returns a different man and the swap reads as a flicker, which is the
defect you were fixing. The bounce/toss steps are idempotent; the runner step is `--runner` and
must run exactly once per generated set, because it mirrors and it pads.
14. **A new pose needs a STANDING frame in the same sheet, as frame 0, thrown away after.** A sprite's
drawn size comes from its total height, so a sheet of nothing but arms-overhead poses has no
honest scale to anchor on and the man comes out short. `_regen.py`'s `ref` field does this, but
only when `ref_only` is unused — with `ref_only` set, list the throwaway as an extra sprite
(`p_pull0`) and delete the PNG afterwards.
Known generator quirks, all still true:
- It sometimes ignores the magenta (harmless — the key uses the corner colour).
@@ -397,6 +409,34 @@ Known generator quirks, all still true:
- **Colour-match any regenerated frame** of an existing pair before shipping: fit a per-channel
linear map onto the original's mean and sd, then verify with a mean-difference number.
## Session log: the serve is the gesture (2026-07-31, after the art pass)
Oliver's notes on the art pass: the zombies are good, the runner "flickers very hard", and the
player has three problems. All four fixed, all four measured first.
- **The pull IS the wind-up.** Four new frames (`p_pull1..4`, `assets/_regen_batches.json` batch
`player_pull`) — racket above the head, behind the head, behind the back, fully coiled — chosen in
`drawChar` off `pullFrac(G.aim.mag)`, the same 0..1 that sets the shot's power. Drag further, coil
further; release fires `p_smash`. Verified per stage with drawImage instrumentation:
mag 10/50/90/130 -> pull1/pull2/pull3/pull4.
- **The bounce pair is now ONE drawing.** `p_bounce2` is `p_bounce` with the free arm sheared up 12
px (0.10 m) — everything else is pixel-identical, so the swap cannot flicker. The swap follows the
BALL now (`|sin| > 0.5`) instead of its rising phase, and `BOUNCE_IDLE` was re-measured off the
sprite (`apex` 0.55 -> 0.6, `dx` 0.5 -> 0.43) so the ball tops out under the raised palm instead of
passing beside it.
- **`p_toss` had the tossed ball drawn into it.** Cut, without re-cropping the canvas: the game
scales him off `img.height`, so trimming the dead space would have shrunk him. Still 100x308.
- **The runner.** Two measured defects, both in the PNGs, no code change. Its four frames were
bbox-cropped and drawn centred on the body's x, so hip centres sat -2.0/+7.1/-6.0/-0.1 px off
centre — a 13 px lateral jump at 12 frames a second, which is the flicker. And Oliver's own
diagnosis was right: all four frames leaned the same way and lifted the SAME leg, so the cycle
never used his other side. Frames 3 and 4 are mirrored (he is front-on, so a mirror costs nothing)
and all four are padded to one width with the hip on the centre line.
Not done, and the honest reason: the runner's cadence is still time-driven (12 fps) rather than
stepped off distance covered like every other body. It did not read as wrong once the frames stopped
jumping, and it is one line if a playtest says otherwise.
## Session log: the art-resolution pass (2026-07-31)
**What changed.** The whole character roster was redrawn at real pixel-art resolution, plus a batch
+71
View File
@@ -0,0 +1,71 @@
# _derive.py — the frame fixes that must NOT go through the generator.
#
# python3 assets/_derive.py
#
# Three sprites are derived from other sprites by arithmetic, because the thing that matters about
# them is that they stay pixel-identical to their neighbour. Ask the generator for "the same drawing
# with one small difference" and it returns a different man, and the swap reads as a flicker — which
# is exactly the bug each of these fixes. Re-run this after regenerating p_bounce or the runner;
# every step is idempotent, so running it twice changes nothing.
import numpy as np
from PIL import Image
import os
A = os.path.dirname(os.path.abspath(__file__)) + '/'
load = lambda n: np.array(Image.open(A + n + '.png').convert('RGBA'))
save = lambda a, n: Image.fromarray(a, 'RGBA').save(A + n + '.png')
# 1. p_toss — the generator drew the tossed ball into the sprite; the game draws the real ball.
# The CANVAS keeps its height: drawChar scales the man off img.height, so re-cropping the dead
# space would shrink him.
a = load('p_toss')
rows = (a[..., 3] > 128).any(1)
cut = int(np.argmax(~rows)) # the ball floats clear above the raised hand
if cut and not rows[cut:cut + 8].any():
a[:cut] = 0; save(a, 'p_toss')
print(f'p_toss: ball removed (rows 0..{cut})')
else:
print('p_toss: already ball-free')
# 2. p_bounce2 — p_bounce with the free arm bent so the hand rises 12 px (0.10 m) to meet the ball
# at the top of its bounce. A per-column shear, pinned at the shoulder and rigid from the wrist out,
# so the forearm bends instead of the hand detaching.
X0, K, MAX = 96, 0.8, 12
b = load('p_bounce'); out = b.copy()
for x in range(X0, b.shape[1]):
s = min(MAX, round(K * (x - X0)))
if s: out[:-s, x] = b[s:, x]; out[-s:, x] = 0
save(out, 'p_bounce2')
print('p_bounce2: derived from p_bounce')
# 3. z_f1..4 — the runner, with `--runner`. This one is NOT idempotent (it mirrors, and it pads),
# so it is opt-in: run it exactly once on a freshly generated set of runner frames.
# Two defects, both measured. The generator drew all four frames leaning the same way and lifting
# the SAME leg, so the cycle never used his other side — mirroring frames 3 and 4 gives an
# alternating stride out of the poses that exist (he is front-on, so a mirror is free). And each
# frame is cropped to its own bbox and drawn centred on the body's x, so a swinging arm slid the
# whole man sideways: hip centres sat -2.0/+7.1/-6.0/-0.1 px off centre, a 13 px lateral jump at 12
# frames a second — that was the hard flicker. Pad every frame to one width with the HIP on the
# centre line. Padding is free: drawStatic derives the drawn width from img.width, so the figure
# inside keeps its scale and only its anchor moves.
import sys
if '--runner' not in sys.argv:
print('z_f1..4: skipped (pass --runner; it mirrors and pads, so it must run only once)')
raise SystemExit
NS = ['z_f1', 'z_f2', 'z_f3', 'z_f4']
for n in NS[2:]:
save(np.array(Image.open(A + n + '.png').convert('RGBA').transpose(Image.FLIP_LEFT_RIGHT)), n)
print(f'{n}: mirrored — the other leg leads')
ims = [load(n) for n in NS]
def hip(a): # midpoint of the hip band, not the mean: an arm
al = a[..., 3] > 128 # crossing the band would drag a mean sideways
xs = np.nonzero(al[int(al.shape[0] * .50):int(al.shape[0] * .62)].any(0))[0]
return (xs.min() + xs.max()) / 2
hips = [hip(a) for a in ims]
W = int(np.ceil(max(max(h, a.shape[1] - h) for h, a in zip(hips, ims)) * 2))
for n, a, h in zip(NS, ims, hips):
left = int(round(W / 2 - h))
o = np.zeros((a.shape[0], W, 4), np.uint8)
o[:, left:left + a.shape[1]] = a
save(o, n)
print(f'z_f1..4: hip-registered onto {W}px')
+428 -411
View File
@@ -1,413 +1,430 @@
{
"shambler_a": {
"sprites": [
"z_a1",
"z_a2",
"z_a3",
"z_a4"
],
"subject": "Four frames of the same walking zombie in a grey shirt and dark trousers, a four-frame shamble cycle, drawn as coarse low-resolution pixel art with big blocky pixels and almost no internal detail.",
"anchor": 0
},
"shambler_b": {
"sprites": [
"z_b1",
"z_b2",
"z_b3",
"z_b4"
],
"subject": "Four frames of the same walking zombie in a brown coat over a shirt, a four-frame shamble cycle, drawn as coarse low-resolution pixel art with big blocky pixels and almost no internal detail.",
"anchor": 0
},
"shambler_wounds": {
"sprites": [
"z_aw",
"z_awb",
"z_bw",
"z_bwb"
],
"subject": "Four frames of two WOUNDED walking zombies, drawn as coarse low-resolution pixel art. The first two are the same grey-shirted zombie whose right arm ends in a ragged bloody stump, in two stages of one stride. The last two are the same brown-coated zombie with an open red gash across his skull, in two stages of one stride. Keep each wound exactly where it is.",
"anchor": 0
},
"brute_walk": {
"sprites": [
"z_g1",
"z_g2",
"z_g3",
"z_g4"
],
"subject": "Four frames of the same enormous fat zombie brute in a stained grey-green shirt and brown trousers, dragging a rusty pole in one hand, a four-frame heavy lumber, drawn as coarse low-resolution pixel art.",
"anchor": 0
},
"brute_wounds_b": {
"sprites": [
"z_gwb",
"z_gw2b",
"z_gw3b",
"z_gw4b"
],
"subject": "Four damage stages of the same enormous fat zombie brute, mid-stride on the opposite foot, drawn as coarse low-resolution pixel art: (1) one arm torn off at the shoulder, dragging his pole; (2) both arms gone; (3) chest and belly torn open with ribs showing; (4) headless, ribs out. Keep each stage's injuries exactly as they are.",
"anchor": 0
},
"steward": {
"sprites": [
"z_d1",
"z_d2",
"z_d3",
"z_d4"
],
"subject": "Four frames of the same big heavy zombie stadium steward in a high-visibility ORANGE safety vest over a dark shirt, dark trousers and heavy boots, a four-frame walk cycle, drawn as coarse low-resolution pixel art. Keep the vest's reflective stripes.",
"anchor": 0
},
"steward_wounds": {
"sprites": [
"z_dw",
"z_dwb"
],
"subject": "Two frames of the same badly wounded zombie stadium steward in a torn high-visibility ORANGE vest \u2014 one arm mangled, a deep bloody gash across the chest, head lolling \u2014 walking, two stages of one stride, drawn as coarse low-resolution pixel art.",
"anchor": 0
},
"cpu_walk": {
"sprites": [
"z_c1",
"z_c2",
"z_c3",
"z_c4"
],
"subject": "Four frames of the same zombie tennis player facing the viewer \u2014 yellow hair, red headband, red shirt, grey shorts, a wooden racket hanging from one hand \u2014 a four-frame shuffle towards the camera, drawn as coarse low-resolution pixel art.",
"anchor": 0
},
"cpu_wounds": {
"sprites": [
"z_cw1",
"z_cw1b",
"z_cw2",
"z_cw2b"
],
"subject": "Four frames of the same wounded zombie tennis player in a red shirt holding a wooden racket, drawn as coarse low-resolution pixel art. The first two are him wounded and bleeding, in two stages of one stride. The last two are the same character HEADLESS, a ragged red stump where the neck was, still holding the racket, in two stages of one stride.",
"anchor": 0
},
"player_stance": {
"sprites": [
"p_idle1",
"p_idle2",
"p_bounce",
"p_bounce2"
],
"subject": "Four frames of the same tennis player seen from BEHIND in a white shirt, blue shorts and a white headband, holding a racket, drawn as coarse low-resolution pixel art: two waiting stances and two frames of him bouncing a ball before serving.",
"anchor": 0
},
"player_serve": {
"sprites": [
"p_toss",
"p_smash"
],
"ref_only": [
"p_run1"
],
"anchor": 0,
"check": "same",
"subject": "The reference is one tennis player in a white shirt, blue shorts and a white headband, seen FROM BEHIND. Draw TWO frames of that same player serving, same size, same style, same BRIGHT WHITE shirt and BRIGHT BLUE shorts as the reference \u2014 do not wash the colours out. Both frames are seen FROM BEHIND, the back of his head towards you, no face, no eyes: (1) the ball toss \u2014 he leans back, his left hand thrown up releasing the ball above him, the racket dropped low behind his back; (2) the smash \u2014 stretched fully upright on his toes, the racket swung high overhead, both arms up. Output exactly two figures in one row, far apart."
},
"player_run": {
"sprites": [
"p_run1",
"p_run2",
"p_run3",
"p_run4"
],
"ref": "p_idle1",
"anchor": 0,
"subject": "Five frames of the same tennis player in a white shirt and blue shorts, drawn as coarse low-resolution pixel art. Every figure is seen strictly FROM BEHIND \u2014 the back of his head and the back of his shirt face the viewer, no face, no eyes, no nose, he is running or standing AWAY from the camera. First a standing frame for scale, then a four-frame run cycle, running away from the camera with no racket.",
"check": "same"
},
"player_death": {
"sprites": [
"p_die1",
"p_die2",
"p_die3",
"p_die4",
"p_die5"
],
"subject": "Five frames of the same tennis player in a white shirt and blue shorts collapsing, each one lower than the last, drawn as coarse low-resolution pixel art: arched backwards with both arms flung up; dropped to his knees; pitching forward onto one hand; face down and flat with both arms out in a dark pool; and a low flattened heap of torn cloth and dark red with the stained headband on top. Each figure must stay clearly shorter than the one before it.",
"anchor": 0
},
"opponent_stance": {
"sprites": [
"o_idle1",
"o_idle2",
"o_bounce",
"o_bounce2"
],
"subject": "Four frames of the same tennis opponent in a RED shirt and dark shorts, seen from the front across the net, holding a racket, drawn as coarse low-resolution pixel art: two waiting stances and two frames of him bouncing a ball before serving.",
"anchor": 0
},
"opponent_play": {
"sprites": [
"o_toss",
"o_smash",
"o_swing1",
"o_swing2"
],
"subject": "Four frames of the same tennis opponent in a RED shirt and dark shorts, drawn as coarse low-resolution pixel art: tossing the ball up for a serve; the smash at full stretch with the racket overhead; and two frames of a forehand swing.",
"anchor": 0
},
"opponent_run": {
"sprites": [
"o_run1",
"o_run2"
],
"subject": "Two frames of the same tennis opponent in a RED shirt and dark shorts running across the court towards the camera, two stages of one stride, drawn as coarse low-resolution pixel art.",
"anchor": 0
},
"ump_boy": {
"sprites": [
"x_ump",
"x_boyrun1",
"x_boyrun2"
],
"subject": "Three frames drawn as coarse low-resolution pixel art: a tennis UMPIRE in a white sun hat, blazer and tie, seated upright with a clipboard; and two frames of a young BALL BOY in a white polo shirt and shorts sprinting, two stages of one stride. Keep the umpire seated and the ball boy running.",
"anchor": 0
},
"boy_carry": {
"sprites": [
"x_boycarry1",
"x_boycarry2",
"x_boycarry3",
"x_boycarry4"
],
"subject": "Four frames of the same young ball boy in a white polo shirt and shorts walking with a mop shouldered, a four-frame walk cycle, drawn as coarse low-resolution pixel art.",
"anchor": 0
},
"boy_mop": {
"sprites": [
"x_boymop1",
"x_boymop2",
"x_boymop3",
"x_boymop4"
],
"subject": "Four frames of the same young ball boy in a white polo shirt and shorts pushing a mop across the ground, a four-frame mopping cycle, bent over the mop handle, drawn as coarse low-resolution pixel art.",
"anchor": 0
},
"brute_wounds_a": {
"sprites": [
"z_gw",
"z_gw2"
],
"anchor": 0,
"subject": "Two damage stages of the same enormous fat zombie brute in a stained shirt, drawn as coarse low-resolution pixel art: the first has one arm torn off at the shoulder leaving a ragged stump and still drags a rusty pole; the second has BOTH arms gone. Keep the injuries exactly as they are."
},
"brute_wounds_c": {
"sprites": [
"z_gw3",
"z_gw4"
],
"anchor": 0,
"subject": "Two damage stages of the same enormous fat zombie brute in a stained shirt, drawn as coarse low-resolution pixel art: the first has his chest and belly torn wide open with ribs showing; the second is HEADLESS with the ribs out, still standing. Keep the injuries exactly as they are."
},
"shield_walk": {
"sprites": [
"z_s1",
"z_s2"
],
"anchor": 0,
"subject": "Two frames of the same zombie spectator in a grey-brown coat holding a wooden folding chair up in front of himself like a shield, shuffling forward, two stages of one stride, drawn as coarse low-resolution pixel art."
},
"shield_walk2": {
"sprites": [
"z_s3",
"z_s4"
],
"anchor": 0,
"subject": "Two more frames of the same zombie spectator in a grey-brown coat holding a wooden folding chair up in front of himself like a shield, shuffling forward, two further stages of the same stride, drawn as coarse low-resolution pixel art."
},
"shield_down": {
"sprites": [
"z_sdown"
],
"anchor": 0,
"subject": "One frame of a zombie spectator in a grey-brown coat who has just had his wooden folding chair knocked DOWN out of his hands \u2014 the chair is falling beside him and he is staggering with his arms up \u2014 drawn as coarse low-resolution pixel art."
},
"boy_run_2": {
"sprites": [
"x_boyrun3",
"x_boyrun4"
],
"anchor": 0,
"subject": "Two frames of the same young ball boy in a white polo shirt and shorts sprinting across a tennis court, two further stages of the same run cycle, drawn as coarse low-resolution pixel art."
},
"player_back": {
"sprites": [
"p_idle1",
"p_idle2",
"p_bounce",
"p_bounce2"
],
"ref_only": [
"p_run1"
],
"anchor": 0,
"check": "same",
"subject": "The reference is one tennis player in a white shirt, blue shorts and a white headband, seen FROM BEHIND. Draw FOUR frames of that same player, same size, same style. Every figure is drawn strictly FROM BEHIND like the reference: the back of his head and the back of his shirt face you, NO face, NO eyes, NO nose, NO mouth anywhere in the image. The four frames: (1) standing waiting, feet apart, racket held down at his right side; (2) the same stance with his weight shifted to the other foot; (3) bouncing a ball on the ground beside his right foot, leaning slightly down; (4) the same bounce a moment later, the ball higher. Output exactly four figures in one row."
},
"runner_front": {
"sprites": [
"z_f1",
"z_f2",
"z_f3",
"z_f4"
],
"ref_only": [
"z_c1"
],
"anchor": 0,
"check": "same",
"subject": "The reference is one zombie drawn FACING THE VIEWER. Draw FOUR frames of a DIFFERENT zombie in that same front-facing style, same size: a lean SPRINTING zombie in a torn grey-green tracksuit, charging straight down the court AT THE VIEWER. In every frame his chest and face point at you and he is coming towards you, foreshortened \u2014 one knee driven up high towards the camera, the other leg trailing far behind, arms pumping across his chest, body leaning into the run. The four frames are four stages of that charge: left knee up, mid-air stretch, right knee up, mid-air stretch the other way. He must NEVER be running sideways across the frame. Torn tracksuit, exposed ribs, green skin, sunken eyes and teeth. Output exactly four figures in one row."
},
"brute_all_wounds": {
"sprites": [
"z_gw",
"z_gwb",
"z_gw2",
"z_gw2b"
],
"anchor": 0,
"subject": "Four frames of the same enormous fat zombie brute, drawn as coarse low-resolution pixel art. The FIRST TWO are the same damage stage \u2014 his LEFT arm torn off at the shoulder leaving a ragged bloody stump, the right arm dragging a rusty pole \u2014 shown as two stages of one stride. The LAST TWO are the next damage stage, BOTH arms gone at the shoulders, again two stages of one stride. Within each pair the injuries must be IDENTICAL and on the same side of the body: only the legs change.",
"check": "same"
},
"brute_all_wounds2": {
"sprites": [
"z_gw3",
"z_gw3b",
"z_gw4",
"z_gw4b"
],
"anchor": 0,
"subject": "Four frames of the same enormous fat zombie brute, drawn as coarse low-resolution pixel art. The FIRST TWO are the same damage stage \u2014 chest and belly torn wide open with the ribs showing \u2014 as two stages of one stride. The LAST TWO are the final stage, HEADLESS with the ribs out, still standing, again two stages of one stride. Within each pair the injuries must be IDENTICAL and in the same place: only the legs change.",
"check": "same"
},
"player_swing": {
"sprites": [
"p_swing1",
"p_swing2"
],
"ref": "p_idle1",
"anchor": 0,
"check": "same",
"subject": "Three frames of the same tennis player in a white shirt, blue shorts and a white headband, seen from BEHIND, drawn as coarse low-resolution pixel art. First a plain standing frame for scale, then two frames of a forehand swing \u2014 racket drawn back across the body, then followed through. In both he faces AWAY from the camera, up the court \u2014 the back of his head towards you, no face."
},
"brute_w1": {
"sprites": [
"z_gw",
"z_gwb"
],
"ref_only": [
"z_gw"
],
"anchor": 0,
"check": "loose",
"scale": 1.5,
"subject": "The reference is one enormous fat zombie brute whose arm on the RIGHT-HAND SIDE OF THE IMAGE is torn off at the shoulder, leaving a ragged bloody stump, while the arm on the LEFT-HAND SIDE of the image is whole and drags a rusty pole. Draw TWO frames of that exact body as two stages of one stride. In BOTH frames the bloody stump must stay on the RIGHT-HAND SIDE OF THE IMAGE and the pole-dragging arm on the LEFT \u2014 do not mirror the body. Only the legs and the lean change: first left leg forward, then legs swapped with the weight sunk lower. Output exactly two figures in one row."
},
"brute_w2": {
"sprites": [
"z_gw2",
"z_gw2b"
],
"ref_only": [
"z_gw2"
],
"anchor": 0,
"check": "same",
"subject": "The reference is one enormous fat zombie brute with BOTH arms torn off at the shoulders, two ragged bloody stumps. Draw TWO frames of that EXACT same body \u2014 same injury, on the SAME side, same clothes, same size, same facing \u2014 as two stages of one stride: the first with his left leg forward and right leg back, the second with the legs swapped and his weight sunk lower. The injury must be pixel-identical in both frames; ONLY the legs and the lean change. Output exactly two figures in one row."
},
"brute_w3": {
"sprites": [
"z_gw3",
"z_gw3b"
],
"ref_only": [
"z_gw3"
],
"anchor": 0,
"check": "same",
"subject": "The reference is one enormous fat zombie brute with his chest and belly torn wide open with the ribs showing through. Draw TWO frames of that EXACT same body \u2014 same injury, on the SAME side, same clothes, same size, same facing \u2014 as two stages of one stride: the first with his left leg forward and right leg back, the second with the legs swapped and his weight sunk lower. The injury must be pixel-identical in both frames; ONLY the legs and the lean change. Output exactly two figures in one row."
},
"brute_w4": {
"sprites": [
"z_gw4",
"z_gw4b"
],
"ref_only": [
"z_gw4"
],
"anchor": 0,
"check": "loose",
"subject": "Draw TWO frames of an enormous fat zombie brute that has NO HEAD AT ALL \u2014 the neck ends in a ragged red stump at shoulder height, there is no skull, no face, no jaw, nothing above the shoulders \u2014 and whose chest is torn open with the ribs showing. Same body, same clothes and same size as the reference, as two stages of one stride: first left leg forward, then legs swapped with the weight lower. The missing head and the chest wound must be identical in both frames; only the legs change. Output exactly two figures in one row."
},
"player_stand": {
"sprites": [
"p_idle1",
"p_idle2"
],
"ref_only": [
"p_run1"
],
"anchor": 0,
"check": "same",
"subject": "The reference is one tennis player in a white shirt, blue shorts and a white headband, seen FROM BEHIND. Draw TWO frames of that same player, same size, same style. Drawn strictly FROM BEHIND like the reference: the back of his head and the back of his shirt face you, NO face, NO eyes, NO nose, NO mouth anywhere in the image. Frame 1: standing waiting, feet apart, a racket held down at his right side. Frame 2: the same stance with his weight shifted onto the other foot and the racket swung slightly across. Output exactly two figures in one row, far apart."
},
"player_bounce": {
"sprites": [
"p_bounce",
"p_bounce2"
],
"ref_only": [
"p_run1"
],
"anchor": 0,
"check": "same",
"subject": "The reference is one tennis player in a white shirt, blue shorts and a white headband seen FROM BEHIND. Draw TWO frames of that same player getting ready to serve, same size, same style, seen strictly FROM BEHIND \u2014 back of his head towards you, NO face. He holds the racket in his LEFT hand, down at his left side, which is the LEFT-HAND SIDE OF THE IMAGE. His RIGHT hand is empty and reaches down and out to the RIGHT-HAND SIDE OF THE IMAGE, palm down, as if he has just bounced a ball on the ground there. Frame 1: he leans forward slightly, right hand low near the ground. Frame 2: he straightens a little, right hand risen to about knee height. Draw NO ball in either frame \u2014 the ball is added by the game. Output exactly two figures in one row, far apart."
},
"brute_stages": {
"sprites": [
"z_gw",
"z_gw2",
"z_gw3",
"z_gw4"
],
"ref_only": [
"z_g1"
],
"anchor": 0,
"check": "loose",
"scale": 1.5,
"subject": "The reference is an enormous fat zombie brute \u2014 huge belly, stained shirt, brown trousers, dragging a rusty pole. Draw FOUR frames of that SAME body at that SAME size and bulk, standing, as four stages of damage: (1) the arm on the right-hand side of the image torn off at the shoulder leaving a ragged bloody stump, the other arm still dragging the pole; (2) BOTH arms torn off at the shoulders, two bloody stumps, no pole; (3) both arms gone and the chest and belly torn wide open with ribs showing through; (4) all of that AND no head at all \u2014 the neck ends in a ragged red stump at shoulder height, no skull, no face. He must stay the same enormous size and shape in all four \u2014 do not slim him down as the damage grows. Output exactly four figures in one row, evenly spaced."
},
"brute_s12": {
"sprites": [
"z_gw",
"z_gw2"
],
"ref_only": [
"z_g1"
],
"anchor": 0,
"check": "loose",
"scale": 1.4,
"subject": "The reference is an enormous fat zombie brute \u2014 huge belly, stained shirt, brown trousers. Draw TWO frames of that SAME body at that SAME enormous size and bulk, standing, as two stages of damage: (1) the arm on the right-hand side of the image torn off at the shoulder leaving a ragged bloody stump, the other arm still dragging a rusty pole; (2) BOTH arms torn off at the shoulders, two bloody stumps, no pole. Do not slim him down. Output exactly two figures in one row, far apart."
},
"brute_s34": {
"sprites": [
"z_gw3",
"z_gw4"
],
"ref_only": [
"z_g1"
],
"anchor": 0,
"check": "loose",
"scale": 1.4,
"subject": "The reference is an enormous fat zombie brute \u2014 huge belly, stained shirt, brown trousers. Draw TWO frames of that SAME body at that SAME enormous size and bulk, standing, as two stages of damage: (1) both arms gone and the chest and belly torn wide open with the ribs showing through; (2) all of that AND no head at all \u2014 the neck ends in a ragged red stump at shoulder height, no skull, no face, nothing above the shoulders. Do not slim him down. Output exactly two figures in one row, far apart."
}
"shambler_a": {
"sprites": [
"z_a1",
"z_a2",
"z_a3",
"z_a4"
],
"subject": "Four frames of the same walking zombie in a grey shirt and dark trousers, a four-frame shamble cycle, drawn as coarse low-resolution pixel art with big blocky pixels and almost no internal detail.",
"anchor": 0
},
"shambler_b": {
"sprites": [
"z_b1",
"z_b2",
"z_b3",
"z_b4"
],
"subject": "Four frames of the same walking zombie in a brown coat over a shirt, a four-frame shamble cycle, drawn as coarse low-resolution pixel art with big blocky pixels and almost no internal detail.",
"anchor": 0
},
"shambler_wounds": {
"sprites": [
"z_aw",
"z_awb",
"z_bw",
"z_bwb"
],
"subject": "Four frames of two WOUNDED walking zombies, drawn as coarse low-resolution pixel art. The first two are the same grey-shirted zombie whose right arm ends in a ragged bloody stump, in two stages of one stride. The last two are the same brown-coated zombie with an open red gash across his skull, in two stages of one stride. Keep each wound exactly where it is.",
"anchor": 0
},
"brute_walk": {
"sprites": [
"z_g1",
"z_g2",
"z_g3",
"z_g4"
],
"subject": "Four frames of the same enormous fat zombie brute in a stained grey-green shirt and brown trousers, dragging a rusty pole in one hand, a four-frame heavy lumber, drawn as coarse low-resolution pixel art.",
"anchor": 0
},
"brute_wounds_b": {
"sprites": [
"z_gwb",
"z_gw2b",
"z_gw3b",
"z_gw4b"
],
"subject": "Four damage stages of the same enormous fat zombie brute, mid-stride on the opposite foot, drawn as coarse low-resolution pixel art: (1) one arm torn off at the shoulder, dragging his pole; (2) both arms gone; (3) chest and belly torn open with ribs showing; (4) headless, ribs out. Keep each stage's injuries exactly as they are.",
"anchor": 0
},
"steward": {
"sprites": [
"z_d1",
"z_d2",
"z_d3",
"z_d4"
],
"subject": "Four frames of the same big heavy zombie stadium steward in a high-visibility ORANGE safety vest over a dark shirt, dark trousers and heavy boots, a four-frame walk cycle, drawn as coarse low-resolution pixel art. Keep the vest's reflective stripes.",
"anchor": 0
},
"steward_wounds": {
"sprites": [
"z_dw",
"z_dwb"
],
"subject": "Two frames of the same badly wounded zombie stadium steward in a torn high-visibility ORANGE vest \u2014 one arm mangled, a deep bloody gash across the chest, head lolling \u2014 walking, two stages of one stride, drawn as coarse low-resolution pixel art.",
"anchor": 0
},
"cpu_walk": {
"sprites": [
"z_c1",
"z_c2",
"z_c3",
"z_c4"
],
"subject": "Four frames of the same zombie tennis player facing the viewer \u2014 yellow hair, red headband, red shirt, grey shorts, a wooden racket hanging from one hand \u2014 a four-frame shuffle towards the camera, drawn as coarse low-resolution pixel art.",
"anchor": 0
},
"cpu_wounds": {
"sprites": [
"z_cw1",
"z_cw1b",
"z_cw2",
"z_cw2b"
],
"subject": "Four frames of the same wounded zombie tennis player in a red shirt holding a wooden racket, drawn as coarse low-resolution pixel art. The first two are him wounded and bleeding, in two stages of one stride. The last two are the same character HEADLESS, a ragged red stump where the neck was, still holding the racket, in two stages of one stride.",
"anchor": 0
},
"player_stance": {
"sprites": [
"p_idle1",
"p_idle2",
"p_bounce",
"p_bounce2"
],
"subject": "Four frames of the same tennis player seen from BEHIND in a white shirt, blue shorts and a white headband, holding a racket, drawn as coarse low-resolution pixel art: two waiting stances and two frames of him bouncing a ball before serving.",
"anchor": 0
},
"player_serve": {
"sprites": [
"p_toss",
"p_smash"
],
"ref_only": [
"p_run1"
],
"anchor": 0,
"check": "same",
"subject": "The reference is one tennis player in a white shirt, blue shorts and a white headband, seen FROM BEHIND. Draw TWO frames of that same player serving, same size, same style, same BRIGHT WHITE shirt and BRIGHT BLUE shorts as the reference \u2014 do not wash the colours out. Both frames are seen FROM BEHIND, the back of his head towards you, no face, no eyes: (1) the ball toss \u2014 he leans back, his left hand thrown up releasing the ball above him, the racket dropped low behind his back; (2) the smash \u2014 stretched fully upright on his toes, the racket swung high overhead, both arms up. Output exactly two figures in one row, far apart."
},
"player_run": {
"sprites": [
"p_run1",
"p_run2",
"p_run3",
"p_run4"
],
"ref": "p_idle1",
"anchor": 0,
"subject": "Five frames of the same tennis player in a white shirt and blue shorts, drawn as coarse low-resolution pixel art. Every figure is seen strictly FROM BEHIND \u2014 the back of his head and the back of his shirt face the viewer, no face, no eyes, no nose, he is running or standing AWAY from the camera. First a standing frame for scale, then a four-frame run cycle, running away from the camera with no racket.",
"check": "same"
},
"player_death": {
"sprites": [
"p_die1",
"p_die2",
"p_die3",
"p_die4",
"p_die5"
],
"subject": "Five frames of the same tennis player in a white shirt and blue shorts collapsing, each one lower than the last, drawn as coarse low-resolution pixel art: arched backwards with both arms flung up; dropped to his knees; pitching forward onto one hand; face down and flat with both arms out in a dark pool; and a low flattened heap of torn cloth and dark red with the stained headband on top. Each figure must stay clearly shorter than the one before it.",
"anchor": 0
},
"opponent_stance": {
"sprites": [
"o_idle1",
"o_idle2",
"o_bounce",
"o_bounce2"
],
"subject": "Four frames of the same tennis opponent in a RED shirt and dark shorts, seen from the front across the net, holding a racket, drawn as coarse low-resolution pixel art: two waiting stances and two frames of him bouncing a ball before serving.",
"anchor": 0
},
"opponent_play": {
"sprites": [
"o_toss",
"o_smash",
"o_swing1",
"o_swing2"
],
"subject": "Four frames of the same tennis opponent in a RED shirt and dark shorts, drawn as coarse low-resolution pixel art: tossing the ball up for a serve; the smash at full stretch with the racket overhead; and two frames of a forehand swing.",
"anchor": 0
},
"opponent_run": {
"sprites": [
"o_run1",
"o_run2"
],
"subject": "Two frames of the same tennis opponent in a RED shirt and dark shorts running across the court towards the camera, two stages of one stride, drawn as coarse low-resolution pixel art.",
"anchor": 0
},
"ump_boy": {
"sprites": [
"x_ump",
"x_boyrun1",
"x_boyrun2"
],
"subject": "Three frames drawn as coarse low-resolution pixel art: a tennis UMPIRE in a white sun hat, blazer and tie, seated upright with a clipboard; and two frames of a young BALL BOY in a white polo shirt and shorts sprinting, two stages of one stride. Keep the umpire seated and the ball boy running.",
"anchor": 0
},
"boy_carry": {
"sprites": [
"x_boycarry1",
"x_boycarry2",
"x_boycarry3",
"x_boycarry4"
],
"subject": "Four frames of the same young ball boy in a white polo shirt and shorts walking with a mop shouldered, a four-frame walk cycle, drawn as coarse low-resolution pixel art.",
"anchor": 0
},
"boy_mop": {
"sprites": [
"x_boymop1",
"x_boymop2",
"x_boymop3",
"x_boymop4"
],
"subject": "Four frames of the same young ball boy in a white polo shirt and shorts pushing a mop across the ground, a four-frame mopping cycle, bent over the mop handle, drawn as coarse low-resolution pixel art.",
"anchor": 0
},
"brute_wounds_a": {
"sprites": [
"z_gw",
"z_gw2"
],
"anchor": 0,
"subject": "Two damage stages of the same enormous fat zombie brute in a stained shirt, drawn as coarse low-resolution pixel art: the first has one arm torn off at the shoulder leaving a ragged stump and still drags a rusty pole; the second has BOTH arms gone. Keep the injuries exactly as they are."
},
"brute_wounds_c": {
"sprites": [
"z_gw3",
"z_gw4"
],
"anchor": 0,
"subject": "Two damage stages of the same enormous fat zombie brute in a stained shirt, drawn as coarse low-resolution pixel art: the first has his chest and belly torn wide open with ribs showing; the second is HEADLESS with the ribs out, still standing. Keep the injuries exactly as they are."
},
"shield_walk": {
"sprites": [
"z_s1",
"z_s2"
],
"anchor": 0,
"subject": "Two frames of the same zombie spectator in a grey-brown coat holding a wooden folding chair up in front of himself like a shield, shuffling forward, two stages of one stride, drawn as coarse low-resolution pixel art."
},
"shield_walk2": {
"sprites": [
"z_s3",
"z_s4"
],
"anchor": 0,
"subject": "Two more frames of the same zombie spectator in a grey-brown coat holding a wooden folding chair up in front of himself like a shield, shuffling forward, two further stages of the same stride, drawn as coarse low-resolution pixel art."
},
"shield_down": {
"sprites": [
"z_sdown"
],
"anchor": 0,
"subject": "One frame of a zombie spectator in a grey-brown coat who has just had his wooden folding chair knocked DOWN out of his hands \u2014 the chair is falling beside him and he is staggering with his arms up \u2014 drawn as coarse low-resolution pixel art."
},
"boy_run_2": {
"sprites": [
"x_boyrun3",
"x_boyrun4"
],
"anchor": 0,
"subject": "Two frames of the same young ball boy in a white polo shirt and shorts sprinting across a tennis court, two further stages of the same run cycle, drawn as coarse low-resolution pixel art."
},
"player_back": {
"sprites": [
"p_idle1",
"p_idle2",
"p_bounce",
"p_bounce2"
],
"ref_only": [
"p_run1"
],
"anchor": 0,
"check": "same",
"subject": "The reference is one tennis player in a white shirt, blue shorts and a white headband, seen FROM BEHIND. Draw FOUR frames of that same player, same size, same style. Every figure is drawn strictly FROM BEHIND like the reference: the back of his head and the back of his shirt face you, NO face, NO eyes, NO nose, NO mouth anywhere in the image. The four frames: (1) standing waiting, feet apart, racket held down at his right side; (2) the same stance with his weight shifted to the other foot; (3) bouncing a ball on the ground beside his right foot, leaning slightly down; (4) the same bounce a moment later, the ball higher. Output exactly four figures in one row."
},
"runner_front": {
"sprites": [
"z_f1",
"z_f2",
"z_f3",
"z_f4"
],
"ref_only": [
"z_c1"
],
"anchor": 0,
"check": "same",
"subject": "The reference is one zombie drawn FACING THE VIEWER. Draw FOUR frames of a DIFFERENT zombie in that same front-facing style, same size: a lean SPRINTING zombie in a torn grey-green tracksuit, charging straight down the court AT THE VIEWER. In every frame his chest and face point at you and he is coming towards you, foreshortened \u2014 one knee driven up high towards the camera, the other leg trailing far behind, arms pumping across his chest, body leaning into the run. The four frames are four stages of that charge: left knee up, mid-air stretch, right knee up, mid-air stretch the other way. He must NEVER be running sideways across the frame. Torn tracksuit, exposed ribs, green skin, sunken eyes and teeth. Output exactly four figures in one row."
},
"brute_all_wounds": {
"sprites": [
"z_gw",
"z_gwb",
"z_gw2",
"z_gw2b"
],
"anchor": 0,
"subject": "Four frames of the same enormous fat zombie brute, drawn as coarse low-resolution pixel art. The FIRST TWO are the same damage stage \u2014 his LEFT arm torn off at the shoulder leaving a ragged bloody stump, the right arm dragging a rusty pole \u2014 shown as two stages of one stride. The LAST TWO are the next damage stage, BOTH arms gone at the shoulders, again two stages of one stride. Within each pair the injuries must be IDENTICAL and on the same side of the body: only the legs change.",
"check": "same"
},
"brute_all_wounds2": {
"sprites": [
"z_gw3",
"z_gw3b",
"z_gw4",
"z_gw4b"
],
"anchor": 0,
"subject": "Four frames of the same enormous fat zombie brute, drawn as coarse low-resolution pixel art. The FIRST TWO are the same damage stage \u2014 chest and belly torn wide open with the ribs showing \u2014 as two stages of one stride. The LAST TWO are the final stage, HEADLESS with the ribs out, still standing, again two stages of one stride. Within each pair the injuries must be IDENTICAL and in the same place: only the legs change.",
"check": "same"
},
"player_swing": {
"sprites": [
"p_swing1",
"p_swing2"
],
"ref": "p_idle1",
"anchor": 0,
"check": "same",
"subject": "Three frames of the same tennis player in a white shirt, blue shorts and a white headband, seen from BEHIND, drawn as coarse low-resolution pixel art. First a plain standing frame for scale, then two frames of a forehand swing \u2014 racket drawn back across the body, then followed through. In both he faces AWAY from the camera, up the court \u2014 the back of his head towards you, no face."
},
"brute_w1": {
"sprites": [
"z_gw",
"z_gwb"
],
"ref_only": [
"z_gw"
],
"anchor": 0,
"check": "loose",
"scale": 1.5,
"subject": "The reference is one enormous fat zombie brute whose arm on the RIGHT-HAND SIDE OF THE IMAGE is torn off at the shoulder, leaving a ragged bloody stump, while the arm on the LEFT-HAND SIDE of the image is whole and drags a rusty pole. Draw TWO frames of that exact body as two stages of one stride. In BOTH frames the bloody stump must stay on the RIGHT-HAND SIDE OF THE IMAGE and the pole-dragging arm on the LEFT \u2014 do not mirror the body. Only the legs and the lean change: first left leg forward, then legs swapped with the weight sunk lower. Output exactly two figures in one row."
},
"brute_w2": {
"sprites": [
"z_gw2",
"z_gw2b"
],
"ref_only": [
"z_gw2"
],
"anchor": 0,
"check": "same",
"subject": "The reference is one enormous fat zombie brute with BOTH arms torn off at the shoulders, two ragged bloody stumps. Draw TWO frames of that EXACT same body \u2014 same injury, on the SAME side, same clothes, same size, same facing \u2014 as two stages of one stride: the first with his left leg forward and right leg back, the second with the legs swapped and his weight sunk lower. The injury must be pixel-identical in both frames; ONLY the legs and the lean change. Output exactly two figures in one row."
},
"brute_w3": {
"sprites": [
"z_gw3",
"z_gw3b"
],
"ref_only": [
"z_gw3"
],
"anchor": 0,
"check": "same",
"subject": "The reference is one enormous fat zombie brute with his chest and belly torn wide open with the ribs showing through. Draw TWO frames of that EXACT same body \u2014 same injury, on the SAME side, same clothes, same size, same facing \u2014 as two stages of one stride: the first with his left leg forward and right leg back, the second with the legs swapped and his weight sunk lower. The injury must be pixel-identical in both frames; ONLY the legs and the lean change. Output exactly two figures in one row."
},
"brute_w4": {
"sprites": [
"z_gw4",
"z_gw4b"
],
"ref_only": [
"z_gw4"
],
"anchor": 0,
"check": "loose",
"subject": "Draw TWO frames of an enormous fat zombie brute that has NO HEAD AT ALL \u2014 the neck ends in a ragged red stump at shoulder height, there is no skull, no face, no jaw, nothing above the shoulders \u2014 and whose chest is torn open with the ribs showing. Same body, same clothes and same size as the reference, as two stages of one stride: first left leg forward, then legs swapped with the weight lower. The missing head and the chest wound must be identical in both frames; only the legs change. Output exactly two figures in one row."
},
"player_stand": {
"sprites": [
"p_idle1",
"p_idle2"
],
"ref_only": [
"p_run1"
],
"anchor": 0,
"check": "same",
"subject": "The reference is one tennis player in a white shirt, blue shorts and a white headband, seen FROM BEHIND. Draw TWO frames of that same player, same size, same style. Drawn strictly FROM BEHIND like the reference: the back of his head and the back of his shirt face you, NO face, NO eyes, NO nose, NO mouth anywhere in the image. Frame 1: standing waiting, feet apart, a racket held down at his right side. Frame 2: the same stance with his weight shifted onto the other foot and the racket swung slightly across. Output exactly two figures in one row, far apart."
},
"player_bounce": {
"sprites": [
"p_bounce",
"p_bounce2"
],
"ref_only": [
"p_run1"
],
"anchor": 0,
"check": "same",
"subject": "The reference is one tennis player in a white shirt, blue shorts and a white headband seen FROM BEHIND. Draw TWO frames of that same player getting ready to serve, same size, same style, seen strictly FROM BEHIND \u2014 back of his head towards you, NO face. He holds the racket in his LEFT hand, down at his left side, which is the LEFT-HAND SIDE OF THE IMAGE. His RIGHT hand is empty and reaches down and out to the RIGHT-HAND SIDE OF THE IMAGE, palm down, as if he has just bounced a ball on the ground there. Frame 1: he leans forward slightly, right hand low near the ground. Frame 2: he straightens a little, right hand risen to about knee height. Draw NO ball in either frame \u2014 the ball is added by the game. Output exactly two figures in one row, far apart."
},
"brute_stages": {
"sprites": [
"z_gw",
"z_gw2",
"z_gw3",
"z_gw4"
],
"ref_only": [
"z_g1"
],
"anchor": 0,
"check": "loose",
"scale": 1.5,
"subject": "The reference is an enormous fat zombie brute \u2014 huge belly, stained shirt, brown trousers, dragging a rusty pole. Draw FOUR frames of that SAME body at that SAME size and bulk, standing, as four stages of damage: (1) the arm on the right-hand side of the image torn off at the shoulder leaving a ragged bloody stump, the other arm still dragging the pole; (2) BOTH arms torn off at the shoulders, two bloody stumps, no pole; (3) both arms gone and the chest and belly torn wide open with ribs showing through; (4) all of that AND no head at all \u2014 the neck ends in a ragged red stump at shoulder height, no skull, no face. He must stay the same enormous size and shape in all four \u2014 do not slim him down as the damage grows. Output exactly four figures in one row, evenly spaced."
},
"brute_s12": {
"sprites": [
"z_gw",
"z_gw2"
],
"ref_only": [
"z_g1"
],
"anchor": 0,
"check": "loose",
"scale": 1.4,
"subject": "The reference is an enormous fat zombie brute \u2014 huge belly, stained shirt, brown trousers. Draw TWO frames of that SAME body at that SAME enormous size and bulk, standing, as two stages of damage: (1) the arm on the right-hand side of the image torn off at the shoulder leaving a ragged bloody stump, the other arm still dragging a rusty pole; (2) BOTH arms torn off at the shoulders, two bloody stumps, no pole. Do not slim him down. Output exactly two figures in one row, far apart."
},
"brute_s34": {
"sprites": [
"z_gw3",
"z_gw4"
],
"ref_only": [
"z_g1"
],
"anchor": 0,
"check": "loose",
"scale": 1.4,
"subject": "The reference is an enormous fat zombie brute \u2014 huge belly, stained shirt, brown trousers. Draw TWO frames of that SAME body at that SAME enormous size and bulk, standing, as two stages of damage: (1) both arms gone and the chest and belly torn wide open with the ribs showing through; (2) all of that AND no head at all \u2014 the neck ends in a ragged red stump at shoulder height, no skull, no face, nothing above the shoulders. Do not slim him down. Output exactly two figures in one row, far apart."
},
"player_pull": {
"sprites": [
"p_pull0",
"p_pull1",
"p_pull2",
"p_pull3",
"p_pull4"
],
"ref_only": [
"p_idle1",
"p_toss",
"p_smash"
],
"anchor": 0,
"check": "same",
"subject": "The reference shows the SAME tennis player three times \u2014 white shirt, blue shorts, white headband, seen FROM BEHIND: standing, tossing, and smashing. Draw FIVE frames of that same player, same size, same style, same BRIGHT WHITE shirt and BRIGHT BLUE shorts \u2014 do not wash the colours out. Every figure is seen strictly FROM BEHIND, the back of his head towards you, NO face, NO eyes, NO nose. Frame 1 is him simply STANDING upright and relaxed, arms down, racket in his right hand \u2014 a plain standing frame. Frames 2 to 5 are four stages of ONE continuous serve wind-up and each must be clearly further into it than the one before. Through all four his LEFT arm stays thrown up high above him where he has just released the ball, and NO BALL is drawn anywhere \u2014 the game draws the ball. Frame 2: standing tall, the racket held UP ABOVE HIS HEAD in his right hand, body straight. Frame 3: the racket has dropped behind his head, his back beginning to arch backwards, knees slightly bent. Frame 4: the racket is down behind his back, his back clearly arched backwards, chest pushed up and out, knees bent. Frame 5: fully coiled \u2014 the racket hangs low behind his back near his waist, his back arched hard backwards, both knees deeply bent, weight back on his heels, ready to explode upwards. Output exactly five figures in one row, evenly spaced and far apart."
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

+3 -1
View File
@@ -333,7 +333,9 @@ 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.
export const BOUNCE_IDLE = { dx: 0.5, apex: 0.55, tempo: 3.6 };
// 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 };
// 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
+1 -1
View File
@@ -97,6 +97,6 @@
<p id="build"></p>
</div>
</div>
<script type="module" src="main.js?v=76"></script>
<script type="module" src="main.js?v=77"></script>
</body>
</html>
+15 -5
View File
@@ -3,7 +3,7 @@
import { cv, ctx } from './dom.js';
import { TUNE, UI, EATEN_GRAB, EATEN_BURST, EATEN_SUMMARY, EATEN_RESTART, COURT, PLAYER_H, BOY_H, UMP_H, UMP_X, MACH_H, BOSS_ART, BOUNCE_IDLE, SPRITE_H } from './config.js';
import { P, O, ball, score, crowd, G } from './state.js';
import { predictLanding, flickTarget } from './gameplay.js';
import { predictLanding, flickTarget, pullFrac } from './gameplay.js';
import { zombies, gore, piles, parts, pellets, extras, shots } from './zombies.js';
// ---------------------------------------------------------------- palette ----
@@ -155,9 +155,10 @@ export function proj(x, y, z) {
// ---------------------------------------------------------------- sprites ----
// assets/<name>.png; game falls back to code-drawn figures until sprites load.
const SPRV = 69; // asset cache-buster — bump with main.js?v=N (see the img.src note below)
const SPRV = 70; // asset cache-buster — bump with main.js?v=N (see the img.src note below)
const SPR = {};
for (const n of ['p_idle1','p_idle2','p_bounce','p_bounce2','p_toss','p_smash','p_run1','p_run2','p_run3','p_run4','p_swing1','p_swing2','p_die1','p_die2','p_die3','p_die4','p_die5',
'p_pull1','p_pull2','p_pull3','p_pull4', // the serve WIND-UP, one frame per quarter of the pull (see drawChar)
'o_idle1','o_idle2','o_bounce','o_bounce2','o_toss','o_smash','o_run1','o_run2','o_swing1','o_swing2',
'x_ump','x_boyrun1','x_boyrun2','x_boyrun3','x_boyrun4','x_crowd','x_crowd2','x_ads',
'x_boymop1','x_boymop2','x_boymop3','x_boymop4', // the push cycle, used only while he is stopped and working
@@ -478,10 +479,19 @@ function drawChar(ch) {
const pre = ch.side === 'P' ? 'p_' : 'o_';
let name = pre + (Math.floor(G.idleT * 1.6) % 2 ? 'idle1' : 'idle2'); // idle weight shift
if (G.phase === 'pretoss' && ch.side === score.server && ch.pose === 'idle') {
const w = G.idleT * BOUNCE_IDLE.tempo; // same phase as the bouncing ball
const rising = Math.sin(w) * Math.cos(w) > 0;
name = pre + (rising ? 'bounce2' : 'bounce'); // hand up while the ball rises, pat down exactly at the apex
// The two bounce frames are ONE drawing whose free hand sits 0.10 m apart (p_bounce2 is sheared
// out of p_bounce, see assets/_bendarm.py) — generated as a pair they came back as two different
// men and the swap read as a flicker. So the swap can follow the BALL: hand up when the ball is
// up at it, hand down when the ball is near the ground. BOUNCE_IDLE.apex is measured off the
// raised hand, so the ball meets the palm rather than passing it.
name = pre + (Math.abs(Math.sin(G.idleT * BOUNCE_IDLE.tempo)) > 0.5 ? 'bounce2' : 'bounce');
}
// THE PULL IS THE WIND-UP. While the slingshot is drawn, the frame is chosen by how far back the
// finger has come: racket above the head -> behind the head -> behind the back -> fully coiled.
// Four drawn stages over pullFrac (the same 0..1 that sets the shot's power), so the gesture and
// the man are the same movement, and the release snaps to p_smash.
if (G.aim && G.aim.mag > 0 && ch.side === 'P' && score.server === 'P' && ch.pose === 'idle')
name = 'p_pull' + (1 + Math.min(3, Math.floor(pullFrac(G.aim.mag) * 4)));
// Four-frame run where the frames exist (the player), two where they don't (the opponent). Same
// CYCLE rate either way — more frames subdivide the stride, they do not speed it up, exactly as
// ZFRAMES does for the shamblers.