Three modes, picked up front — and CLASSIC TENNIS is one of them

The setup screen picks WHAT before WHO and WHERE. Three rows out of a table,
in the court row's own markup:

CLASSIC TENNIS is not the opener with the turn switched off and left there —
it is the mode the other two are built on, so it gets real match scoring: six
games with a two-game lead is a set, best of TUNE.classicSets is the match,
the board shows the sets column (hidden until now outside splatter) and the
bar under it says which set this is. The CPU stops rubber-banding and flubs
less: aiFlubLoss exists to get a new player PAST the opener, and a mode whose
whole promise is a real match cannot be won by an opponent that falls apart
the moment he goes ahead. No tiebreak — 6-6 plays on to 8-6. Marked ponytail.

That needed a second ENDING, the one with no corpse in it: G.done, the same
summary and gated tap as the death screen, its own words (GAME, SET, MATCH. /
BEATEN.) and the man still standing. The three endings now share one block
rather than three copies of it.

Caught by the first classic test: stats banked only the FIRST classic run of a
session. `banked` was cleared by the next KILL, and a classic match has no
kills in it at all — every later match was swallowed by the double-bank guard.
beginRun() is the honest reset and restartRun calls it.

ZOMBIE TENNIS from the menu is one line; the back room keeps its door.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-12 09:27:49 +02:00
1 parent 66fdad3d74
commit c89d4a3ff9
31 files changed
+472 -123

No files matched your search

+6 -2
View File
@@ -26,8 +26,12 @@ let run = bag();
// count lives on G and survives the flush, so a second endRun would sail past a "nothing happened"
// guard and bank a duplicate with zero kills. `pagehide` really does fire more than once — the
// bfcache dismisses and restores the same page — which is exactly how this was caught.
// Cleared by the next kill, so a fresh run re-arms itself with no reset call to forget.
// Cleared by the next kill — which was the whole of the reset until CLASSIC TENNIS arrived, a mode
// with NO KILLS IN IT AT ALL. There, nothing ever cleared the flag, so the second and every later
// match of a session was silently swallowed by the guard below. beginRun() is the honest reset and
// gameplay.restartRun (the one "a new run starts here" moment there is) calls it.
let banked = false;
export function beginRun() { run = bag(); banked = false; }
// ---------------------------------------------------------------- storage ----
// Private-mode Safari throws on both read and write. Stats are a nicety; a menu that will not open
@@ -88,7 +92,7 @@ export function multiKill() { run.mk++; }
// opponents felled and has exactly one kill per opponent, by definition, always credited to 'bomb'
// (matchGameWon's explode). Un-tagged records predate the split and ARE gore runs, which is why the
// default is 'g' everywhere and no migration is needed.
export const MODE_GORE = 'g', MODE_MATCH = 'z';
export const MODE_GORE = 'g', MODE_MATCH = 'z', MODE_CLASSIC = 'c';
const modeOf = r => r.m || MODE_GORE;
export function endRun(games, sets, secs, dead = true, mode = MODE_GORE) {