diff --git a/PROJECT.md b/PROJECT.md index 1676cb9..87a6126 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -10,7 +10,7 @@ detail of any individual change, read the git log. 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-11 (the horde's breather restored, the boss walk-on overlay; beats.js +Last updated 2026-08-11 (the coaching drill lands as a back-room prototype; the horde's breather restored, the boss walk-on overlay; beats.js extraction, this file renamed, tests gate the build). **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 should be handled, the second is everything known to be outstanding in the game, the third is the @@ -57,8 +57,10 @@ the primary target. `config.js` TUNE (live T panel) + structural constants + `UI` palette · `state.js` the `G` object + P/O/ball/score + `setupPoint`/`sendBoy` · `gameplay.js` ballistics/swings/serve/rules/AI/`update()` + transition + `splatPoint` + ball-boy routines · `beats.js` **the beat machinery: every mode -transition (duel/horde/boss/changeover/`proceedAfterClear`) — ONE MODE OWNS THE COURT, and each -`start*` enforces that itself; a new mode starts there and refuses there** · `zombies.js` +transition (duel/horde/boss/changeover/drill/`proceedAfterClear`) — ONE MODE OWNS THE COURT, and +each `start*` enforces that itself; a new mode starts there and refuses there** · `drill.js` the +coaching drill's own clock, bodies and rules (its transitions live in beats.js like every other +mode's) · `zombies.js` zombie/gore/particle state + `updateZombies` · `render.js` camera/palette-lerp/sprites/all drawing · `input.js` keys/touch/flick · `audio.js` sfx + recorded umpire (`assets/audio/*`, macOS `say -v Daniel`) · `panel.js` the T panel · `main.js` boot, loop, scoreboard test-mode taps, @@ -431,6 +433,77 @@ WELL drains instead: a gold wash across the whole tile, emptying leftward, clipp rounded path (`panel` leaves it as the current path) and drawn under the icon and labels. Measured lit vs empty on the live tile: rgb(88,125,68) against rgb(31,84,49). +## Session log: THE COACHING DRILL — the cognitive mode, as a prototype (2026-08-11, same day) + +Oliver and his team-mate decided the game needs **a different angle alongside the gore mode**, after +he reported the late game going repetitive: *"something that requires some immediate strategy other +than saving specific weapons for a boss I was killed by last time"*. Three modes planned — classic +tennis, **splatter tennis** (this), splatter tennis GORE (what exists). What went in today is the +middle one's feel-test, not the mode: **back room only** (`DRILL TEST`, or `#drill`), no menu entry, +nothing wired to tennis scoring. + +**Why the late game is flat, for the record** (diagnosed before proposing anything, per the +meta-lesson). The optimal move never changes: kill the nearest thing, now. Every interruption is on +a fixed metronome (duel/3, machine/4, horde/6, boss/10, barrel/every game past 16), the director's +dials all bottom out by ~game 25, nothing is scarce once the rack is full — and **the zombie side's +score is unreachable by construction**: contact is a radius around the player and bodies home on his +x, so anything that could cross the baseline eats him first. The crossing branch in `updateZombies` +(and the `ump_unsportsmanlike` line with it) is very nearly dead code. Oliver caught that himself — +the first design direction proposed this session was built on it and had to be thrown away. + +**The mode.** The umpire CALLS a colour sequence aloud while bibbed bodies jog on and take their +marks; then they come for you and you put them down in the called order. Colours never called are +DECOYS — bodies walking at you that you are required to ignore. Two faults = double fault = the +round is theirs. Settled in discussion: **audio-first, and shown as a pattern too.** + +- **`voiceSeq` in audio.js** is what makes the call possible at all. `voice()` DROPS a line that + would start while the umpire is still talking (one man, one microphone) — correct for unrelated + triggers, fatal for a list. It decodes every clip FIRST, then queues them all at accumulated + offsets, so the anti-overlap rule is satisfied by construction rather than fought. Five new + recordings (`say -v Daniel -r 168` -> `afconvert`, the existing pipeline). +- **Four colours, not six**, and every bib carries PIPS as well as a hue: ~8% of men cannot separate + red from green, and a mode whose entire input is "which colour is that" owes them a second + channel. The same pips appear on the HUD strip, so the two readouts teach each other. +- **The bib is a DRAWN OVERLAY and it knowingly breaks the sprite rule** (`ponytail:` marked in + `drawBib`). Six bib sheets x every walk frame is a week of art to answer a question a trapezoid + answers today. If the mode survives its playtest, `drawBib` is the thing to DELETE and replace + with a real bib sprite per colour, drawn like a wound skin. Shoulders wider than waist — the first + cut flared the other way and every body was wearing a sandwich board. +- **TWO functions pick a target, and snapping only one of them was the bug.** `flickTarget` (the + slingshot) and `zombieTarget` (the plain tap) are separate paths; with only the first snapped, + fifteen tap-serves in a row scored zero and the round just ran out. Both snap now. Anything that + changes how a shot chooses a body has to touch both — same shape as the "three of five weapons + silently no-oped on the racket zombie" bug. +- **Contact costs the POINT, not the run** — a deliberate contract change, hooked in `killPlayer` + (the single death path for both damage channels, which is why one hook covers every way in). + Permadeath stays settled in the gore mode: a memory game where one lapse deletes a thirty-minute + run punishes the exact skill it is trying to teach. +- **The empty-court net** in `updateDrill`: a live round with nothing bibbed on court means something + outside the drill emptied it (seen once while wiring up, not reproducible afterwards). Unrepaired + that is a dead mode — no targets, no clock, nothing to tap. It redeals and logs `INVARIANT`, the + same contract `watchdog` uses. `startDrill` also DISARMS the ordinary schedule (`pendingHorde`, + `hordeWarn`, `bossDue`, `duelDue`): taking the court is not enough if someone else's arming latch + is still loaded behind you. +- **Eight new tests** (24 total) pin the ownership contract both ways, the call beat, the clean win, + the double fault, the un-winnable-round guard and the softer death. + +**Open, and every one of them is Oliver's call after he plays it:** +1. **A ball that carries through the lane can complete two steps at once** — measured: one serve + killed bib 1 and then bib 2 standing behind it, banking two correct steps in one shot. Had the + second body been out of order that is an instant fault the player could not have intended. Either + the lane is part of the puzzle (it is the same occlusion that makes footwork matter) or the pierce + has to stop on a drill kill. Do not patch it silently — it is a design fork, not a bug. +2. **The tap now aims itself completely.** With `zombieTarget` snapped, a bare tap is a legal shot at + the called body, so the mode currently has NO aiming skill in it at all — only order and tempo. + That is probably right for a cognitive mode played one-thumbed, and occlusion is the only thing + left that makes standing position matter. Worth a deliberate look rather than an accident. +3. Difficulty climbs on sequence LENGTH today (`drillSeq` 3 -> `drillSeqMax` 4, +1 every 3 rounds) + and is deliberately capped low: a longer list is a memory test and memory tests fatigue. The dial + with real headroom is the GEOMETRY — how often the called order fights the order they reach you + in, and how often the target is behind someone. Unbuilt. +4. Nothing is wired to tennis scoring, the menu or the run, and the mode has no ending. That is the + next job IF the answer to "tense or noisy?" comes back tense. + ## Session log: beats.js, the rename, and the build gate (2026-08-11, same day) Oliver's structural question ("framework? smaller files?") answered with a deliberate NO on both — diff --git a/assets/audio/drill_blue.m4a b/assets/audio/drill_blue.m4a new file mode 100644 index 0000000..eba42ba Binary files /dev/null and b/assets/audio/drill_blue.m4a differ diff --git a/assets/audio/drill_call.m4a b/assets/audio/drill_call.m4a new file mode 100644 index 0000000..2f6d138 Binary files /dev/null and b/assets/audio/drill_call.m4a differ diff --git a/assets/audio/drill_green.m4a b/assets/audio/drill_green.m4a new file mode 100644 index 0000000..5e9ced7 Binary files /dev/null and b/assets/audio/drill_green.m4a differ diff --git a/assets/audio/drill_red.m4a b/assets/audio/drill_red.m4a new file mode 100644 index 0000000..addb9f6 Binary files /dev/null and b/assets/audio/drill_red.m4a differ diff --git a/assets/audio/drill_yellow.m4a b/assets/audio/drill_yellow.m4a new file mode 100644 index 0000000..df2c673 Binary files /dev/null and b/assets/audio/drill_yellow.m4a differ diff --git a/index.html b/index.html index 1dd7f1b..97ed074 100644 --- a/index.html +++ b/index.html @@ -373,6 +373,7 @@