Basic menu: persistent Game root + sandbox menu area + menu panel #2

Merged
merith-tk merged 7 commits from muse-lantern/da-game:basic-menu into main 2026-10-21 20:43:31 +00:00
Member

Implements the basic menu setup from the map/save/level-loading design (consensus reached 2026-10-20; design write-up for game-planning follows separately).

What boots now

Launch -> Game root (persistent) initialises XR -> LevelLoader loads the Sandbox level underneath it -> MENU mode: player anchored in the level's menu area, world-space menu panel in front, laser-pointer driven (ITR-style stationary menu in a real place).

The shared-area model

The Sandbox level carries three content groups — MenuContent / StoryContent / SandboxContent — and modes just redress the same physical space:

  • New Game -> story dressing on, player unanchored at the spawn point (save creation joins here at the save milestone; nothing persists yet)
  • Sandbox -> debug dressing on (slab + test cubes + sign), throwaway state, never touches saves
  • Continue -> present but disabled until save/load exists

Pieces

  • src/core/game.gd — root + mode state machine; XR init with flat/desktop fallback (mouse pointer, same menu)
  • src/core/level_loader.gd — owns level swaps (a level never frees itself); synchronous loads for now, threaded loading is the fog-transition upgrade
  • src/core/level_registry.gd — level id -> scene table (saves/transitions will reference ids, not paths)
  • src/levels/level.gd — level base: content groups + MenuAnchor/SpawnPoint contract
  • src/ui/menu_panel.gd + main_menu.* — SubViewport-on-a-quad panel; controller raycasts and mouse feed the same viewport GUI input path (both input styles stay, per the accessibility rule)
  • src/player/player_rig.gd — rig now implemented on the skeleton's structure: pointers with beams, anchoring, and provisional locomotion (left-stick smooth/head-relative, right-stick snap turn). The #19 locomotion questions are still open — the numbers are placeholders, clearly marked
  • Return to menu: controller menu button, or Esc on desktop
  • scenes/main.tscn removed — scenes/game.tscn is the boot scene now

Verification (from the build VM, no headset)

  • Headless import with the pinned Mono editor: clean, no script errors
  • tests/smoke_menu.gd: 16/16 checks pass — level loads, MENU anchoring + content visibility, New Game / return / Sandbox mode switches all behave
  • Project boots headless error-free

In-headset test is Zach's: panel placement/feel, pointer behaviour, and the anchor framing can't be judged from here.

Implements the **basic menu setup** from the map/save/level-loading design (consensus reached 2026-10-20; design write-up for game-planning follows separately). ## What boots now Launch -> **Game root** (persistent) initialises XR -> **LevelLoader** loads the Sandbox level underneath it -> **MENU mode**: player anchored in the level's menu area, world-space menu panel in front, laser-pointer driven (ITR-style stationary menu in a real place). ## The shared-area model The Sandbox level carries three content groups — `MenuContent` / `StoryContent` / `SandboxContent` — and modes just redress the same physical space: - **New Game** -> story dressing on, player unanchored at the spawn point (save creation joins here at the save milestone; nothing persists yet) - **Sandbox** -> debug dressing on (slab + test cubes + sign), throwaway state, never touches saves - **Continue** -> present but **disabled** until save/load exists ## Pieces - `src/core/game.gd` — root + mode state machine; XR init with flat/desktop fallback (mouse pointer, same menu) - `src/core/level_loader.gd` — owns level swaps (a level never frees itself); synchronous loads for now, threaded loading is the fog-transition upgrade - `src/core/level_registry.gd` — level id -> scene table (saves/transitions will reference ids, not paths) - `src/levels/level.gd` — level base: content groups + MenuAnchor/SpawnPoint contract - `src/ui/menu_panel.gd` + `main_menu.*` — SubViewport-on-a-quad panel; controller raycasts and mouse feed the *same* viewport GUI input path (both input styles stay, per the accessibility rule) - `src/player/player_rig.gd` — rig now implemented on the skeleton's structure: pointers with beams, anchoring, and **provisional** locomotion (left-stick smooth/head-relative, right-stick snap turn). The #19 locomotion questions are still open — the numbers are placeholders, clearly marked - Return to menu: controller menu button, or Esc on desktop - `scenes/main.tscn` removed — `scenes/game.tscn` is the boot scene now ## Verification (from the build VM, no headset) - Headless import with the pinned Mono editor: clean, no script errors - `tests/smoke_menu.gd`: **16/16 checks pass** — level loads, MENU anchoring + content visibility, New Game / return / Sandbox mode switches all behave - Project boots headless error-free In-headset test is Zach's: panel placement/feel, pointer behaviour, and the anchor framing can't be judged from here.
Boot -> Sandbox level loaded under a persistent Game root (player rig +
level loader survive level swaps). The level's menu area is shared
ground: MENU mode anchors the player in front of a world-space
laser-pointer menu panel; New Game and Sandbox redress the same area
(StoryContent / SandboxContent groups) and unanchor. Pointer input via
XR controller raycasts, mouse in flat/desktop mode — same viewport GUI
path both ways. Continue present but disabled until save/load lands.
Locomotion provisional (left-stick smooth, snap turn) pending #19.

