Public: anyone can read this; the crew can change it.

forked from trevato/cs

Radio: three calls a person makes, and four teammates who go #386

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

You can now make three radio calls to your own side: **go A** (<kbd>Z</kbd>), **go B** (<kbd>Q</kbd>) and **hold** (<kbd>H</kbd>). A call overrides the round's plan for your side for 20 seconds, then the bots go back to the rolled plan on their own. `bun run test` passes (typecheck and 1579 tests), and the app boots and serves `/` with the new buttons.

shipped production is serving it deploy ae07c2d

opened this

You can now make three radio calls to your own side: go A (<kbd>Z</kbd>), go B (<kbd>Q</kbd>) and hold (<kbd>H</kbd>). A call overrides the round's plan for your side for 20 seconds, then the bots go back to the rolled plan on their own. bun run test passes (typecheck and 1579 tests), and the app boots and serves / with the new buttons.

  • The override is src/plan/call.ts, which sits beside plan.ts. liveCall finds the call standing for a side in the current round and ignores it once 20 seconds have passed. steer turns a site call into everyone on that site at once, at a rush. heed then does what a plan can't express on its own:

- A site call also pulls in the lurker that a stack or fake would send to the other site, so all four teammates go. - A hold stretches every bot's existing wait to the end of the call. The bomb carrier still moves, because the bomb never waits.

  • The bots: jobOf in src/client/bots.ts now re-cuts a side's jobs when the plan or the live call changes. When a call expires it counts as a change, so the jobs are cut from the plan again. With no call, the check is null === null, so behaviour is unchanged and every existing replay digest is still the same.
  • Making a call: src/client/radio.ts refuses a call from a dead player or between rounds. Otherwise it stores the call on world.radio, puts Radio — <name>: go A on the HUD's message line, and adds a radio sound. hears lets only the caller's side hear that sound, and audio.ts plays it as an unplaced static-and-blip.
  • Bindings and pad: Z, Q and H are in CS_KEYS, the three actions are in CS_TAP, and they are on the phone's left pad (crouch, jump, use, go A, go B, hold). main.ts handles them in onAction. It plays your own call's sound straight away, because the call happens at the exact time the last frame already played sounds up to, so the normal per-frame sound pass would skip it.
  • The pin: CALL_SCRIPT makes a CT player say "go B" one second into the live round of the seeded de_forge round. It is played under the local plan and pinned as CALL_ROUND (36 seconds, so the run includes the expiry). A second test shows the same run without the call matches it second for second until the call.

Not done:

  • Rooms carry no calls. A room's round runs on the server, and the network protocol has no message for a call yet. In a room the keys show a line saying so instead of pretending. radio is added at the end of the protocol's list of sound names rather than in alphabetical order, so no existing sound's number changes.
  • The terminal is unchanged, because it only shows the leaderboard.

Tests changed because behaviour was added, not because they were wrong: the CS_TAP expectation in binding.test.ts now includes the three calls. The audio test's list of sounds leaves out radio, which gets its own test, because the shared test expects every sound to get quieter with distance and the radio doesn't.

To try it: start a match, press <kbd>Z</kbd> during the freeze or the live round and watch the message line and your side on the minimap. Press <kbd>H</kbd> and your side stops moving. On a phone, use the left pad.

🤖 Generated with Claude Code

Closes #290

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs