Plan focus cone, head ring, and radial action menu; defer host sign-in

Document the next playable pass: WASD/look stop fighting action keys, cone
focus with a billboarded head ring, doughnut HUD opened by LMB or Space
(click or A/D then Space to confirm, Esc/RMB cancel). Emergency stays on R.
Wand leaves W. NPC rigs stay procedural but get a face/hair/neck. Host
REQUIRE_SIGNIN_VIEW follow-up deferred.
This commit is contained in:
zwitschi committed 2026-10-31 15:00:11 +01:00
1 parent b704a8ed61
commit 5662baa14d
3 files changed
+193

No files matched your search

+4
View File
@@ -15,6 +15,10 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
## Current State ## Current State
- Next playable pass (planned, not coded): first-person stay, but focus becomes a camera cone +
head ring, and NPC verbs move onto a round pie menu (LMB/`Space` open, click or A/D/arrows then
`Space` confirm, `Esc`/RMB cancel). Emergency stays on `R`. Wand leaves `W` (conflicts with walk).
Host sign-in follow-up is deferred. Details in PLAN.md "Player Control, Focus, and Action Menu".
- **Release v0.2.1 is LIVE** (2026-10-31): CI run 1110 built the NSIS installer + AppImage - **Release v0.2.1 is LIVE** (2026-10-31): CI run 1110 built the NSIS installer + AppImage
and published — installers/AppImage are on the releases page. Releasing = bump version → tag → push. and published — installers/AppImage are on the releases page. Releasing = bump version → tag → push.
- NSIS gotchas (validated in a local ubuntu:24.04 docker repro): makensis uses `-D` defines - NSIS gotchas (validated in a local ubuntu:24.04 docker repro): makensis uses `-D` defines
+134
View File
@@ -452,6 +452,140 @@ The following commands apply once the Godot project is scaffolded:
Desktop is the first target: Windows, Linux, and macOS. Web export is a later option. Desktop is the first target: Windows, Linux, and macOS. Web export is a later option.
## Player Control, Focus, and Action Menu (next pass)
The prototype already walks and looks in first person, but the control scheme fights itself and targeting is a hairline. This pass replaces that with three things that belong together: solid locomotion, a visible focus on a person, and a round action menu for what to do with them.
### What is wrong today
- `W` is walk-forward in the input map and wand-screening in `GameManager._handle_key`. Walking at someone with a wand venue fires the wand.
- `A` / `D` are strafe and also allow/deny during inspection.
- `Q` is polite direction and is also bound as `cancel`.
- Focus is an 8 m physics ray from the camera. Missing a capsule by a few pixels clears the target. There is no mark in the world that you have someone.
- NPC bodies are colored capsules with props. They read as markers, not people.
- Actions live on number keys `1`–`6` plus `Q` / `E` / `W` / `R`. That is a cheat-sheet, not a verb you pick while looking at a person.
- `Esc` only toggles mouse capture. There is no "I changed my mind" for an action in progress.
### Movement and look
Keep first-person WASD + mouse look. Fix the feel and stop stealing those keys.
- Body yaw from mouse X, head pitch from mouse Y, pitch clamped (already `-80` to `80`).
- Move relative to body yaw only (already). Add short acceleration / deceleration so starts and stops are not instant.
- Gravity only when `not is_on_floor()`; `floor_snap_length` so the steps work.
- Mouse look runs only while `MOUSE_MODE_CAPTURED` and no modal UI is open (radial, inspection, wand, radio, shift review).
- Walk speed stays around 3.2 m/s. No sprint, no crouch in this pass.
- While the radial is open: freeze look and WASD so `A` / `D` can cycle the menu. Recapture the mouse when the menu closes.
- `Esc` with a modal open closes that modal and does nothing else. `Esc` with nothing open keeps the current capture/release toggle.
Equipment stays on its own keys because it is not about a person: `G` flashlight, `L` door lock, `Tab` cycle nearby people, `V` venue (debug), `F` debug overlay. `R` stays the dedicated emergency radio (hold-to-confirm on the existing panel). Those do not go on the wheel.
Wand leaves `W`. It is a slice on the wheel (greyed when the venue has no wand) and an optional extra key that is not WASD, suggested `C`.
### Focus area
Replace the hairline ray as the primary picker.
- A **focus cone** in front of the camera: people within about 10 m whose head projects inside a screen-space circle around the crosshair (about 90 px / 10 degrees).
- If several heads sit in the circle, pick the one closest to the center, then the nearer one if tied.
- The person must be in front (dot with camera forward > 0) and not fully occluded by the building. A short ray to the head is enough for occlusion.
- `Tab` still cycles people inside 12 m as the aim-free path. Cycling sets the same focused person the cone would.
- Clearing: look away until the head leaves the circle for a few frames (small grace so a bob does not drop them), or they walk out of range.
### Head ring
When a person is in focus, draw a thin ring around their head.
- Attach to the avatar `Head` node, sitting just above the skull, billboarded to the camera so it stays a circle on screen.
- Slow spin is fine; no ping-pong scale spam.
- Idle focus: pale gold, modest size. Radial open on that person: brighter, slightly larger.
- Optional small name under the ring (`display_name` only, not the archetype cheat `Violent Threat` in the world). The existing HUD target line can keep the archetype for the prototype.
- One ring at a time. Everyone else has none.
Implementation sketch: `MeshInstance3D` torus or a thin unshaded ring mesh on the avatar, toggled from `GuardController` / `GameManager` when focus changes. Screen-space fallback (unproject head, draw a `Control` circle) is acceptable if the 3D torus z-fights on hats.
### Round action HUD
A doughnut / pie menu centered on the screen, opened on the focused person.
**Open**
- Left mouse button, or `Space`, while someone is in focus and no other modal is open.
- If nobody is in focus, log the existing "aim first" line and do not open.
- The click that opens the menu must not also pick a slice.
**While open**
- Mouse becomes visible. Look and walk freeze.
- Center of the doughnut shows the focused person's `display_name`.
- Slices, clockwise from the top, de-escalation first:
1. Raise hand (stop / wait)
2. Point away
3. Head shake
4. Polite direction (old `Q`)
5. Inspect access (old `E`)
6. Wand (grey if the venue has no wand)
7. Formal warning
8. Block
9. Radio assist
- Emergency is **not** on the wheel (`R` only).
- Highlight follows the mouse angle when the cursor is over the ring. `Left` / `Right` or `A` / `D` step the highlight. Wrap around.
**Confirm**
- Click a slice: that action runs, menu closes, mouse recaptured.
- `Space` while a slice is highlighted: same.
- Inspect or wand: close the wheel, then open the existing inspect / wand panel. Allow / deny on that panel stay `A` / `D` (walk is frozen only while the wheel is up; once the inspect panel is open, WASD can move again, so allow/deny on `A` / `D` remains a press handler and must ignore repeats / echo, which it already does). Safer: keep walk frozen for inspect/wand too, until the panel closes. Prefer freeze until the whole interaction ends.
- Radio assist: close the wheel, open the existing radio panel.
**Cancel**
- `Esc` or right mouse button: hide the menu, no action, no recorder event, recapture mouse.
- Focus and the head ring stay as they were.
Number keys `1`–`6` may remain as shortcuts that skip the wheel so a smoke-test script still works. They must not fire while the wheel is open (the wheel owns `A` / `D` / `Space`). Document both in the README.
### NPC display
Stay on the procedural capsule rig (no Mixamo / GLB dependency this pass). Make them read as people at conversation distance.
- Neck between torso and head; shoes; simple hair cap (color from seed); two dark eye spheres on the face so a head has a front.
- Keep archetype props (camera, box, hat, sign, bag). Keep height / brightness variation from the visual seed.
- Collision capsule matches the scaled height so the focus ray / cone hits the body you see.
- `Head` stays a named node other systems can find (`get_node("Rig/Head")`) for the ring and the cone.
- Walk bob and the "look at the guard when noticed" cue stay.
- Do not put the internal type name (`Trespasser`, `Violent Threat`) on the body. The player should judge from clothes, props, and behavior. The debug overlay (`F`) can still print the type.
AI textures in `assets/textures/ai/` stay out of this pass unless a specific map (shirt, hair) is already the right size.
### Input map (target)
| Action | Bind |
| --- | --- |
| Walk | WASD / arrows (arrows **do not** walk while the wheel is open; they step the highlight) |
| Look | Mouse, captured |
| Open / confirm action menu | LMB, `Space` |
| Cancel menu | RMB, `Esc` |
| Step highlight | `A` / `D`, left / right arrows |
| Emergency radio | `R` |
| Flashlight | `G` |
| Door lock | `L` |
| Cycle nearby people | `Tab` |
| Wand shortcut | `C` (not `W`) |
| Inspect shortcut | `E` still allowed when the wheel is closed |
### Files this pass is expected to touch
- `src/game/guard_controller.gd` — cone focus, no look while modal, accel, floor snap.
- `src/game/npc_avatar.gd` — face / hair / neck, head marker, ring toggle.
- `src/game/ui/action_radial.gd` — new Control, `_draw` doughnut, slice pick.
- `src/game/ui/hud.gd` — host the radial; stop teaching number-key verbs as the only path.
- `src/game/game_manager.gd` — input routing, freeze flags, open/confirm/cancel.
- `project.godot` — input map.
- `src/core/strings.gd` — controls line, aim-first, no new tone-of-voice.
- `src/test/tests/test_focus_and_radial.gd` — cone pick, wrap, cancel emits nothing, `W` is not wand.
- README controls table and smoke checklist.
## Design Direction ## Design Direction
The game should feel like a tense workplace simulation with occasional crisis moments. The most satisfying outcome is not overpowering an NPC; it is recognizing the situation early and resolving it calmly. The game should feel like a tense workplace simulation with occasional crisis moments. The most satisfying outcome is not overpowering an NPC; it is recognizing the situation early and resolving it calmly.
+55
View File
@@ -1,5 +1,54 @@
# TODO # TODO
## Player control, focus ring, and radial action menu
Design is in `PLAN.md` → "Player Control, Focus, and Action Menu (next pass)". Distribution
follow-up (host `REQUIRE_SIGNIN_VIEW`) is deferred.
### Locomotion
- [ ] Stop stealing WASD: wand off `W` (use wheel + optional `C`); allow/deny `A`/`D` only while
inspect/wand is open; polite direction off `Q` as a required bind.
- [ ] Accel/decel on walk; gravity only when airborne; `floor_snap_length` for the steps.
- [ ] Mouse look only while captured and no modal UI is open. Freeze walk+look while the radial
(and inspect/wand opened from it) is up. Recapture on close.
- [ ] `Esc` closes the top modal first; with nothing open it still toggles mouse capture.
### Focus and head ring
- [ ] Replace the 8 m hairline ray as the primary picker with a focus cone (range ~10 m, head
inside a ~90 px / 10° circle around the crosshair; closest to center wins; occluded heads
skipped). Keep `Tab` as the 12 m cycle.
- [ ] Small grace so walk-bob does not drop focus.
- [ ] Billboarded ring on the focused avatar `Head`; gold idle, brighter while the radial is open;
one at a time. Optional `display_name` under the ring. No internal type name in the world.
### Radial HUD
- [ ] New `src/game/ui/action_radial.gd` doughnut: open on LMB or `Space` when someone is focused.
Opening click does not pick a slice. Slices (top, clockwise, de-escalation first): raise
hand, point, head shake, polite direction, inspect, wand (grey if no wand), warning, block,
radio assist. Emergency stays on `R` only.
- [ ] Highlight by mouse angle, or `A`/`D` / arrows. Click slice or `Space` confirms. `Esc` / RMB
closes with no action and no recorder event.
- [ ] Inspect / wand / radio slices close the wheel then open the existing panels. Freeze until
that panel closes.
- [ ] Number keys `1`–`6` may remain as closed-menu shortcuts; they must not fire while the wheel
is open.
### NPC display
- [ ] Procedural rig: neck, shoes, hair cap from seed, two eye spheres so the head has a front.
Keep props, height variation, walk bob, notice-head-turn. Collision capsule matches height.
Named `Head` node stays the attach point.
### Input, copy, tests
- [ ] Update `project.godot` input map (`action_menu`, `confirm`, `cancel` includes RMB, wand on
`C`). Update `hud.controls` and the README table + smoke checklist.
- [ ] Headless tests `src/test/tests/test_focus_and_radial.gd`: cone picks the centered head;
highlight wrap; cancel writes no recorder event; `W` does not open wand.
## Distribution follow-up (findings + host config) ## Distribution follow-up (findings + host config)
Verified via API+host credentials (2026-10-31): Verified via API+host credentials (2026-10-31):
@@ -34,3 +83,9 @@ performance hardening only.
- [ ] Profile the crowd: ensure textured materials and props hold target frame rate around a - [ ] Profile the crowd: ensure textured materials and props hold target frame rate around a
busy entrance. busy entrance.
- [ ] Add LOD or culling for distant NPCs/props if the crowd grows. - [ ] Add LOD or culling for distant NPCs/props if the crowd grows.
## Generated Asset Integration
- [ ] Regenerate the AI asset set at role-specific aspect ratios and dimensions.
- [ ] Audit, process, and wire every generated asset into the game runtime.
- [ ] Add screenshot auditing and refresh the README screenshot gallery.