Commit Graph
22 Commits
Author SHA1 Message Date
wuwiandClaude Opus 5 4a066e5cc6 Add chapter bosses and a second damage channel; demote the racket zombie
Steps 4 and 5 of the late-game depth plan.

The ranged channel. A rogue ball machine gets wheeled onto the far court every
four games: it never walks, never crosses your baseline and never scores, so
it is a standing threat you choose when to deal with — the target-priority
decision the game was missing. Its shot is a real arc solved like a serve, not
a flat bolt, so it reads coming in and the swing window has something to meet.
Three outcomes: a swing inside the volley window returns it as your own ball
flying up-court (an extras clone, so "a parry is a weapon" cost nothing to
build), the panic swish bats it away, and letting it reach you costs blood.
The swish path is load-bearing, not politeness: in pretoss the player is
rooted and cannot swing at all.

Contact still kills outright — that stays a separate channel, and killPlayer()
is now the single death path for both causes so the run summary and the best-run
write cannot drift. Health is drawn on the man: procedural spatter at fixed
anchors, a live trickle, and a red wash from the screen edges. No bar. Healing
is winning a duel, which finally gives the duel a reason to be wanted.

Chapter bosses every ten games, with their own sheet — a colossal zombie
groundskeeper hauling a ball hopper, coming apart through arm-off, ribs-out and
headless-still-throwing. Chapter 1 is 10 hp and 2.7 m, chapter 4 is 34 hp and
3.67 m. It outranks the set and duel beats at a clear, because a boss walking
into a standing wave is unreadable. And it fixes the economy: a chapter-4 boss
costs 12 bombs or 100 rounds or 34 side balls against a per-type cap of 9, so
no single weapon can take one and a banked mixed arsenal is mandatory.

The racket zombie is demoted to an occasional opponent, as asked — appearing
every won game is exactly why he read as the boss.

Five silent bugs found on the way, all in HANDOFF: a blast deleted a 34 hp boss
outright (isBoss() exists to stop that class recurring), the machine's hp did
nothing because it fell through to the one-shot fallback, the boss both scored
and fired its card, the machine gated the hand-clear, and a boss died with a
shambler-sized gore burst.

Tennis untouched: 30 tapped points still 50 hits / 0 whiffs. 45-game soak clean
with all four chapters firing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 12:55:35 +02:00
wuwiandClaude Opus 5 ffac9b5512 Make every weapon hurt the boss, drop his HP bar, un-cap the director
Steps 1-3 of the late-game depth plan, after Oliver's 50+ game playtest.

"Sometimes the racket zombie doesn't seem harmable" was real, not a feeling.
Three of the five rack slots did nothing at all to him and said nothing about
it: TRIPLE side balls skipped him, GUN and horde pellets skipped him, fire
couldn't ignite him. BOMB meanwhile deleted him outright ignoring his hp. All
of it now routes through one wound() with a TUNE.dmg* table, so damage is
proportional to the body and a future 20 hp boss needs no new code. Fractional
damage banks on zb.chip so whole points only come off hp — the wound skins
switch on hp thresholds. His contact radius goes to 0.7 m, because part of
"unharmable" was just missing him. Melee still skips him and that's correct:
he's on the far side of the net and melee can't span it.

The HP bar is gone. It was the only floating UI on a body, it was on screen
for nearly the whole run, and nobody knew what it meant — while the wound
skins already say the same thing in the language every other multi-hit enemy
uses. A one-per-run modal explains what he is instead. Skins and bleed anchors
now read a fraction of hpMax, since his hp scales with the run.

And the plateau: every difficulty dial bottomed out by about game 12, so games
12-50 were arithmetically identical. Body cap, spawn gap, shamble speed,
variant mix, boss hp and horde size all keep climbing now, calibrated to track
the old curve through game 12 rather than replace it. One deliberate exception:
shamble speed was time-based and maxed during game 2, so the early game is
genuinely gentler now — zSpeedPerGame if that reads flabby.

