[31 game-shell] K3: one pause menu, levels, per-game settings and FPS in every game - #251
Merged
Merged
Conversation
- src/lib/gameSettings.js: the ONE settings store every game shares - the core rows (music/volume, sfx/volume, haptics, show FPS, VR turning + snap angle, comfort vignette, quality preset) plus game rows declared by api.game.addSetting or a flow node (registerGameSetting(row, owner) -> off). LOCAL per device via safeStorage, per game id: core rows in tp:game:<id>:shell, a game row in tp:game:<id>:<row id>. - the game id = slug of the last scene FILE opened (sessions.applySession notes the payload name, so a Games-tab load of Towers is 'towers'), else the saved scene name, else 'untitled'. - live resolvers core reads: sfxLevel/musicLevel/hapticsAllowed/resolveTurning. - vitest gameSettings (14): per-game isolation (SFX off in Towers, on in Waves), row coercion, reserved ids, a game row surviving re-registration. Counterfactual: drop the gid from shellKey -> "SFX off in game A does not reach game B" goes red. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… core obeys, show FPS, levels
G4 THE PAUSE MENU (gameShell.js + GameShellMenu.svelte + the VR board):
- Resume · Restart · Levels (when registered) · Settings · How to play · Main menu, plus a
quiet desktop-only "Back to editor". ONE model (shellMenuItems / shellSettingViews / ...)
drawn by the desktop card and, in a headset, on the VR game panel (shellPanelDraw.js,
kind 'shell' hits pressed through the panel's one press path).
- Desktop: Escape in a GAME opens it (a lost pointer lock pauses into it; Escape again =
Resume, re-locking through the retry path); the corner Menu button; desktop Interact
takes Escape in the component. A scene that is not a game keeps Escape = exit.
- VR: the LEFT X button (the free face button), a Menu button on the board footer and on
the wrist card.
- HudLayer stays the ONE writer of playPointerFree (folds shellMenu.open in) and its ring
stands down while the menu owns the keys.
- Main menu: music stops, a headset session ends, Interact -> Edit, play exits (editor
camera via the camera-follows-isLocked effect), the templates modal opens on Games
(templatesModalTab, new). Restart: requestResetGame (host/alone), module onRestart hooks,
respawn, screen overrides cleared. Seams registered by gameShellWire.js.
G1/G2 PER-GAME SETTINGS OBEYED LIVE:
- the per-game Music / Sound effects levels land on the audio BUSES (audioEngine
setBusLevel, built-later buses start at the level): api.playSound, api.music, flow sound
nodes, pings and the scene track all obey; playGameSound starts nothing when off.
- haptics: the hapticPulse funnel also gates on this game's "Controller vibration".
- VR turning: snap / smooth (90 deg/s, deadzone) / off + the snap angle, per game while
playing, the device's snap angle otherwise (vrControls.turningInForce).
- comfort vignette (comfortVignette.js): a head-locked ring closing on stick locomotion and
smooth turning, one mesh, no per-frame allocation.
- Quality: qualityGovernor.applyGameQuality pins High 0 / Medium 3 / Low 7 while playing,
Auto releases to the governor (split agreed with 31-perf).
- the game id also follows the saved scene name (levels.currentLevel) after the file name.
G3 SHOW FPS: fpsMeter.js (desktop = sceneBudget's frame observer, VR = the panel's per-XR-
frame call), FpsCounter.svelte (fps · ms · calls · tris), the VR strip + wrist lead line.
P3 SDK (moduleSDK api.game): levels, addSetting, setting, setSetting, onSettingsChange,
setHelp, onRestart, openMenu/closeMenu/menuOpen — all journalled, feature-detectable.
Tests: e2e game-shell (70 checks, desktop + VR emulated via fakeXR), vitest gameShell (10),
gameSettings (14). svelte-check 333/47 (baseline held).
Counterfactuals (each run red with the change reverted, then restored):
- drop the PLC Escape -> openShellMenu line: 1.2-1.5 red (Escape exits play again)
- drop setBusLevel('sfx', ...): 3.3 / 3.6 red
- drop the left-X toggle in vrControls: 8.2 / 8.3 / 8.5 / 8.6 red
- drop hapticsAllowed() from the funnel: 10.1 red (off:2)
- levels off() compared object identity: vitest "a pick reaches onPick" red (fixed with a token)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…art, menu A section per call with a short example; the core rows core obeys and how a module with its own WebAudio graph obeys the sfx row. (The docs-site copy is on the docs repo branch docs-31-game-shell.) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… through the pause menu - game-shell-games (63 checks): Towers, Stars Room, Football, Jam Room, Dungeon Realms, Untangle, Waves from their REAL .tpscene + module zips — each: the game id is its slug, Escape mid-game opens the pause menu, Resume returns, Main menu leaves Play to the Games tab with the music off; then VR (fakeXR): left X opens it on the board, Resume closes, Main menu leaves the game. ALL PASS (SHELL_SCENES_DIR = the preview-1-17 scenes). Evidence: /home/deck/.code/lanes-30/after-31/31-game-shell/menu-desktop-<game>.png. - game-shell: EVIDENCE_DIR also writes the VR board's four pages as PNGs. - game-editor-flow asserted "Escape returns to the editor" in a game, which K3 changes on purpose (QUESTIONS-31-game-shell fork 1): it leaves through the menu's Back to editor now. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… arrows "Device default" overflowed the 110 px value slot between the ‹ › buttons on the VR board (seen in the evidence capture). The two choice rows say "Default" now, and the board shrinks a value's font to fit its slot, so a game row's long option label cannot overlap its arrows either. game-shell 65/0, game-editor-flow 41/0, vr-game-panel 35/0. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
31 game-shell (K3) — one pause menu, levels, per-game settings and FPS in every game
The Quest feedback items this lane owns (cloud
plans/core/lanes/31/user-feedback-2026-10-01.md):api.game.levels→ desktop + VR pickersrc/lib/gameSettings.js(31-stars-jam registers its rows into it)qualityGovernor.applyGameQualitypins High 0 / Medium 3 / Low 7 while playing (split agreed with 31-perf)What changed
gameSettings.js(leaf): core rows + game rows, LOCAL per device and per game (tp:game:<id>:shell/tp:game:<id>:<row>); game id = the opened scene file's name slug.gameShell.js(leaf) +gameShellWire.js: menu state, levels, help, restart hooks, Main menu (music stops, XR session ends, play exits, Templates opens on Games).GameShellMenu.svelte,FpsCounter.svelte;shellPanelDraw.jspaints the same pages on the VR game panel (content only — no edits to 31-vr-core's rendering/laser/hit-test functions).playPointerFree.audioEngine.setBusLevel, haptics gate in thehapticPulsefunnel,turningInForce+ smooth turning in vrControls,comfortVignette.js,fpsMeter.js.api.game.levels / addSetting / setting / setSetting / onSettingsChange / setHelp / onRestart / openMenu / closeMenu / menuOpen; MODULES.md section (docs site: theprototype-app/docs PR fromdocs-31-game-shell).Tests
game-shell65/0 (desktop + VR via fakeXR),game-shell-games— the SEVEN real games (scene + module zips), desktop + VR: ALL PASS.setBusLevel('sfx')→ 3.3/3.6; left-X toggle → 8.2/8.3/8.5/8.6;hapticsAllowed()→ 10.1; levels off() identity → vitest.Decisions (QUESTIONS-31-game-shell.md)
Escape in a game = the menu (leaving is Main menu / Back to editor) · the menu does not pause the shared world · Restart = the shell's reset rule + module hooks + respawn · per-game volumes multiply the device volumes on the buses · VR menu button = left X.
Merge notes
qualityGovernor.js(decideNow early return +applyGameQualityblock) — 31-perf keeps out of those hunks; its XR session-start floor must stand down whilegameForcedLevel !== null.vrControls.js(haptics gate,turningInForce/smooth turning in updateSnapTurn, left X) andvrGamePanel.js(footer extraction, shell branch) — expect textual overlap with 31-vr-core; both sides compose.__stores.gameKit(no App.svelte tail change).OWED on device (Quest)
left X opening the menu · board readability at arm's length · smooth-turn speed + vignette feel · an FPS reading on the strip · Main menu ending the session cleanly.
🤖 Generated with Claude Code