From 5662baa14d35ea39a563c577fb2891ac8bbe5883 Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Sat, 31 Oct 2026 15:00:11 +0100 Subject: [PATCH] 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. --- MEMORY.md | 4 ++ PLAN.md | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ TODO.md | 55 ++++++++++++++++++++++ 3 files changed, 193 insertions(+) diff --git a/MEMORY.md b/MEMORY.md index 77289fa..1f5023e 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -15,6 +15,10 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o ## 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 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 diff --git a/PLAN.md b/PLAN.md index 5e6e3d2..eca1eec 100644 --- a/PLAN.md +++ b/PLAN.md @@ -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. +## 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 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. diff --git a/TODO.md b/TODO.md index 870029a..ab76c4b 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,54 @@ # 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) 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 busy entrance. - [ ] 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.