HANDOFF carries the full spec for steps 4 and 5 (ranged attacks with player
damage, then chapter bosses) including the decisions we settled, so the next
session can pick it up cold.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 12:30:27 +02:00
wuwiandClaude Opus 5 4cf54603f3 Fly the modals in with a swoosh instead of cutting to them
m.in (0..1 over TUNE.modalIn, 0.34 s) is the entrance clock, ticked inside
update()'s modal early-return — the one thing that animates while the world
is frozen. Icon and title fly in from the right trailing two ghost copies as
a speed streak, the body lines cascade in from the left one after another,
everything on easeOutBack so it overshoots and settles rather than
decelerating politely into place. The dim washes in over the first third. A
descending noise sweep on arrival (swish is parameterised now — same six
lines of Web Audio, opposite reading) and a low pock as it lands.

Every x is rounded: a fractional x on a nearest-neighbour scaled image
re-snaps its pixel columns, which is the boiling that once read as HUD
flicker.

Dismissal is gated on the entrance: while m.in < 1 dismissModal swallows the
gesture but doesn't clear the modal, so a tap already in the air when a popup
appears can't skip it — and swallowing rather than returning false is what
keeps that tap from falling through into a serve.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 11:23:30 +02:00
wuwiandClaude Opus 5 79fb43f60d Stop the dead for the changeover instead of letting the player wander
Oliver vetoed the free movement between points on sight: he never walks
freely during play, so being able to only between points reads as the game
breaking, not as a fix. Reverted to 'rally' only, and the rule is written into
the settled-feel list so it doesn't get re-invented.

The frozen changeover is answered his other way instead — the dead wait too.
`held` in updateZombies stops their advance, their steering and the eat check
for the breather; they still bleed and burn, they just stand there, because
the umpire called time and it is still a tennis match. The path where this
actually bit: losing a duel scheduled on a changeover beat spawns five fresh
shamblers and THEN starts five seconds you have no agency in.

The stuck pose was never about movement — 'run' is the one pose expirePose
doesn't expire, and runT stops advancing outside the block, so a beat that
began mid-stride welded him to a single frame. Reset it and it's fixed for
every phase.

And he could walk off the screen in ordinary play, not just during the beat I
had added: P_BOUNDS.x is 6.5 m, portrait shows about ±4 m at the baseline.
The xVis clamp already existed but only applied during auto-fire; it now
always applies, floored at the sideline so every legal ball stays catchable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 10:07:38 +02:00
wuwiandClaude Opus 5 e00886f57e Let the player walk between points, and fix seven retest gripes
All eight issues from Oliver's 35-game retest.

The freeze (1 and 6 — one root cause): the movement block, pose update
included, lived inside `if (G.phase === 'rally')`, so every 'over' beat and
every pretoss stranded the player mid-animation while the zombies walked on.
It now also runs whenever splatter is in any phase but 'toss' — 'toss' is the
aim, and the thumb is busy. Tennis keeps its old gating.

The rest:
- the mop boy is waved off the sudden-death duel and barred for its duration
- triple-serve clones inherit the serve's topspin gravity, so the wings stop
  floating while the centre ball dives
- "Time." belongs to the changeover's own countdown, not to whatever beat
  happened to expire while the breather ran
- the tutorial illustration is a finger pressing and pulling back now, not a
  curved arrow that read as a scroll
- aiming holds the pretoss stance; the smash plays when the shot fires
- GAME N sits on the scoreboard permanently

HANDOFF records all eight as closed with what the fix was, plus two ways a
soak rig lies to you and why the CPU-return benchmark can't be reproduced
from the number alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 09:33:37 +02:00
wuwiandClaude Opus 5 8dca9b6d45 Delete PLAN.md — HANDOFF is the only planning document
Everything it specified shipped long ago, and the parts still worth knowing are
already folded into HANDOFF. Keeping a stale plan around only invites a future
session to treat it as live spec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 09:02:51 +02:00
wuwiandClaude Opus 5 01ee068e6d HANDOFF is the source of truth; record the 35-game retest findings
Rewrote HANDOFF.md around what a next session actually needs: what the game is,
how it flows, the invariants you have to know before touching anything, the
settled feel decisions, the regression net with its real numbers, the asset
pipeline with the generator's known failure modes, and the hard-won gotchas.
Five rounds of chronological "feedback round" sections are folded into that
structure — the history is in the git log and the memory file, not here.

