- Link every modal's enabled controls into a wrapping UiFocus ring and give each screen a default focus target. - Add gamepad A/B bindings for ui_accept/ui_cancel; include Start menu footer actions and Continue in its focus ring. - Skip hidden/disabled controls, close focus traps via per-modal cancel behavior, and return review to the menu on cancel. - Record the focus audit and add regression coverage.
11 KiB
UI copy and button inventory
Status: dialogue, button-state and keyboard/gamepad focus passes complete; layout/end-to-end audit in progress for 0.9.3.
Screen inventory
| Screen / panel | Owner | Primary actions | Notes |
|---|---|---|---|
| Start / venue menu | start_menu.gd |
Continue shift, choose venue, Help, Settings, Quit | Campaign status and free-play venues share this screen. |
| Tutorial card | tutorial_overlay.gd |
F1 skip | Non-blocking; no mouse capture. |
| HUD | hud.gd |
Observe, target, inspect, wheel, radio, flashlight, body camera, door, pause | No interactive controls; must stay mouse-transparent. |
| Action wheel | action_radial.gd |
Choose de-escalation/access/radio action | Confirm with Space/click; cancel with Esc/RMB. |
| Access inspection | hud.gd |
A = admit, D = refuse | Real access evidence cards. |
| Wand screening | hud.gd |
A = admit, D = refuse | Shows alert and carried items. |
| Radio / emergency | radio_panel.gd |
Send request / hold Send emergency, Cancel | Normal click vs emergency hold. |
| Pause | pause_overlay.gd |
Resume, restart, Settings, quit to menu | Esc resumes. |
| Settings | settings_panel.gd |
Apply, Cancel, Replay tutorial, Reset campaign, rebind controls | Destructive reset asks for confirmation. |
| Help | help_overlay.gd |
Filter controls, Close | Policy card and current bindings. |
| Shift review | shift_review.gd |
Continue campaign, timeline, return to menu, quit | Campaign next step is shown here. |
| Timeline | event_timeline.gd |
Previous, Next, Close | Review evidence stepper. |
Canonical terms
- Admit / refuse — access decisions. Do not alternate with “allow / deny” or “grant / reject” in player-facing controls.
- Warning — Level 2.
- Blocking — Level 3 positioning; do not call it “force”.
- Assistance — Level 4 radio call; emergency is Level 5.
- Body camera — always two words.
- Shift review — the end-of-shift screen; “review” alone is acceptable in prose.
- Continue campaign — advance to the next venue.
- Return to menu — leave the review to the venue menu (it does not start a new shift by itself).
- Send request — non-emergency assistance; Send emergency — emergency call.
- Hollywood Boulevard — Day / Night — the two scenery variants in the venue menu. Use an em dash and title case for the variant; do not call them “maps” in player-facing copy.
Button labels and states
| Surface | Label | Behavior | State audit |
|---|---|---|---|
| Start menu | Continue shift — | Resume/advance campaign | Hidden when complete; venue-specific |
| Start menu | Venue rows | Start free-play/campaign venue | Hover/focus/pressed styled |
| Start menu | Help | Open Help overlay | Standard button |
| Start menu | Settings | Open Settings | Standard button |
| Start menu | Quit game | Exit application | Destructive; label clarifies scope |
| Tutorial | F1 skip tutorial | Skip lesson | Keyboard only, non-blocking |
| Action wheel | Stop/Look/Point/No/Ask/Inspect/Wand/Warn/Block/Reason/Recognize/Radio | Choose verb | Hover/selection highlight |
| Inspection | A = admit · D = refuse entry | Access decision | Keyboard hints, policy text |
| Wand | A = admit · D = refuse entry | Screening decision | Keyboard hints, item/alert text |
| Radio | Send request | Submit assistance | Normal click |
| Radio | Send emergency | Submit emergency | Hold-to-confirm; Cancel aborts |
| Radio | Cancel | Close form | Standard button |
| Pause | Resume | Resume shift | Default focus |
| Pause | Restart shift | Restart current shift | Standard button |
| Pause | Settings | Open Settings | Standard button |
| Pause | Quit to menu | Return to venue menu | Standard button |
| Settings | Apply settings | Persist settings | Standard button |
| Settings | Cancel | Discard edits | Standard button |
| Settings | Replay tutorial | Clear tutorial flags and restart Hollywood | Standard button |
| Settings | Reset campaign | Confirmed campaign reset | Confirmation dialog |
| Help | Close | Close overlay | Standard button |
| Review | Continue campaign | Start next venue | Hidden when campaign complete |
| Review | Body camera timeline | Open event timeline | Standard button |
| Review | Return to menu | Return to venue menu | Standard button |
| Review | Quit game | Exit application | Standard button |
| Timeline | Previous / Next / Close | Step/close evidence | Standard buttons |
Button/label issues found
- HUD inspection and wand used “allow/deny” versus “admit/refuse”; both now use A = admit · D = refuse entry.
- Shift review’s old “Run another shift” actually returned to the menu; renamed Return to menu.
- Radio emergency action used all-caps while assistance used sentence case; both are now sentence case, with the warning in the panel title/hold hint.
- Normal radio send was previously wired only to the emergency hold handlers; fixed in 0.8.1 and covered by
test_radio_panel.gd. - “Quit” was ambiguous between menu and application exit; both application-exit buttons now read Quit game.
- Settings “Apply” was made verb+object as Apply settings.
Dialogue audit
The NPC reason verb now uses the authored reason context instead of always
falling back to the general dialogue lines. Every shared archetype has at least
one role-specific reason answer, so asking a staff member, guest, delivery
worker or performer no longer produces an unexplained silence. Deliberately
silent profiles can still return an empty context line.
| Surface | Finding | Result |
|---|---|---|
| Shared NPC reasons | request_reason() ignored context_lines["reason"]; staff, guests, delivery, performers, intoxicated people, crisis and threat profiles had no answer. |
Fixed; all 12 shared archetypes now answer in character. |
| Door staff | Only The Joke Shop authored door lines. | Hollywood has wristband lines; the Studio has ticket/credential/guest-list lines; the inspection log selects the venue context. |
| Radio | “Call cancelled” could read as if a call had been sent; the description placeholder was ungrammatical. | “Call not sent — hold the button to confirm.” and “Describe the person and their behavior.” |
| Tutorial / Help | The wheel verb is Ask, but help and the HUD key strip called it “polite direction”. | Both now say Ask; the tutorial tells the player to choose Ask after closing the panel. |
| Review / campaign | Continue/return/quit labels and campaign-next copy checked. | No ambiguity found; no change. |
| Repetition | Shared tourist/confused/delivery lines recur across venues by design; role-specific reason lines prevent every archetype sounding alike. | Accepted for the shared-archetype model. |
| Localization length | Longest authored NPC/door line is 56 characters; longest UI string is hud.controls at 172 characters. |
Dialogue fits the HUD log; the 1.5–2× string/font-scale layout test remains a separate 0.9.3 task. |
Button state audit
All Button controls now use src/game/ui/ui_styles.gd, which defines explicit
normal, hover, pressed, focus and disabled styles plus the
matching font colours. This removes the engine-theme fallback that previously
appeared when a custom dark button was disabled or focused by keyboard.
| Surface | States / behavior checked | Result |
|---|---|---|
| Start menu | Venue rows, Continue, Help, Settings, Quit; focus clamped to the list; Continue hidden when the campaign is complete. | All buttons styled; hidden state is intentional, not a disabled state. |
| Tutorial | F1 skip is a keyboard shortcut, not a button; its hint is non-interactive. | Keyboard-only by design; no hover/focus state to style. |
| Pause | Resume, Restart, Settings, Quit to menu; Resume receives focus on open. | All four states styled; focus is distinct from hover. |
| Settings | Replay tutorial, Reset campaign, key-binding rows, Apply settings, Cancel; rebinding shows “Press…” while listening. | All action buttons styled; reset-confirmation dialog OK/Cancel styled; fields keep the engine control theme. |
| Help | Close; Esc still closes. | Styled; no disabled state is used. |
| Shift review | Continue campaign, Body camera timeline, Return to menu, Quit game; Continue hidden when the campaign is complete. | All styled; hidden state is intentional. |
| Timeline | Previous/Next/Close; Previous is disabled at the first event, Next at the last, and both when empty. | Disabled state verified in test_event_timeline.gd; Close stays enabled. |
| Radio | Send request, Send emergency, Cancel. Emergency hold uses press/release, so the action button stays enabled while holding. | All styled; disabling it during the hold would break confirmation. |
| Toggles | CheckButton/OptionButton controls keep the engine theme. | Intentional: the shared style targets action buttons, not checkbox/radio affordances. |
Keyboard and gamepad focus
UiFocus builds an explicit focus ring for each modal, skips hidden and
disabled controls, and makes Up/Down, Tab/Shift+Tab and the D-pad/stick wrap.
GuardController._ensure_gamepad_bindings() adds A to ui_accept and B to
ui_cancel; the built-in directional UI actions already carry D-pad/stick
events.
| Surface | Default focus | Cancellation / trap check |
|---|---|---|
| Start menu | First visible target: Continue when present, otherwise the first venue. | A/Enter/Space activates the focused row; B/Esc quits; Continue, venue rows and footer actions share one wrapping ring. |
| Pause | Resume. | B/Esc resumes; the four actions wrap. |
| Settings | First slider. | B/Esc cancels; focus ring covers sliders, toggles, difficulty, rebinds, Apply and Cancel; sliders keep Left/Right for value adjustment. |
| Help | Search field. | B/Esc closes; Search and Close share a ring. |
| Shift review | Continue when available, otherwise the timeline. | B/Esc returns to the menu; all four actions are in the ring and hidden Continue is skipped. |
| Timeline | First enabled action: Next when Previous is disabled, Close when empty. | B/Esc closes; Previous/Next leave the ring when disabled so focus cannot land on a dead button. |
| Radio | Send action. | B/Esc cancels; form fields, action and Cancel are linked; A confirms the focused action. |
| Tutorial | None; F1 is the documented skip shortcut. | Keyboard-only by design, no focus trap because the card is mouse-transparent and non-modal. |
Remaining audit work
- Player-facing labels are now routed through
content/strings.en.json; numeric values, empty-state clears and debug-only text remain in code by design. - Focus/hover/pressed/disabled states and keyboard/gamepad rings are recorded above.
- Test long strings and 1.75x HUD font scale.
- Capture every dialog/button state and run
screen_designs.py compareplus the pinned judge.