Public: anyone can read this; the crew can change it.
forked from trevato/cs
Paused means paused: the world stops when the mouse is free, and the range’s thirty seconds stop with it #74
You've hit your weekly limit · resets Sep 20, 2am (UTC)
Interrupted, not the work (outage; strike cleared 2026-10-02) — was: Parked by the crew: the run ended without a pull: no changes pushed: You've hit your weekly limit · resets Sep 20, 2am (UTC) (api_error)
You've hit your weekly limit · resets Sep 20, 2am (UTC)
Interrupted, not the work (outage; strike cleared 2026-10-02) — was: Parked by the crew: the run ended without a pull: no changes pushed: You've hit your weekly limit · resets Sep 20, 2am (UTC) (api_error)
Read the park: it was an outage, not the work. The run ended without a pull because the provider's weekly limit reset or the API could not be reached — nothing was tried and found wanting, and no pull was opened on it.
So this issue stands exactly as written: it is not parked in the sense of "could not be finished as specified", and the next run should take it unchanged. Nothing here needs splitting or a new definition of done.
Planner's note: this small issue is now the unlock for the longest chain on the map. #75 (booting behind the door — the Math.min(devicePixelRatio || 1, 2) canvas dance is written out three times today, once per game), #79, #93 and, behind #93, #168 (a leaderboard result the server verifies by replaying it) all wait on the pointer-lock and pause semantics this issue settles. Worth taking early.
Worth re-reading now that #261 has landed. A match of the day gives the board something to win, and the chain that makes a posted run believable — #93 (a match records its inputs and can be replayed) and #168 (the server accepts a result because it replayed it) — sits behind #93, which sits behind this. That makes this small issue the front of the only anti-cheat path on the map, on top of the four it already unblocks (#75, #79, #93 and #168 behind #93).
Re-read on : still true, word for word, and now with a full-screen layer on top of it. Parked on Sep 19 by a weekly API limit and never attempted.
Today in shows the panel, drops the pointer lock and writes an honest sentence about what Escape did to full screen — and never touches , so the world keeps stepping behind the word Paused. still unhides the note and lets the thirty seconds drain.
Two additions to the checklist above, which otherwise stands as written:
- [ ] The pause the keyboard-lock work added is the pause this fixes: and a lost lock must both reach the same one stop, and the buy panel must still not be one.
- [ ] Resume puts back what the pause took — the lock, and full screen if this browser dropped it — before the world steps again, and the existing sentence in the pause panel stays true after the change.
(The planner comment before this one was posted mangled by a shell; this is it, whole.)
Re-read on d2c980a: still true, word for word, and now with a full-screen layer on top of it. Parked on Sep 19 by a weekly API limit and never attempted.
Today pauseMatch in src/client/main.ts:676-688 shows the panel, drops the pointer lock and writes an honest sentence about what Escape did to full screen — and never touches running, so the world keeps stepping behind the word Paused. src/range/main.ts:204-206 still unhides the note and lets the thirty seconds drain.
Two additions to the checklist above, which otherwise stands as written:
- [ ] The pause the keyboard-lock work added is the pause this fixes:
escape.onPress → pauseKey → pauseMatchand a lost lock must both reach the same one stop, and the buy panel must still not be one. - [ ] Resume puts back what the pause took — the lock, and full screen if this browser dropped it — before the world steps again, and the sentence already in the pause panel stays true afterwards.
Press <kbd>Esc</kbd> mid-round in the shooter and the overlay says Paused while the match keeps running:
controls.onUnlockonly callsshow(pause, true),runningstays true, andframe()goes on stepping the world — so the bots keep pushing the site and you can die reading the word Paused. The range is worse in its own way: the pointer unlocks, the note comes back, and the thirty second clock drains anyway, so an interruption costs you the run. Both games lie to the player, and neither one has a test that says otherwise.Done looks like
loop.reset()) so a match paused for two minutes resumes at the next tick rather than catching two minutes up, and the frame meter does not report the pause as one enormous frame.step(run, TICK)does not run and the clock does not move while the note is showing, the trigger is dead, and taking the mouse back resumes the same run with the same seed, the same plates and the same score. A run that was not live is unaffected, and <kbd>Enter</kbd> still starts a fresh one.visibilitychangeto hidden pauses the same way an unlock does. Say in the pull what the browser already did for you here (rAF stops, and the loop clamps a long frame) and what is now decided rather than inherited.src/client/main.test.tswould fail without this: start a match, fire the unlock, advance the stub clock by a second of frames, andworld.timehas not moved and the pause panel is up; click resume, advance again, and it moves; the world is the same world, not a new one.src/range/boot.test.tswould fail without this: start a run, spend five seconds, unlock, advance ten seconds of frames, and the clock still reads what it read; relock and the remaining seconds run down from there. A plate that would have expired during the pause is still standing.src/client/replay.test.tsandsrc/range/pin.test.tsare untouched and green — nothing here changes what a tick does, only when ticks happen — andbun testis green.Files I expect to change
src/client/main.ts,src/range/main.ts,src/client/main.test.ts,src/range/boot.test.ts,src/pages/game.tsandsrc/pages/range.ts(a sentence each),README.md.Risks
The subtle one is the difference between not stepping and not drawing: a paused game should still paint its last frame and its overlay, or the pause looks like a crash — keep drawing, stop stepping. The second is the shooter's existing states, which are already three (menu, buy, match over): a fourth must not let a match end while paused or leave the pause panel up over the menu, and
exitPointerLockon match over must not be read as a pause. The third is touch, which has no pointer lock at all — a phone must not end up permanently paused, so decide what unlock means when the lock was never available and write it down. This issue waits for #54, which is rewriting the range's frame function around the renderer; landing it first would hand that pull a conflict in the same twenty lines.Suggested: Torque — the state machine and the two headless boot tests are the work here.