Deleted POLISH-PLAN.md: all fourteen items shipped. PLAN.md is left in place but
marked historical at the top of HANDOFF.

Corrected two stale facts carried in the old handoff: RES is 3 (not 2, which
would have thrown off the umpire-cheat coordinates) and UMP_X is 6.05 (not 5.6).

Added an OPEN ISSUES section with Oliver's eight findings from the 35-game
retest, each with a diagnosis from reading the code rather than just the
symptom. Two of them (player frozen between games, sprite freeze after the
horde) look like one root cause: the movement and pose block sits inside
`if (G.phase === 'rally')`, so every 'over' beat freezes the player mid-frame
while the zombies walk on. The triple-serve lob is confirmed: the wing clones
integrate at flat gravity while the real serve carries up to 2.3x from topspin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 09:00:49 +02:00
wuwiandClaude Opus 5 7dde5874ab Brighter stands, a serve hint that shows the gesture, and a boy who walks
- The crowd sat in the dark, worst of all in splatter mode where you spend the
  game. Both crowd frames get an identical gamma lift (means 30/14/39 ->
  42/22/56) and the rot tint drops 0.5 -> 0.26. The curve MUST stay identical
  across the two frames or the clap swap flashes brightness; _sheet_crowd1.png
  keeps the pre-lift original.
- The splatter hint led with the tap when pulling is the whole gesture. It now
  reads PULL BACK TO AIM - RELEASE TO FIRE, and after 1.5s of sitting in the
  serve stance untouched a ghost thumb slides back while chevrons grow up-court
  on a loop. Pull back, shoot forward is an inverse relationship that a line of
  text cannot carry on its own.
- The mop boy has three sheets instead of two: the push cycle now plays ONLY
  while he is stopped and working, a new carry sheet walks him upright with the
  mop shouldered, and the run sheet stays for the tennis-mode ball fetch. He was
  mopping thin air the entire way to the stain.

Sheet note: frames 2 and 3 of the carry sheet merged (a mop handle bridging the
gap), so it is cut at the true column gaps plus the thinnest column inside the
merged pair, then common-scaled — same technique as the brute stride sheet.

26-game soak clean. Serve returns 133/150 = 88.7% +/-2.6%, 30 human-tapped
points = 50 hits / 0 whiffs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 10:34:43 +03:00
wuwiandClaude Opus 5 21b9dd53fa Thumb-scale draw meter, looser aim magnet, a bomb worth the tin
- The draw meter is a fixed bar across the bottom, ~90% of the width and 10px
  tall, with the kill notch on it. It was a 26x4 stub floating on the apron by
  the player's feet, which is nothing under a thumb at arm's length. The serve
  hint is suppressed while pulling since they share the slot. pullFull 95 -> 130:
  at 95 every pull maxed out by accident, so the meter never meant anything.
- The aim magnet took the nearest body ALONG the lane, so a close shambler a
  little off-axis stole shots clearly aimed past it. It now picks the best
  ALIGNED body, with TUNE.aimTie of slack in which a nearer one still wins — a
  straight-ahead lane still eats the nearest body first, which is the settled
  feel. aimTie: 1 restores the old behaviour exactly, for A/B.
- Bomb radius 2.5 -> 4.2 (2 kills -> 5 in a scattered ten-wave), a 120-particle
  eruption, a ring of thrown stains and a full-size pile. The blinding white
  frame is now a brief warm bloom — it was hiding the very gore it announced.
- Rack tiles 22 -> 30 tall, and the touch band is padded above and runs to the
  screen edge below. At 22 logical px they were ~30 CSS px on a phone, well under
  the ~44 a thumb expects; now ~62.

