From 3cf3e029a4a825501540b5640a517c35255c49e4 Mon Sep 17 00:00:00 2001 From: Oliver Rieger Date: Sat, 15 Aug 2026 13:53:42 +0200 Subject: [PATCH] Gore opens in the gore, the theme comes back, and every mode gets a board MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Splatter gore no longer makes you win a game of tennis first — the mode you pick is the mode you get, with the man you came to play already turned. The transition itself is untouched and still reachable; it just isn't a toll booth. The menu theme could only ever be started, never brought back, so the first run of a session silenced it for good while the court's ambience followed you into the menu. Starting a bed now un-ducks it too. Three world boards instead of one: kills, opponents felled, and games taken off the CPU, each with its own name prompt and its own row per player. ...and the gore economy stops paying a barrel for every game won past game 16, while the shamblers, the mix and the ball machines keep escalating past the point they all used to stop. Co-Authored-By: Claude Opus 5 --- PROJECT.md | 99 ++++++++++++++++++++++++++++++++++++++++++++++++- index.html | 12 +++++- server.py | 74 +++++++++++++++++++++++++----------- src/audio.js | 28 ++++++++++++-- src/config.js | 38 ++++++++++++++----- src/gameplay.js | 62 ++++++++++++++++++++++++++----- src/main.js | 58 +++++++++++++++++++++-------- src/render.js | 2 +- src/state.js | 3 ++ src/stats.js | 79 +++++++++++++++++++++++++++++---------- test.js | 73 ++++++++++++++++++++++++++++++++++-- 11 files changed, 440 insertions(+), 88 deletions(-) diff --git a/PROJECT.md b/PROJECT.md index 5ae5ce0..01b614c 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -20,7 +20,9 @@ anything, so if you add a `return` for an awkward deal, check the body still run the state-machine contracts that have historically produced the show-stoppers. The Dockerfile runs it as a build stage, so a red suite cannot deploy — but finding out from Coolify is the slow way. -Last updated 2026-08-15 (the batch below: six new opponent families and an 18-rung ladder, the +Last updated 2026-08-15 (two sessions: the finetuning pass — gore opens in the gore, the menu theme +comes back, one world board per mode, and the barrel economy cut — over the batch below: six new +opponent families and an 18-rung ladder, the tournament DRAW screen, three netplay fixes off Oliver's cellular report, the night court, and camera flashes in the stand). **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 @@ -412,6 +414,101 @@ zombie (now an occasional opponent, `TUNE.racketEvery`, not the every-game fixtu `RETURNED`, `DOUBLE KILL`) is a footnote to something you already watched happen. Sharing the loud FACE with the milestone cards is right; sharing their SIZE made every footnote read as a milestone. +## Session log: gore opens in the gore, the theme comes back, three boards (2026-08-15, same day) + +Oliver's finetuning pass on the batch below. + +### THE GORE MODE STARTS IN THE GORE + +*"the splatter gore should not have the normal tennis intro anymore, we should just start right in +splatter mode."* The prologue was a game of real tennis you had to WIN before the mode you picked +would begin — a reveal the first time, a toll booth every time after. + +`gameplay.startGore()` is the direct door: it lands on the same state the transition's last beat +lands on (extracted as `enterSplatter`, so the two doors can never drift), sets `everTransitioned` +so the cinematic cannot fire behind it, and spawns the turned opponent as your first body — he is +the man you came to play and the mode's premise is that he is already one of them. **The transition +is NOT deleted**: it is still what `forceZombies` / `T.forceZombies()` plays, and it is still the +best thing in the game to look at. It just no longer stands between the menu and the mode. The mode +poster stopped promising a match that turns, because there is no longer one. + +`main.intoSplatter` — the back room's "get me into splatter" hack — now calls `startGore()` too, +which deletes its 900 hidden `update()` calls. That loop fast-forwarded the whole eight-second +cinematic inside a single frame, firing every sound it makes at once. + +### THE MENU THEME COMES BACK, AND THE COURT STAYS ON THE COURT + +*"after some games — or returning to the main menu via the close button — it does not play the +background music anymore but you sometimes still hear the court background noise."* + +Two halves of one bug, and both are `loopBed`'s early return. **It no-ops once a file is playing**, +so `menuMusic()` could only ever START the theme and never bring it back from the duck `start()` +put it under — the menu was silent for the rest of the session from the first run onward, and +quitting was the reliable way in because the death summary is the only path that ever called +`menuDuck(false)`. Meanwhile nothing ducked the court's own two beds at all, so the ambience +followed you onto the silent menu. + +`menuMusic()` is now the one call that means "we are in the menus": it starts the theme, un-ducks +it, and ducks both court beds. `ambient()` and `zombieMusic()` un-duck themselves for the same +reason — the SECOND run would otherwise have been played in the silence the menu left them in. +**The rule, for anything added here later: a bed's start function must also un-duck it, because +starting it is a no-op the second time and ducking it is not.** `audio.bedTest` exposes the duck +set to the suite (same idea as `netTest`), which is what makes this testable at all — the beds +never load headless, but the INTENT is the thing that was wrong. + +### THREE WORLD BOARDS + +*"the world rank is still about the splatter gore which is not the only thing — we need something +similar for the other modes."* + +`stats.BOARDS` is the only place that knows what each board ranks, and the server does not know at +all: it sorts `k` then `g` within a mode and nothing crosses. + +| board | ranks | second column | +|---|---|---| +| SPLATTER | kills in a run | games survived | +| TENNIS (arcade + cup) | opponents felled | games | +| CLASSIC | games taken off the CPU | games played | + +Classic needed a number worth ranking — every win is exactly two sets, so a board of 2s is a wall +of ties. `G.classicWon` counts the games YOU took and rides in the record's `s` slot, which held +sets until now and which **nothing has ever displayed** (the sets line on the death screen is +gore's; `read()` has never looked at `s`). The name prompt is per-mode too, gated on the board's +own number rather than on kills — the old `run.kills > 0` test silently barred classic (no kills) +from ever reaching it. + +Server: rows carry `m`, `GET /api/scores?m=g|z|c` filters, POST keeps one row per name **per +board** and trims `KEEP` **per board** so a busy mode cannot evict a quiet one. Name ownership +stays GLOBAL — one person owns their name on all three, or a stranger could take it on classic +while you hold it on gore. Rows written before the split carry no `m` and are gore rows, which is +the same convention the client has used since the stats screen split, so there is no migration. + +### THE GORE MODE GETS HARD AGAIN + +*"in splatter gore it now gets too easy to just play through. Maybe we should reduce the barrels +again — pretty early in the game every won game gives a barrel. The splatter gore should be +unbelievably hard towards the end."* + +He is right and the arithmetic said so. `barrelEvery 3 / barrelRamp 8` drove the gap to **one** at +game 16 and left it there, so the entire back half of a run paid out a weapon for every game won — +while every difficulty ramp in the file reached its ceiling around game 25 and stopped. A run got +richer after game 25 and never more dangerous. + +- **Barrels: 5 / 14, and a new `barrelFloor` of 3.** The ramp survives (the late game does need + more inventory than the early one), it is just slower and it STOPS. `gameplay.barrelGap()` is the + curve, exported and pinned by a test, because this is exactly the kind of one-liner that quietly + becomes an income again. +- **`zSpeedMax` 2.9 → 3.6** — shamblers keep gaining speed to ~game 28 instead of 22. +- **`nastyMax` 0.75 → 1.0** — by game 34 there are no ordinary shamblers left in the mix at all. +- **Ball machines scale**: `machCap()` is 2 + one per `machMaxPer` games, capped at `machMaxCap`. + The one kind of late pressure that does not crowd the ground — they hold position and shoot, so + the court stays readable while the number of things that can reach you goes up. + +The body COUNT ceiling (`zMaxCap`) is untouched and stays untouched: it is a readability limit on a +270 px screen, not a difficulty dial. What escalates now is what the bodies ARE and how fast they +arrive. **Feel-pending: these are dials, and the end of a long run is the one part of this game +that cannot be judged from the numbers.** + ## Session log: the roster doubles, the draw is made on screen, and netplay stops locking (2026-08-15) Oliver's seven-item batch. All of it is on STAGING and pending his phone verdict; items 3-5 in diff --git a/index.html b/index.html index 438df34..d77d921 100644 --- a/index.html +++ b/index.html @@ -498,9 +498,17 @@ if (location.hostname !== 'splatter-tennis.wuwi.one') {
+

WORLD BOARD

-

BEST SINGLE RUNKILLS · GAMES

+
+ + + +
+

BEST SINGLE RUNKILLS · GAMES