P0/P1 gameplay polish, input ergonomics, and UI fixes

Chromium Dino · build 23765948 · published

On SteamNotes only · this build shipped before we watched the app

Gameplay (dino & obstacles)

- dino: variable jump height, ~90ms coyote time, ~130ms jump buffer,

shrink hitbox to 0.6 x 0.78 of sprite for fairer hits.

- obstacle-manager: expose getSeed(); add obstacleTypeFilter so the

EASY difficulty can drop the harder obstacle types.

- ghost-manager: take the obstacle seed from the live run so ghost

races spawn identical obstacle sequences.

- power-up-manager: prevent coins from overlapping obstacles and

retune spawn cadence so pickups feel meaningfully more frequent.

Input

- input-manager: honor W/S for P0 in single-player too (coopEnabled

no longer gates secondary controls when there is no P1).

- gesture-source: drop debounce to 80ms, divider 1, numHands 2 for

snappier hand-gesture response.

- input-mode-store: dispose gesture / head-pose camera singletons

when the input mode changes so the webcam is released.

UI / flows

- main-menu: add a Quit button (power_settings_new icon) wired

through preload bridge to a new app:quit IPC handler in main.

- daily-challenge: add a Replay path (launchDaily(isReplay)) and

a Replay button when the daily was already played today.

- game-scene + App + types: thread GameConfig.isReplay through to

GameOverData; on replay only savePlayer(), skip coin / highscore

/ streak / lastDailyDate updates.

- game-over: support keyboard Space / R to restart.

- i18n: add menu.quit and daily.replay strings (en + zh-hans).

- components.css: minor styling tweak supporting the new buttons.

Material Symbols FOUT (the "workspace_premium" flash on refresh)

- index.html: declare Google Fonts <link> tags statically with

preconnect hints so requests start during HTML parse instead of

after React mounts; switch URLs from display=swap to display=block

so icon slots stay invisible during the short load window instead

of leaking ligature keywords.

- main.tsx: drop the now-redundant runtime <link> injection.

Wiring

- main/index.ts: register ipcMain.on('app:quit') → app.quit().

- preload/index.ts: expose electronAPI.quit().

- game/types: add GameConfig.isReplay, GameOverData.isReplay, and

Window.electronAPI.quit(): void.