Control pass (ADRs 0007-0010) stays first. Added the holes the audit found: venue-correct access artifacts (override_access unread), session shell, leftover PLAN verbs, crowd rubberneck, emergency aftermath, replay, NPC poses, real ambience, a few Joke Shop/Studio events, and doc drift. Host sign-in remains deferred. Generated-asset integration stays out (already in DONE).
9.4 KiB
TODO
Checked against PLAN.md, ADRs 0001–0010, README.md, DONE.md, and src/ (Godot 4.3
prototype, release v0.2.1, 57 headless tests). Do the control pass first; it unblocks
everything that uses focus or verbs.
Host REQUIRE_SIGNIN_VIEW stays deferred.
Player control, focus ring, and radial action menu
Design: PLAN.md → "Player Control, Focus, and Action Menu" and ADRs 0007–0010.
Locomotion
- Stop stealing WASD: wand off
W(wheel + optionalC); allow/denyA/Donly while inspect/wand is open; polite direction offQas a required bind. - Accel/decel on walk; gravity only when airborne;
floor_snap_lengthfor 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.
Esccloses 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
Tabas 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. Optionaldisplay_nameunder the ring. No internal type name in the world. - Add a small screen-center crosshair so the focus circle has a visible origin.
Radial HUD
- New
src/game/ui/action_radial.gddoughnut: open on LMB orSpacewhen 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 onRonly. - Highlight by mouse angle, or
A/D/ arrows. Click slice orSpaceconfirms.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–6may 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
Headnode stays the attach point.
Input, copy, tests
- Update
project.godotinput map (action_menu,confirm,cancelincludes RMB, wand onC). Updatehud.controlsand 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;Wdoes not open wand.
Access artifacts actually match the venue
GameManager._artifacts_for maps NpcType → a wristband or free RECOGNITION. Joke Shop
events set override_access {has_ticket, has_id} and nobody reads it, so club guests inspect
as "recognized staff" instead of ID/ticket. Studio counterfeit/follow events have the same hole.
- Build artifacts from the event (
override_access, carried items) and the venue policy, not fromNpcTypealone. - Joke Shop: valid ID+ticket, ID without ticket, expired/mismatched/missing ID, no ID when
required. Recognition must not auto-grant on every
AUTHORIZED_GUEST. - Give some guests
carried_items(keys / belt / phone / prohibited) so wand alerts are not always clear. - Studio: counterfeit wristband/credential and "follow authorized guest" use the scripted event data, not the generic type map.
- Headless tests: Joke Shop
guest_no_idis not granted by recognition;guest_validgrants only with ID+ticket; wand venues still requirecontroller.screened.
Session shell
The game boots into a live 900 s shift. Save declares settings.json and never uses it.
V cycles venues by restarting the shift.
- Startup menu: pick venue, start shift, quit. Keyboard + click. Replaces
Vas the player-facing path (Vmay stay as a debug bind). - Pause overlay when
Escreleases the mouse (or a dedicated pause key): resume, restart shift, settings, quit to menu. - Settings: mouse look sensitivity, persist through
Save/settings.json. Load on boot. - Shift review "Run another shift" returns to the venue menu, not a silent restart of the same scenario.
Remaining PLAN verbs (after the radial exists)
- Eye contact as a Level 1 action (PLAN / glossary). Distinct from raise-hand.
- "I recognize this person" is a player decision recorded as evidence, not a pre-attached artifact on every staff/guest type.
- Formal warning can activate the body-camera / security-light flag (PLAN Level 2). Overlay is currently always on; toggling should write an incident event.
- Blocking stations the guard between the person and the door (PLAN Level 3). Today it only
sends
apply_player_action(BLOCKING). - "Request reason for entry" as a Level 3 ask, with a short NPC reply from
dialogue_lines.
Crowd presentation
CrowdSystem is a mood/attention float. PLAN wants people watching, gathering, and panicking.
- On
aggressive_behavior/emergency_responder, nearby idle NPCs stop and face the incident (rubberneck). Oncrowd_panic, they move away from the door. - Profile a busy entrance with textured materials and props; add LOD or culling for distant NPCs if frame time drops.
Emergency aftermath
Responder outcomes (command / restraint / evacuation / arrest) are log lines. PLAN also
wants evacuation, a closed street, and an investigation when the call was unjustified.
evacuationoutcome actually clears the entrance (NPCs leave, door stays locked).- Unjustified emergency (low danger, no weapon) writes an investigation note on the shift review and applies the existing false-report / reputation hit.
- Review copy names the responder outcome, not only battery and crowd %.
Body-camera replay
- Shift review: a timeline of recorded incident events (ADR-0005 already stores them). Scrub or step through; no live 3D replay required in the first version.
NPC theater
Intents sit / photo / film / perform already pick waypoints. Bodies still idle-stand.
- Sit pose at
steps_end. Photo: brief raise of the influencer/tourist prop. Performer stays put and plays the hat/sign. After the ADR-0010 rig pass, not before.
Audio
AudioFeedback is sine beeps. DONE called that "crowd ambience"; it is not.
- Loop a low crowd bed that rises with
CrowdSystem.mood. - Radio open/close crackle on the existing radio panel, still headless-safe (no-op without an audio server).
Accessibility
- Escalation colors that still read in grayscale (shape or label, not hue alone).
- HUD font scale in settings.
Scenario content still missing vs PLAN lists
- Joke Shop: guest on the reservation list without ID; borrowed/mismatched ID; guest refuses wand screening; late arrival during a performance.
- Studio: filming-boundary repeat (influencer ignores a posted line); celebrity-arrival
crowd spike (more rubberneckers, not only one
arrivespawn). - Building: "activate body camera" and "request reason" once those verbs exist, used in at least one authored event.
Documentation drift
- README Development Status: test count is 57, not 52. "Not yet implemented" should match this file (control pass, artifact wiring, session shell, replay, crowd presentation).
PLAN.mdTech Stack / Planned Commands still say GUT and GitHub Actions; the project usessrc/test/test_runner.gdand.gitea/workflows/build.yml.- MEMORY: drop "door_staff_lines not surfaced" (they log through
GameManager) and the obsolete "prototype defaults to 300 s" decision.
Distribution follow-up (host config, deferred)
Verified via API+host credentials (2026-10-31):
- Repo is public (
private:false) and account has admin rights. - Release
v0.2.1now carries all five artifacts (portable binaries + NSIS installer + AppImage) — the two installers were missing after CI publish (script uploaded only the portables);tools/publish_release.shfixed to include them and re-uploaded via API. - Remaining host setting: instance-wide
[service] REQUIRE_SIGNIN_VIEW=truemakes anonymous browsing/downloads redirect to login (API-confirmed repo is public; this is a serverapp.inisetting, not an API or repo option). Advised path for the host admin: setREQUIRE_SIGNIN_VIEW = falseinapp.iniunder[service], restart Gitea, then verifycurl -sI https://git.allucanget.biz/zwitschi/security-game/releases/tag/v0.2.1returns 200 (not a login redirect) and an anonymous asset download succeeds. Alternative: mirror downloads (Itch.io / GitHub Releases) for anonymous users.
Open Polish
- Keep
%placeholders stable inStrings.fmtwhen adding keys (positional formatting).
Tooling (when an editor workflow exists)
- IDE/editor-based GUT integration. Until then the custom headless runner stays.
- Optional later: author venues as
.tresresources instead ofScenarioFactoryfunctions. Not required for the control pass.