Tins now accumulate on a courtside rack (cap 3) instead of replacing each other; a PLAIN slot banks specials for later, and GUN is a charge you pop for an auto-fire burst. Tap the rack or press 1-5 to choose what your serve throws. A fresh delivery auto-equips but never overrides a deliberate PLAIN, and the 1:1 sudden-death duel forces plain balls.
HUD gets real art: generated pixel-art weapon icons, an ornate Wimbledon scoreboard PNG backing the live text (no more code-drawn boxes on top), and a bundled Silkscreen pixel font replacing the system serif. Rack tiles get padding, count-over-label alignment, and an infinity marker on the ball.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire the new assets/audio/*.wav samples over the synth SFX:
- Ball hits: per-side tennis-player / tennis-opponent on serve & return,
now with a stadium reverb tail (same convolver as umpire/aww).
- Crowd: tennis-applaus on your points/games (louder on big moments);
tennis-aww when the opponent scores or you're eaten (reverb tail).
- Ambient bed: tennis-ambient-loop from first tap, 500Hz lowcut, low vol.
- Zombie music: zombie-mode loop when splatter kicks in (800Hz lowcut),
fades out for the 1:1 sudden-death duel and back in after.
- Headshots: random headshot-1/-2 on every zombie kill (killZombie).
audio.js: sfx() one-shot player w/ rate+reverb opts, loopBed() beds with
duck/fade, aww() wrapper, hotter reverb send on the umpire voice.
Also: normal opponent aiFlub 0.09 -> 0.14 (a little easier to beat).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Umpire chair moved courtside (UMP_X 5.6, was 7.1 — which projected to
x=272 on a 270px-wide portrait canvas: literally one pixel off-screen).
Tap-cheat coords follow the constant.
- Generated pixel-art night crowd (x_crowd.png) replaces the tier stripes +
random dots; drawn in 16 columns that bounce independently while the crowd
cheers. Old procedural crowd kept as the loading fallback.
- Generated courtside sponsor boards (x_ads.png): ACEADE, TOPSPIN & CO,
VOLLEX, NETTO GRIP, SLAMWICH — tiled at native aspect so lettering is
never half-cut at any viewport width. Replaces the 'SPLATTER OPEN' text.
- One rot tint over crowd + boards driven by the shared paletteT() (new
helper), so the stadium dims into splatter and brightens back for duels.
Verified: fresh origin; tennis + splatter screenshots (boards tile cleanly,
crowd reads, tint dims, umpire on-screen beside the court); umpire triple-tap
works at the new chair position; tennis serve benchmark 33/36 across two runs
(first 12 was flub noise); no console errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- RES 2 -> 3: phone screens now DOWNSCALE the canvas (crisp pixels) instead
of upscaling it (soft). Umpire-tap coords un-hardcoded from RES=2.
- Flatter lens: camZ 36 -> 42 with camF scaled per orientation (406/580) so
the PLAYER stays the same size — far-court zombies render ~25% bigger and
finally read as individuals from the baseline.
- Smoother animation: 4-frame walk cycles for both classic shamblers and the
runner's sprint (new sheets, same style anchor; z_a1-4, z_b1-4, z_f1-4).
Same cycle frequency — frames subdivide the stride. Render falls back to
2 frames per variant until its sheet loads.
- Tiered gore (GORE_MUL): the more advanced the zombie, the more absurd the
mess — classics 1x (base bumped 24 -> 30 particles), runner 1.8x, shield
2.5x, racket boss 3x, brute 4x (120 particles, a scatter of stains, an
instant size-3 mound).
Verified: fresh origin, canvas 1440px, umpire triple-tap works at RES 3,
all 4 anim frames cycle at the old stride rate, brute kill = 120 parts /
2 stains / size-3 pile, far-court lineup screenshot clearly readable,
portrait layout numbers correct (810x1440, camF 580), tennis 11/12,
no console errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Oliver's six complaints, addressed:
1. Opponent never came — a racket zombie now spawns on EVERY game you win
(if none alive); endHorde no longer double-spawns
2. No wave-cleared feeling — sweepCourt: at game end the surviving wave
collapses, every game gets a real beat (gameBeatT 1.6 / changeover 4.5),
and 'GAME N' announces the next wave when play re-arms
3. Serve-posture lockup during machine gun — root cause: any 'over' beat
expiring into setupPoint() -> pretoss during auto-fire (no movement outside
rally, no serving during horde/GUN). The over-expiry now returns auto-fire
modes to 'rally', never the serve loop
4. Forced bomb spending — specials commit ONLY on deliberate serves: flick
(or arrow-aimed keyboard serve) fires the tin, plain tap keeps it; serve
prompt teaches it ('FLICK FIRES THE BOMB - TAP KEEPS IT')
5. Auto-aim — free-aim: the flick vector maps to a landing point (length =
depth, up to 22m), zombies within zSnap 1.4 magnet the shot, otherwise the
ball lands exactly where flicked; taps keep the old auto-target assist;
flicks now also work at the toss apex
6. Zombies walking through you — option (a): touch is honest. eatenR 0.9
(brute +0.2) matches the visual sprite overlap
Phase 13 — SUDDEN DEATH interlude: court clear + un-dueled racket zombie +
about to serve -> 'Quiet, please. Play will resume.' / 'Sudden death.' One
point under REAL tennis rules (line calls live, the boss plays every ball,
spawns paused). Win (line call, double bounce, or regicide by any weapon) =
the next tin delivered on the spot; lose (out, or its return passes you) =
a game on their scoreboard + an instant 5-shambler surge. One duel per
opponent. New lines: play_resume.m4a, sudden_death.m4a.
Verified in browser: horde 'over' beat -> rally (lockup dead); game win ->
sweep + boss + beat -> SUDDEN DEATH chains naturally; duel lose path (GAME.
THEM., surge 5, scoreboard), no re-duel, fresh-boss duel, bomb regicide ->
tin on the spot; tap serve keeps stock, real touch-flick consumes + lands
at the flicked point, magnet snaps when near; eaten at 0.85m; tennis 11/12;
no console errors. PLAN.md/HANDOFF updated: plan v2 complete.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sheets generated with the existing zombie sheet as the style anchor (edit mode);
sliced to z_f1/2 (runner sprint), z_g1/2 (brute + net-post club), z_s1/2/3
(shield zombie, chair up / chair dropped stagger), x_tin (black new-balls tin).
assets/_slice.py is now committed (chroma-key + de-fringe + segmentation) so the
pipeline survives scratchpad wipes; HANDOFF updated.
Sprites are NOT wired into render.js yet — Phase 11 does that after Oliver
approves the sheets (plan's design checkpoint).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Camera shake (G.shake, decays over 0.35s; HUD stays steady via transform reset)
- One white flash frame (G.boomFlash overlay)
- Scorch crater: near-black stain the boys mop 10x slower — the lawn scars
- Hitstop 0.1 -> 0.18
- Umpire loses composure: 'Order. Order, please.' (new order.m4a), rate-limited
so bomb chains don't spam him
Verified: explode kills cluster of 4 (MASSACRE x4 chains), shake/flash set and
decay clean, 2nd bomb order-call suppressed, 1s of mopping erases blood (0.9->0)
but not the crater (1->0.84); screenshot mid-shake; no console errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Spawn director: gap and court cap scale with games finished (runGames);
game 1 is three lonely shamblers, late runs approach zSpawnMin/zMaxCap
- Changeover after odd games: 4.5s forced breather, no spawns, no serving
(melee still works), boys mop, umpire calls 'Time.' (new time.m4a)
- 6 games = a set; set end launches the horde (replaces the splatGames>=2 trigger)
- Zombies taking a game sets shameGame (phase 9 withholds the delivery)
- Eaten = the run is over: GAME, SET, MASSACRE summary with games/sets survived
and best run (localStorage); HUD shows sets in splatter (SETS·GAMES PTS)
- spentBall() helper so dead-ball paths can't clobber a changeover's beat
- T surface: splatPoint + explode exposed for verify scripts
Verified: game-1 cap 3 held 25s; changeover blocks taps+spawns then resumes;
6 games -> set+horde -> racket zombie; runGames=6 tempo ~6 spawns/10s;
eaten writes best; tennis regression 11/12 CPU returns; no console errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A single shot that splatters ≥2 zombies now escalates: DOUBLE KILL /
TRIPLE KILL / MASSACRE ×N flash, louder crowd, and a new umpire line
(double_kill / triple_kill / unbelievable m4a, recorded say -v Daniel).
ball.kills tracks the streak, reset on every fresh shot (doHit + hitServe).
zombies.js imports cheer (now exported from gameplay). The splatter pace
cut is now TUNE.zPierce (0.75, was hardcoded 0.5) so a hot ball plows
through a lined-up lane instead of stalling on the first kill.
Verified via window.T: a low ball down a one-side lane kills 3→TRIPLE and
5→MASSACRE ×5 with escalating flash, streak resets on the next serve, the
three new lines serve 200 and match the existing m4a format, no crash when
the ball dies mid-streak, no console errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Win your first game and the applause cuts off mid-clap. The palette drains,
the umpire lowers his voice ('Quiet... please'), your opponent turns — torn
red polo, dragging his racket — and shamblers come over the billboard wall.
The scoreboard rewrites CPU to THEM. 'Play.'
- zombies.js: shamblers walk your way; quality shots splatter them (gore
decal + particle burst + wet synth splat), weak sitters only stagger them
back; one crossing your baseline is a point for THEM
- tennis scoring machinery keeps running untouched: fifteen, thirty, deuce,
and 'Game, set... massacre' on a game win, all in recorded umpire English
- ball boys mop the gore they pass (their destiny fulfilled)
- transition: palette lerp, scripted spawns incl. the turned opponent,
new recorded lines (quiet_dark, remain_seated, play, new_balls)
- moan ambience, drained splatter palette, all zombie params in TUNE
Verified: transition triggers on first game win, splat/stagger/baseline
scoring all correct, dead-ball flow feeds fresh serves, tennis mode
regression clean (6/6 returns, 6/6 points resolved).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- p_bounce/p_bounce2 re-extracted from Oliver's corrected _sheet_pbounce.png
(racket properly in the right hand)
- bounce ball offset 0.4 -> 0.32 so it sits under the reaching hand
- landing rings draw with integer-snapped center/radii and a solid 2px
stroke — the sub-pixel anti-aliasing shimmer read as flickering
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- boy runs on a proper 4-phase cycle (stride, pass, mirrored stride, pass)
- server holds the crouched bounce stance through pretoss instead of
flickering to standing idle; hand-down/hand-up frames sync with the ball,
which now bounces floor-to-hand height
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- fake topspin: quality shots dive under extra effective gravity, so real pace
(up to 28 m/s) can clear the net AND land in — without it the net-clearance
loop silently slowed every shot to a coverable lob, which is why the CPU
returned everything regardless of aim
- no grass-scraping: balls below ankle height (0.24m) are past you, both sides
- CPU: 0.45s shot-read delay, 4.3 m/s legs, half-speed recovery between shots.
Verified gradient: perfect/good corner shots are winners, center balls and
weak shots still come back
- player aimed shots go wider (3.3m vs 2.7m)
- ball boy: nano-banana mirrored stride frames (was stride-vs-hop), height
1.2m, z-ordered against umpire and CPU (no more running over the chair)
- umpire voice through a synthesized convolver reverb — center-court PA echo
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- intercept() now picks a point the runner can actually reach in time
(travel-time feasibility), falling back to the deepest hittable point
- tapping no longer slows the auto-run: anticipating a shot doesn't lose it
- assist legs slightly superhuman (1.15x) so positioning is guaranteed
- ball boy run frames regenerated with contrasting stride/gather poses
(previous pair was near-identical, which read as no animation)
Verified: 10/10 CPU shots end inside the honest 1.3m contact radius with
zero manual movement.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- ball boy redesign: spawns at the sideline on point end, sprints across at
the ball's depth with a proper side-view run animation, scoops the ball
mid-run, despawns off the other side (no more net-post idling/sliding)
- hit feel: ball snaps to the racket at the contact frame so hits visually
connect; contact radius tightened 1.7 -> 1.3 (assist does the positioning,
the tap must be honest); missed swings now play an audible swish
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 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>
- 23 pre-recorded umpire lines (macOS say, en-GB Daniel) played via WebAudio,
works on mobile; speechSynthesis stays as fallback only
- umpire chair + two ball boys (generated sprites) dress the court
- court redrawn: doubles alleys, white perimeter border, clearly darker out area
- intro overlay with control symbols; first tap dismisses it and unlocks audio
- nginx now serves everything Cache-Control: no-cache so a reload always
revalidates and picks up new deploys
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- new 5-pose sheets per character: idle weight-shift x2, ball-bounce crouch,
serve trophy pose (toss), overhead smash
- serve shows toss stance through the ball toss, smash frame on contact
- auto-assist: full player speed, adds depth tracking after the bounce,
and drives the run animation + facing like manual movement
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>