26-game soak clean with heavy bomb use, arrays stay bounded. Serve returns
136/150 = 90.7% +/-2.4%, 30 human-tapped points = 50 hits / 0 whiffs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 09:51:24 +03:00
wuwiandClaude Opus 5 55ddf5932e Fix the serve deadlock, give the mop boy a job, align net and umpire
The freeze first: G.mgT only counted down inside `G.phase === 'rally'`, so
anything that moved the phase mid-burst — a scheduled duel calling setupPoint —
stranded it above zero forever. mgT > 0 is one of keyPressed's early returns, so
no tap could serve again, and the yellow shape over the player's head was
drawMgRacket's muzzle flash still being drawn. Fixed at the root (the clock ticks
in every phase, only the pellets are gated on rally) and semantically: starting a
duel cancels any burst, because the duel is tennis.

The mop boy now does an actual round: walk to the biggest nearby stain, stand on
it and push the mop while the cycle animates in place, scrub it out, move to the
next, then leave. Gore fades ~3.5x faster while he is stopped and working than
when walking past, so the stain dies under him instead of fading as he drifts by.
boyMaxJobs caps the round so a kill streak can't employ him forever.

Applause: the clip is 4.16s and always plays in full, but every cheer() passed a
shorter beat, so the stand sat down while it was still audible. Floored at the
clip length. New, much bolder clap frame — the re-roll came back brighter and
bluer, which would have flashed the whole stand, so it is colour-matched to frame
one per channel on mean and sd before quantising.

Net and umpire were both wrong since forever: netW was the singles-stick width
(10.06) against a court drawn to the doubles lines, so the net stopped 0.46m
short each side; the chair's near legs stood 0.45m inside the sideline. Both now
sit exactly on the doubles line.

26-game soak clean, no stalls. Serve returns 134/150 = 89.3% +/-2.5% with the
wider net (the bar is inside one sigma), 30 human-tapped points = 50 hits /
0 whiffs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 18:09:18 +03:00
wuwiandClaude Opus 5 32401366fe 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>
2026-07-27 10:20:43 +03:00
wuwiandClaude Opus 5 ff77922e85 Feedback round: fire actually spreads, brutes lurch, crowd stops flickering
- Fire never spread in play. The flame ball's full pierce wiped its own lane, so
  the bodies that would have caught were already gone. It now ignites everything
  it FLIES PAST (TUNE.burnBallR), with a wider ignition and spread radius. One
  flame serve into a scattered 10-wave kills 9, nine burning at once.
- Wounded brutes slid along like decals: every wound stage is a single frame and
  the brute now spends most of its life wounded. They get a procedural lumber
  instead of eight more sprites — translate + squash, no rotation, so the pixels
  stay on the grid, and the amplitudes scale with the projection or the whole
  thing goes sub-pixel at range.
- The "scoreboard flicker" was the crowd: the cheer bounce was per-column
  Math.random() every frame, i.e. TV static, showing through the scoreboard art's
  transparent ornate edges. Now a smooth per-column sine on idleT.
- Ball boys: mopping is the default in splatter mode now, and the boy is half
  again as tall — at 1.2m he read as a distant speck. The running fetch-boy
  survives for tennis, where there is nothing on the grass but a ball.