Verified headless: import clean; tests/smoke_menu.gd passes (mode
switches, content groups, anchor state); project boots error-free.
The action map resource existed since the skeleton but xr/openxr/
default_action_map was never set, so the runtime received no
interaction bindings: no controller poses, buttons, or thumbsticks
in-headset. Smoke test now guards the setting.

gl_compatibility is Zach's call from headset testing: the test rig's
Vulkan ICD is missing (nv-vk64.json absent even after a clean NVIDIA
reinstall), and Compatibility reaches OpenXR through OpenGL instead.
Vulkan driver pins kept for an eventual Forward+ return.
tools/export_hands.py splits the private blender-assets hands.blend
per hand (skinned split, wrist-centred, canonical frame) into GLBs
that stay local + gitignored (licensed); HandView instances the GLB
when present and falls back to a placeholder mitten otherwise.
Trigger curls index, grip curls the fist; the curl axis/sign is
calibrated at startup from rest geometry (tests/hand_check.gd
verifies fingertips close palm-ward on both hands). Hand placement
on the aim-posed controllers is a blind first guess — tuning
constants live on the HandView nodes in player_rig.tscn.
- Base player model: Drillimpact's CC0 PSX arms, remapped to the
  avatar-rig bone contract, at the canonical overridable path
  assets/player/arms/player_arms.glb (source .blend alongside,
  textures, README mapping). Attribution as assets/player/arms/
  LICENSE.txt in the assets-repo two-part format (upstream license
  + project license); ledger entry in docs/external-resources.md.
- ArmsView replaces the per-hand HandView nodes on the rig: one
  combined skeleton driven by analytic two-bone IK from the
  controllers (local bone poses down the chain), controller
  orientation for the hands, calibrated finger curl (trigger=index,
  grip=fist). Bones are addressed by contract name only, so any
  conforming replacement model works unchanged.
- Addons (docs/addons.md): packs are .pck/.zip mirroring res://
  paths, mounted by the AddonLoader AUTOLOAD before any content
  loads; same path replaces, new paths are discovered. Search:
  <exe dir>/addons then user://addons, alphabetical, later wins.
  Terminology is now 'addon' across code comments (was 'mod'):
  extension-first by design, alteration allowed.
- LevelRegistry is data-driven: descriptors in data/levels/*.json
  scanned lazily after addons mount; addons can add levels or
  shadow base ones. Fallback table kept for broken installs.
- Tests: addon_check (override replaces base file + addon level
  discovered), arms_check (39 contract bones, curl closes hand,
  IK reaches target and bends elbow), smoke updated + passing.
  hand_check retired with HandView's rig role.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
dead-air/engine!2
No description provided.