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>
This commit is contained in:
1 parent
32401366fe
commit
55ddf5932e
8 files changed
+108
-23
No files matched your search
+29
@@ -182,6 +182,35 @@ Oliver rejected the procedural fakes on sight. The rule this round: **animation
|
||||
- Editing a full-scene image (`x_crowd` → `x_crowd2`) preserved the layout far better than expected:
|
||||
mean difference 3.2/255, railing and tier rows unmoved. Worth reusing for other 2-frame swaps.
|
||||
|
||||
## Third feedback round (2026-07-27)
|
||||
- **THE FREEZE (serving dead, gun racket stuck over your head).** `G.mgT` only counted down inside
|
||||
`if (G.mgT > 0 && G.phase === 'rally')`. Anything that moved the phase mid-burst — a scheduled
|
||||
duel calling `setupPoint()` — stranded `mgT` above zero *forever*, and `mgT > 0` is one of
|
||||
`keyPressed`'s early returns, so no tap could ever serve again. The yellow blob Oliver saw 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 (`startDuel` cancels any
|
||||
burst — the duel is tennis). If another "can't serve" report ever comes in, check these flags
|
||||
first: `G.mgT`, `G.horde`, `G.changeover`, `G.interlude.introT` — each one blocks `keyPressed`.
|
||||
- **The mop boy has a job now** (`mopRound` in gameplay.js): walk to the biggest nearby stain,
|
||||
stand on it for `TUNE.boyMopDwell` while the push cycle animates in place, scrub it out, pick the
|
||||
next, then walk off. `TUNE.boyMaxJobs` caps the round so a kill streak can't employ him forever.
|
||||
Gore fades ~3.5× faster while he is actually stopped and mopping than when merely walking past.
|
||||
A straight-line constant-speed crossing was what read as moonwalking past the mess.
|
||||
- **Applause is floored at the audio length.** `assets/audio/tennis-applaus.wav` is 4.16s and always
|
||||
plays out in full, but every `cheer()` call passed a shorter beat, so the stand sat down while the
|
||||
clap was still audible. `cheer()` now does `Math.max(crowd.cheer, seconds, APPLAUSE_T)`.
|
||||
- **Regenerating a crowd frame: colour-match it or don't ship it.** The bolder re-roll came back
|
||||
much brighter and bluer; swapping to it would have flashed the whole stand. Fixed by a per-channel
|
||||
linear map onto frame one's mean and sd (means now agree to within half a level), then quantised
|
||||
to 64 colours. Verify with a mean-difference number before trusting any regenerated frame.
|
||||
- **Net and umpire were both wrong since forever.** `COURT.netW` was the singles-stick width 10.06
|
||||
while the court is drawn to the doubles lines, so the net stopped 0.46 m short each side — now
|
||||
10.97, flush. `UMP_X` 5.6 put the chair's near legs 0.45 m *inside* the doubles line (x_ump is
|
||||
42×112 px at UMP_H 3.0 = 1.12 m wide as drawn); 6.05 stands them exactly on the line. Do not push
|
||||
past ~6.3 or the chair clips the right edge of the portrait viewport.
|
||||
The wider net is a real physics change (balls between |x| 5.03 and 5.485 now catch the tape);
|
||||
serve returns measured 134/150 = 89.3% ±2.5% afterwards, bar still inside one sigma.
|
||||
|
||||
## Gotchas learned the hard way
|
||||
- Browser-pane verification: python http.server sends no cache headers, so edited ES
|
||||
modules often load STALE even after location.reload(). Reliable fix: preview_stop +
|
||||
|
||||
Reference in new issue
Block a user