- Changeover 7s -> 5s, with the mop crossing timed to fill it.
- Save-to-homescreen: a bloody tennis ball, wired up as apple-touch-icon +
  favicon + webmanifest. The Dockerfile now ships the manifest and gives it its
  own MIME type via an exact-match location (a bare `types {}` block would have
  replaced nginx's whole map and broken .js and .png with it).

Benchmarks re-run: CPU returns 67/72 serves (93.1%, above the 11/12 bar — a
20/24 sample in between was flub noise), 30 human-tapped points = 50 hits /
0 whiffs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 05:58:18 +03:00
wuwiandClaude Opus 5 a09a5d7559 Polish round: modal system, softened palette, pixel font, and the 14 plan items
The three foundations first, since everything else leans on them:

- G.modal is the only thing that stops the world. update() early-returns on it
  so no zombie steps, no ball flight, no spawn timers run while a popup is on
  screen — you can read it without being eaten. Dismissal swallows the gesture;
  the pointerdown dismisser is registered only on pointer:fine, because on touch
  pointerdown beats touchstart and a shared handler cleared the modal and then
  served with the same thumb.
- UI in config.js is the accent palette. The arcade primaries were shouting over
  the artwork; the gore reds deliberately stay loud.
- Every canvas font string now goes through F(). Bare `monospace` resolved to
  Courier — a slab serif — which was the "generic serif" bug, not taste.

Then: weapons are only ever WON. deliverTin() has exactly one caller now
(resolveInterlude('P')); the old game schedule calls the DUEL instead, fired off
proceedAfterClear() where the court is guaranteed empty. Four popups (splatter
tutorial with a generated illustration, first duel, weapon received, horde),
wCap 9 with the tin amounts tunable, a 5-hp brute that comes apart over four
wound skins, fire that ignites the neighbours and chains outward, a three-stage
death where you become the biggest gore mound on the court, and a 7s changeover
with a ball boy who finally has a mop.

New art: assets/logo.png, x_howto, z_gw2-4, x_boymop1-4, plus assets/_key.py for
standalone art (chroma-key + crop, no frame splitting, no forced sprite height).

Benchmarks re-run and holding: CPU returns 23/24 serves, 30 human-tapped points
= 50 hits / 0 whiffs, 26 simulated games with no exceptions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 05:28:04 +03:00
wuwiandClaude Fable 5 5d815a4fc8 Docs: record the three feedback rounds, decisions, and verification gotchas
PLAN.md: two-stage aiming, runner-glass, fps-60, and no-engine decisions added
to the don't-re-litigate list; PWA/App Store noted as a next-arc candidate.
HANDOFF.md: status covers the post-plan rounds; gotchas gain the synthetic-touch,
1/60-stepping, port-hold, and zombies[]-mutation lessons.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 18:33:50 +02:00
wuwiandClaude Fable 5 2f1419b1f2 Feedback round + Phase 13: the duel, free-aim, honest stakes
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>
2026-07-12 11:50:18 +02:00
wuwiandClaude Fable 5 811fe3f91f Phase 10: sprite session — runner, brute, shield zombie, weapon tin
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>
2026-07-12 10:19:21 +02:00
wuwiandClaude Fable 5 f512709074 HANDOFF: phases 7-9 shipped, next is the sprite session
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 10:00:47 +02:00
wuwiandClaude Fable 5 25db84ed87 Plan v2: match structure, enemies, weapons, interlude (phases 7-13)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 09:54:01 +02:00
wuwiandClaude Fable 5 fe5532b558 PLAN.md: phased zombie-mode expansion plan for follow-up implementation
Five phases, each independently shippable with verify recipes: tighter
shooting loop, multi-kill umpire fanfare, procedural gore piles, bomb
balls delivered by ball boys, horde waves with machine-gun racket.
Racket zombies deferred to a design checkpoint (Phase 6, not first cut).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 21:37:04 +02:00
wuwiandClaude Fable 5 775d465f23 HANDOFF: tennis feel confirmed good; record benchmarks and cache gotcha
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 21:23:19 +02:00
wuwiandClaude Fable 5 943b7da9fd Zombies get faster over time; when one reaches you, you're eaten
splatT now ticks while in splatter mode and scales spawn speed by
1 + min(1.5, splatT/70). A zombie within 0.7m of the player ends the
run: gore burst, 'game, set, massacre', dark EATEN. overlay with final
games and a tap-to-restart (gated 0.7s so death-spam doesn't skip it).
Zombies keep shambling on the end screen but no longer score. Also
verified the two prior unverified items: quick-serve cadence (0.25s
between balls, flick = instant aimed serve) and flick aiming across
the net (far/near lane selection works as designed). HANDOFF updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 19:54:09 +02:00
wuwiandClaude Fable 5 e7aab295d4 WIP splatter feedback round + HANDOFF.md for session continuation
Done (unverified): faster serve cadence in splatter (overT 0.25, serve from
where you stand, flick = instant aimed quick serve), aim-anywhere targeting
relative to the player. Not done: zombie speed ramp (G.splatT exists but is
never incremented), eaten-on-touch death + restart screen. Full state and
plans in HANDOFF.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 19:28:00 +02:00