diff --git a/POLISH-PLAN.md b/POLISH-PLAN.md index e4ed711..0e22036 100644 --- a/POLISH-PLAN.md +++ b/POLISH-PLAN.md @@ -181,21 +181,49 @@ oder nicht." Note path 2 has a partial gate already: `G.shameGame` withholds the tin if *they* took a game since the last delivery. That is not the same as winning the duel. -**Build:** make winning the duel the ONLY source of weapons. -- Delete the `tinDue` scheduling in `splatPoint()` and the `deliverTin()` call in - `startChangeover()`. Keep the "CHANGEOVER" flash. `G.tinDue` / `TUNE.ballChangeFirst` / - `ballChangeEvery` / `G.shameGame` all become dead — remove them rather than leaving - vestigial state. -- Keep `resolveInterlude('P') → deliverTin()`. +**The design decision (Oliver, 2026-07-27):** duels must come round on their own after a +while, "damit es immer die chance auf waffen gibt". So the schedule is not the problem — it +is attached to the wrong event. **Move it from delivering tins to calling duels.** The +cadence stays, the reward becomes something you have to earn, and the umpire-restores-order +framing is unchanged. -**Consequence to check:** duels only trigger when the court is clear and a racket zombie is -the last one standing (`gameplay.js:~760`). If that turns out to be rare, weapons become -near-unobtainable. **Measure before shipping:** run a 5-minute simulated session and count -duels. If it's under ~1 per two games, tell Oliver rather than silently re-adding a second -source — the design question (how often should duels happen?) is his call. +**Why the duel is rare today:** the trigger at `gameplay.js:~760` requires +`zombies.length === 1 && zombies[0].variant === 'r' && !zombies[0].dueled` — the court has to +be completely empty apart from one never-yet-dueled racket zombie. Racket zombies only appear +when you win a game (`splatPoint`) or survive a horde (`endHorde`), and fresh shamblers spawn +again the moment a clear finishes. So it fires by luck. -**Acceptance:** simulate a full game where the player loses the duel; `G.bombs` etc. must be -unchanged. Win one; exactly one tin arrives. +**Build:** +- Rename the schedule to what it now schedules: `TUNE.ballChangeFirst`/`ballChangeEvery` → + `duelFirst`/`duelEvery`, `G.tinDue` → `G.duelDue`. Keep the same arithmetic in + `splatPoint()`. Suggested start: first duel after game 2, then every 3. +- Delete the `deliverTin()` call in `startChangeover()` (keep the "CHANGEOVER" flash). + `G.shameGame` — which withheld a tin if they took a game — has nothing left to gate; + remove it rather than leaving vestigial state. +- Keep `resolveInterlude('P') → deliverTin()` as the only weapon source. +- **Fire the scheduled duel off the existing clear beat.** `proceedAfterClear()` + (`gameplay.js:290`) already runs at the one moment the court is guaranteed empty — the + player just cleared the wave by hand, and no new shamblers have spawned yet. That is the + natural slot. There: if `G.duelDue`, consume it, ensure a racket zombie exists + (`spawnRacketZombie()` if none, and clear `dueled` on the existing one so a veteran can be + challenged again), and start the interlude instead of the changeover/game beat. The + changeover follows after the duel resolves. +- Leave the existing opportunistic trigger in place. The schedule is a floor, not a ceiling — + a lucky clear can still produce a bonus duel, which is pure upside for weapon supply. + +**Why this slot and not a timer:** clearing the wave is already mandatory before play +advances (`G.clearing`, a settled feel decision Oliver was emphatic about), so the schedule +can never stall. A wall-clock timer would need its own "wait for a safe moment" logic and +could fire mid-swarm. + +**Acceptance:** +- Simulate ~10 games. A duel must occur at game 2, 5, 8 … regardless of how the waves fall. +- Lose a duel → `G.bombs`/`triples`/`flames`/`guns` unchanged, and the next duel still + arrives on schedule. +- Win one → exactly one tin, and the item-8 modal names it. +- No path reaches `deliverTin()` except `resolveInterlude('P')` + (`grep -n "deliverTin" *.js` should show the definition, that one call, and the `window.T` + test export — nothing else). ---