Files
security-game/docs/ui-copy.md
T
zwitschi dee939c2ee
Full build / Import + headless tests (push) Successful in 42s
Full build / Full desktop build + optional release (push) Skipped
Consolidate documentation into architecture and merged guides
2026-11-04 14:06:23 +01:00

17 KiB
Raw Permalink Blame History

UI copy and button inventory

Status: dialogue, button-state, focus, modal-close, font/layout, destructive-action and end-to-end passes complete; capture/smoke sign-off remains 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 Both reset and tutorial replay ask 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 Confirm, then clear tutorial flags and restart Hollywood Confirmation dialog
Settings Reset campaign Confirm, then reset campaign progress 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; long-string/font-scale layout is covered below.

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.

Modal close and pointer recapture

GameManager._close_top_modal() is now the single close path for the top interactive layer. It handles the radial, inspection, wand, radio, timeline, Help, Settings, Pause and review before Esc/right-click falls back to target clearing or pause. _refresh_freeze() recaptures the mouse when the shift resumes; _show_start_menu() keeps the pointer visible at the menu.

Surface Close routes Result
Pause Resume, Esc, right-click Closes via one path; guard unfreezes and mouse recaptures.
Settings Apply settings, Cancel, Esc, right-click From the venue menu, both apply and cancel restore the menu. From a shift, cancel returns to the game with the guard unfrozen; apply returns to pause.
Help Close, Esc, right-click Closes only Help; it no longer opens Pause behind the overlay. Returns to whatever was underneath.
Shift review Return to menu, Esc, right-click Returns to the venue menu with the pointer visible.
Timeline Close, Esc, right-click Returns to the review and keeps the pointer visible.
Radio Cancel, Esc, right-click Closes the form, clears the action hint and recaptures the mouse.
Inspection / wand Esc, right-click Closes the panel, unfreezes the guard and recaptures the mouse.
Action wheel Esc, right-click Cancels without a log entry and recaptures the mouse.
Start menu Esc quits the application Root menu; right-click is intentionally inert.
Tutorial F1 skip Non-modal card; never blocks the pointer or movement.

The session smoke now covers menu → Settings → Cancel → menu, Help closing on right-click, and Settings cancel from pause leaving the guard unfrozen.

Destructive actions

Campaign reset and tutorial replay both open a ConfirmationDialog; pressing the button again while it is open focuses the existing dialog instead of stacking a second one. The confirmation is dismissed and the signal is emitted at most once on OK; Cancel closes without emitting. The emergency radio remains a press-and-hold action: a normal click does nothing for an emergency, a short hold cancels, a full 1.6 s hold submits once, and the release after a completed hold cannot submit again.

Regression coverage:

  • test_session_shell.gd checks reset reuse/one-shot and replay confirmation/cancel.
  • test_radio_panel.gd checks normal click, short hold, full hold and post-submit release.

Font-scale and long-string layout

The HUD scale is tested at its 0.75× and 1.75× bounds. HUD top/time labels clip instead of crossing into each other, and the target chip now measures the scaled target text: normal long names widen the chip, while an extreme synthetic name is capped at 420 px and ellipsized. Action-button labels use clip_text and ellipsis in UiStyles, so a 1.5–2× pseudo-string cannot push a panel beyond the viewport. The menu shell intentionally keeps its own fixed typography; the HUD scale setting only changes HUD text.

Regression coverage lives in test_accessibility.gd (target-chip width/cap) and test_ui_buttons.gd (long action-label minimum width). The pseudo-locale translator workflow remains a 0.10.0 task; this pass uses deterministic long strings and the maximum supported HUD scale.

End-to-end interaction flows

smoke_ui_flows.gd boots the real scene and verifies, in order:

  • inspection opens, then allow and deny both close the panel;
  • a normal radio click sends and spends battery;
  • an emergency hold sends once, spends battery and starts cooldown;
  • Help opens, filters to radio controls and closes;
  • the wand opens at The Joke Shop, then allow and deny close it;
  • returning from the review restores the venue menu.

smoke_campaign.gd covers review continue plus checkpoint resume, and smoke_timeline.gd covers review → timeline → review. These are end-to-end scene smokes, not just panel-unit tests.

Visual coverage

src/test/capture_ui_states.gd completes the screen-design catalogue by staging the states that the scene captures did not cover. The gallery now has one deterministic current-build frame for every reference:

State Capture Design reference
Start / venue menu 14-start-menu.png start_menu.png
Pause 15-pause-overlay.png pause_overlay.png
Settings 16-settings-panel.png settings_panel.png
Shift review 17-shift-review.png shift_review.png
Emergency radio 18-emergency-radio.png emergency_radio.png
Action wheel 19-radial-menu.png radial_menu.png
Crowd panic 20-crowd-panic.png crowd_panic.png
Responder arrival 21-responder-arrival.png responder_arrival.png

The crowd-panic frame is taken 70 frames after the panic event so the staged group is visibly displaced from the steps toward the street; the responder is parked on the approach with the door locked so the arrival/secure state is readable. Both are presentation staging, not gameplay changes. compare reports 21 good / 0 bad and the automatic judge 21/21 match, 0 diff. The gallery is regenerated by tools/check_screenshots.sh.

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.
  • Pseudo-locale layout and a translator workflow remain scheduled for the localization milestone, not this UI pass.

Screen-design judge report

Date: 2026-11-02 Model: automatic cheapest image+JSON selection (z-ai/glm-5.3-flash at run time) Command: python3 tools/screen_designs.py judge Result: 21/21 match, 0 review, 0 diff

The screenshot gallery was regenerated on the current build before judging, including the eight UI-state captures 14–21 added in src/test/capture_ui_states.gd. Accepted captures are in docs/screens/; the committed gallery is in docs/screenshots/.

The automatic model selection and retry/spend behavior is documented in docs/asset-tooling.md. This report intentionally records the outcome rather than generated captures, which remain local/gitignored.