feat(game): choose a game mode before entering the yard#31
Merged
Conversation
`/play` only ever opened one scene — Team Deathmatch against bots — with no way to see or change that. It now asks first, offering three modes on the start gate. Two of the three already existed and had no entry point. `TrainingTargets` (stationary targets, no return fire) and the empty greybox are the "training" and "greybox" technical scenes PRD §23.1 already lists under the free sandbox; this exposes built work rather than adding scope. These are deliberately not new competitive modes. The locked V1 decision is one multiplayer mode, 6v6 Team Deathmatch, and inventing a second would contradict it. Everything here is single-player against NPCs and sits on the free side of the access line, so `/play` stays openable without an account. `Opponents` takes a roster size, and an empty roster is a supported configuration rather than a degenerate one: it owns the `MatchSimulation`, which is where the player's own grenade count, cooldown and blast are resolved, so it is built in every mode including the ones with no bots. The picker is a real fieldset and radio group, so arrow-key navigation and screen-reader announcement come for free (accessibility is P0). The native input is visually hidden but keeps focus, so the focus ring is drawn on the label instead — without that the group is keyboard-navigable and invisible to anyone using it that way. Changing mode reloads rather than re-dressing the yard live: the roster and the targets are decided at boot, and swapping them in place would be a second, subtly different code path for something that happens once. The choice is remembered on change rather than on start, so picking a mode and then reloading does not silently lose it, and `?mode=` beats the remembered value so a link opens what it says. Note `?mode=` is also read by `access.ts` for demo/campaign/multiplayer. The two never collide — access values are not scene ids and fall through to the default — and there is a test pinning that. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
/playonly ever opened one scene — Team Deathmatch against bots — with no way to see or change that. It now asks first.Two of the three already existed and had no entry point.
TrainingTargetsand the empty greybox are the "training" and "greybox" technical scenes PRD §23.1 already lists under the free sandbox. This exposes built work rather than adding scope.Not new competitive modes
The locked V1 decision is one multiplayer mode — 6v6 Team Deathmatch — and inventing a second would contradict it. Everything here is single-player against NPCs and sits on the free side of the access line, so
/playstays openable without an account. Worth saying plainly since the request started from "can we have a multiplayer demo": this is NPCs only, no networked play.Notes
Opponentsnow takes a roster size, and an empty roster is a supported configuration, not a degenerate one: it owns theMatchSimulation, which is where the player's own grenade count, cooldown and blast are resolved, so it is built in every mode including the ones with no bots.fieldset/radio group, so arrow-key navigation and screen-reader announcement come for free (accessibility is P0). The native input is visually hidden but keeps focus, so the focus ring is drawn on the label — without that the group is keyboard-navigable and invisible to anyone using it that way.?mode=beats the remembered value so a shared link opens what it says.?mode=is also read byaccess.tsfor demo/campaign/multiplayer. The two never collide — access values are not scene ids and fall through to the default — and there is a test pinning exactly that.Verification
Typecheck, lint, 245 tests (10 new) and the build pass. All three modes were booted in a browser and screenshotted: the picker renders with the selected mode highlighted and its blurb below, Firing Range spawns stationary targets with no roaming bots, and Free Roam is empty. No console errors in any of the three.
🤖 Generated with Claude Code