diff --git a/DONE.md b/DONE.md index 3a8d3d5..00f65c2 100644 --- a/DONE.md +++ b/DONE.md @@ -691,3 +691,21 @@ tooling **53/53**. `OPENROUTER_API_KEY` by hand. - [x] Verified every reference is 1280x720, reviewed the new images visually, and confirmed `list` reports 19 designs (12 kept, 7 new). + +## Staged Captures For The New Screens + +Completed 2026-11-01 (v0.3.3). `screen_designs.py compare` reports **9 good / 0 bad**; +vision judge: wand **match** (0.90), timeline **match** (1.00), escalation **review**, 0 diff. +Python tooling **53/53**, headless **182/182**. + +- [x] `capture_screenshots.gd` now continues past the night set: switches to The Joke Shop, + stages a guest, retries the wand until it alerts (falls back to the documented keys + false alarm), applies formal warning + blocking, and opens the body-camera timeline. +- [x] New captures `07-wand-screening.png`, `08-escalation-hud.png`, `09-body-camera-timeline.png` + added to `docs/screenshots/` and to the README gallery. +- [x] Wired `ref` for `wand_screening`, `escalation_hud` and `body_camera_timeline` so + `compare` and `judge` include them (9 of 19 screens now have a staged capture). +- [x] Regenerated the escalation HUD mockup as a third-person over-the-shoulder night shot so + the reference matches the game camera. +- [x] Added `Hud.clear_log()` so staged captures drop target-cycling noise; the escalation shot + keeps the real reaction lines. diff --git a/MEMORY.md b/MEMORY.md index 489f299..30fa39c 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -41,6 +41,11 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o catalogue screens. Only the six staged captures (01–06) are compared/judged; the other 13 have no `capture`, so adding staged captures for the wand panel, timeline and escalation HUD would extend automated visual checks. `generate` now calls `_load_env_file()`. +- **Staged captures (v0.3.3):** `capture_screenshots.gd` now emits nine shots. States 7–10 switch + to The Joke Shop at elapsed 620 s (EVENING has routine spawns; LATE has none), retry the wand + until it alerts, then jump to 760 s after staging for night lighting. The timeline needs + `recorder.close_all_open()`, because `to_dict()` only lists closed incidents. The escalation + HUD mockup is third-person now; `judge` still classes it `review` (no diff). - **Backlog complete:** every planned TODO section is done and moved to `DONE.md`; `TODO.md` now only lists optional future ideas (live 3D replay, NPC LOD, more venues, real audio, prop atlas, AI mockups for the new catalogue entries, image-to-mesh props). GUT was consciously not diff --git a/README.md b/README.md index 894acb8..424167e 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,10 @@ The central design goal is simple: | ![Venue overview: the Hollywood Boulevard entrance with steps, sidewalks, street lamps, and signage](docs/screenshots/01-venue-overview.png) | ![Targeting an approaching NPC with the interaction HUD](docs/screenshots/02-targeting-npc.png) | | ![Access inspection: wristband/credential check with allow/deny prompt](docs/screenshots/03-access-inspection.png) | ![Radio report form: emergency dispatch panel](docs/screenshots/04-radio-report.png) | | ![Late night: street lamps lit, marquee glowing, dark Hollywood Boulevard](docs/screenshots/05-late-night-avenue.png) | ![Night shift: guard flashlight cutting through the dark](docs/screenshots/06-night-flashlight.png) | +| ![Wand screening at The Joke Shop: false alarm on keys](docs/screenshots/07-wand-screening.png) | ![Escalation HUD at L3 with body camera armed and reaction log](docs/screenshots/08-escalation-hud.png) | +| ![Body-camera timeline: formal warning, body camera on, blocking](docs/screenshots/09-body-camera-timeline.png) | | -*Screenshots are generated by [`src/test/capture_screenshots.gd`](src/test/capture_screenshots.gd) from the current build — day and late-night shifts.* +*Screenshots are generated by [`src/test/capture_screenshots.gd`](src/test/capture_screenshots.gd) from the current build — day, late-night, wand screening, escalation, and shift-review timeline.* ## ⬇️ Downloads @@ -32,7 +34,7 @@ The central design goal is simple: > Downloads are public (no sign-in required) since the host's instance-wide > `REQUIRE_SIGNIN_VIEW` was disabled on 2026-10-31. > -> **Latest: v0.3.2** — every screen in the design catalogue now has an AI reference mockup. +> **Latest: v0.3.3** — staged captures for the wand panel, escalation HUD, and body-camera timeline now feed the visual checks. > Releases are built automatically when a `vX.Y.Z` tag is pushed > (see [`docs/RELEASING.md`](docs/RELEASING.md)); manual builds use > `./tools/build_releases.sh`. Installer/AppImage packaging runs in the same pipeline @@ -258,11 +260,11 @@ python3 tools/screen_designs.py judge --only 03- # one capture python3 tools/screen_designs.py judge --dry-run # build requests, no network ``` -The catalogue covers 19 screens: the six README captures plus the start menu, +The catalogue covers 19 screens: the nine README captures plus the start menu, pause overlay, settings, shift review, radial menu, emergency radio, wand screening, Joke Shop queue, TV Studio arrival, crowd panic, responder arrival, body-camera timeline, and the escalation HUD. All 19 have an AI reference in -`docs/screens/designs/`; the six screens with staged captures are also compared +`docs/screens/designs/`; the nine screens with staged captures are also compared or judged against the running game. `compare` flags gross drift: wrong aspect ratio, undersized frame, near-blank diff --git a/docs/screens/designs/escalation_hud.png b/docs/screens/designs/escalation_hud.png index fea24d0..db7a498 100644 Binary files a/docs/screens/designs/escalation_hud.png and b/docs/screens/designs/escalation_hud.png differ diff --git a/docs/screenshots/01-venue-overview.png b/docs/screenshots/01-venue-overview.png index 6574f3a..29a4377 100644 Binary files a/docs/screenshots/01-venue-overview.png and b/docs/screenshots/01-venue-overview.png differ diff --git a/docs/screenshots/02-targeting-npc.png b/docs/screenshots/02-targeting-npc.png index 9e2b033..a667c40 100644 Binary files a/docs/screenshots/02-targeting-npc.png and b/docs/screenshots/02-targeting-npc.png differ diff --git a/docs/screenshots/03-access-inspection.png b/docs/screenshots/03-access-inspection.png index 47d9ace..e310f36 100644 Binary files a/docs/screenshots/03-access-inspection.png and b/docs/screenshots/03-access-inspection.png differ diff --git a/docs/screenshots/04-radio-report.png b/docs/screenshots/04-radio-report.png index 63b76e6..ba05a36 100644 Binary files a/docs/screenshots/04-radio-report.png and b/docs/screenshots/04-radio-report.png differ diff --git a/docs/screenshots/05-late-night-avenue.png b/docs/screenshots/05-late-night-avenue.png index f68ec21..0028e46 100644 Binary files a/docs/screenshots/05-late-night-avenue.png and b/docs/screenshots/05-late-night-avenue.png differ diff --git a/docs/screenshots/06-night-flashlight.png b/docs/screenshots/06-night-flashlight.png index ade8a05..5e33ae1 100644 Binary files a/docs/screenshots/06-night-flashlight.png and b/docs/screenshots/06-night-flashlight.png differ diff --git a/docs/screenshots/07-wand-screening.png b/docs/screenshots/07-wand-screening.png new file mode 100644 index 0000000..235fc83 Binary files /dev/null and b/docs/screenshots/07-wand-screening.png differ diff --git a/docs/screenshots/08-escalation-hud.png b/docs/screenshots/08-escalation-hud.png new file mode 100644 index 0000000..f5da08b Binary files /dev/null and b/docs/screenshots/08-escalation-hud.png differ diff --git a/docs/screenshots/09-body-camera-timeline.png b/docs/screenshots/09-body-camera-timeline.png new file mode 100644 index 0000000..d6042fa Binary files /dev/null and b/docs/screenshots/09-body-camera-timeline.png differ diff --git a/project.godot b/project.godot index c57c752..01ce9f7 100644 --- a/project.godot +++ b/project.godot @@ -8,7 +8,7 @@ config_version=5 config/name="Step Line" config/description="Workplace security simulation about observation, access verification, and proportionate escalation." -config/version="0.3.2" +config/version="0.3.3" run/main_scene="res://scenes/main.tscn" config/features=PackedStringArray("4.3", "GL Compatibility") diff --git a/src/game/ui/hud.gd b/src/game/ui/hud.gd index 7341843..7ba8031 100644 --- a/src/game/ui/hud.gd +++ b/src/game/ui/hud.gd @@ -402,6 +402,12 @@ func set_escalation(level: int) -> void: if vignette_rect: vignette_rect.modulate = Color(tint.r, tint.g, tint.b, 0.05 + 0.2 * float(level) / 5.0) +## Dev helper: drop queued log lines so a staged screenshot shows only the +## lines that belong to the moment. +func clear_log() -> void: + _log_lines.clear() + log_label.text = "" + func log(text: String) -> void: _log_lines.append(text) if _log_lines.size() > 8: diff --git a/src/test/capture_screenshots.gd b/src/test/capture_screenshots.gd index 06fee40..5a845f1 100644 --- a/src/test/capture_screenshots.gd +++ b/src/test/capture_screenshots.gd @@ -13,6 +13,7 @@ var state := 0 var state_frames := 0 var targeted := false var camera_staged := false +var wand_attempts := 0 func _initialize() -> void: root.size = Vector2i(1280, 720) @@ -89,7 +90,7 @@ func _advance() -> void: if gm != null: gm.radio_panel.hide() gm.hud.set_action_hint("") - gm.coordinator.elapsed_s = 750.0 + gm.coordinator.elapsed_s = 620.0 state = 5 state_frames = 0 5: @@ -110,9 +111,101 @@ func _advance() -> void: gm.call("_toggle_flashlight") if state_frames >= 30: _shoot("06-night-flashlight.png") + # Move to The Joke Shop for the wand/shift-review set, and keep + # the late-night phase so the UI sits on the night scene. + if gm != null: + gm.call("_toggle_flashlight") + gm.switch_scenario("joke_shop") + gm.coordinator.elapsed_s = 620.0 state = 7 + state_frames = 0 + 7: + # Wand screening: wait for guests and keep re-targeting until one + # accepts the screen (some authored Joke Shop guests refuse it). + if state_frames > 40 and state_frames % 20 == 0 and gm != null and gm.npcs.size() > 0: + # Prefer a guest whose items make the wand alert, so the panel + # shows the interesting case instead of "Alert: none". + if gm.wand_open: + if _wand_alerts(): + state = 8 + state_frames = 0 + elif wand_attempts >= 6: + # Nobody nearby carried an alerting item: stage the documented + # false-alarm case (keys) on this guest and re-open the panel. + var guest: Object = gm.call("_targeted_controller") + if guest != null and guest.profile != null: + var false_alarm: Array[String] = ["keys"] + guest.profile.carried_items = false_alarm + gm.call("_toggle_wand_screening") + gm.call("_toggle_wand_screening") + state = 8 + state_frames = 0 + else: + gm.call("_toggle_wand_screening") + wand_attempts += 1 + else: + gm.call("_cycle_nearby_target") + _face_target() + gm.call("_toggle_wand_screening") + wand_attempts += 1 + elif state_frames > 600 and gm != null: + # Fallback: real controller, panel shown directly. + var controller: Object = gm.call("_targeted_controller") + if controller != null and controller.profile != null: + gm.hud.show_wand(controller, gm.wand_screener.screen(controller.profile.carried_items)) + state = 8 + state_frames = 0 + 8: + if state_frames == 1 and gm != null: + gm.hud.clear_log() + _stage_closeup() + if state_frames >= 45: + _shoot("07-wand-screening.png") + if gm != null: + if gm.wand_open: + gm.call("_toggle_wand_screening") + # Real escalation path: formal warning arms the body camera + # (a timeline row), then blocking raises the meter to L3. + gm.call("_apply_interaction", 2, "formal warning") + gm.call("_apply_interaction", 3, "blocking") + state = 9 + state_frames = 0 + 9: + if state_frames == 1 and gm != null: + # Jump into the late phase now that guests exist: LATE has no + # routine spawn pool, so this happens after staging the target. + gm.coordinator.elapsed_s = 760.0 + _stage_closeup() + if state_frames >= 45: + _shoot("08-escalation-hud.png") + if gm != null and gm.coordinator != null: + # Close the open incident so the recorder's review includes it + # (to_dict only lists closed incidents). + gm.coordinator.recorder.close_all_open("unresolved") + gm.call("_open_timeline", {"incidents": gm.coordinator.recorder.to_dict()}) + state = 10 + state_frames = 0 + 10: + if state_frames >= 30: + _shoot("09-body-camera-timeline.png") quit(0) +## Put the guard a couple of steps in front of the target so a staged UI capture +## frames the person the panel is about. +func _wand_alerts() -> bool: + if gm == null or gm.hud == null or gm.hud.wand_text == null: + return false + return gm.hud.wand_text.text.contains("Alert: YES") + +func _stage_closeup() -> void: + var target: Node3D = gm.targeted_npc + if target == null or not is_instance_valid(target): + return + gm.guard.global_position = target.global_position + Vector3(0, -0.4, 2.2) + gm.guard.global_position.y = 0.1 + _face_target() + gm.guard.head.rotation.x = deg_to_rad(1.0) + func _face_target() -> void: var target: Node3D = gm.targeted_npc if target == null: diff --git a/tools/screen_designs.py b/tools/screen_designs.py index a3a46b6..9667c26 100644 --- a/tools/screen_designs.py +++ b/tools/screen_designs.py @@ -342,7 +342,7 @@ SCREENS = [ "kind": "ui", "title": "Wand screening", "source": "README screenshot gallery · PLAN The Joke Shop · src/core/wand_screener.gd", - "ref": None, + "ref": "07-wand-screening.png", "description": "Handheld wand screening panel for The Joke Shop: the alert " "result, the items the guest is carrying, and allow/deny " "buttons. A phone, keys or a belt buckle can raise a false " @@ -452,7 +452,7 @@ SCREENS = [ "kind": "ui", "title": "Body-camera timeline", "source": "README screenshot gallery · ADR-0005 · src/game/ui/event_timeline.gd", - "ref": None, + "ref": "09-body-camera-timeline.png", "description": "The shift review's body-camera timeline: a text list of every " "recorded incident event stamped mm:ss with the person and the " "action or outcome, one row highlighted, and previous/next " @@ -475,22 +475,25 @@ SCREENS = [ "kind": "ui", "title": "Escalation HUD", "source": "README accessibility note · Hud.escalation_meter (shape meter · level)", - "ref": None, + "ref": "08-escalation-hud.png", "description": "The in-shift HUD: a shape meter plus numeric escalation level " "(`Escalation: Blocking [###--] L3/5`), the targeted person's " "name, flashlight / door / body-camera indicators, shift clock, " "and the reaction log.", "prompt": ( - "Realistic 3D game screenshot with a minimal in-game HUD overlaid: an " - "escalation meter drawn as a shape bar with the text Blocking and L3/5, " - "a target name at the bottom centre, small indicator icons for flashlight, " - "door lock and body camera, a shift clock at the top right and a short " - "reaction log at the left, over a softly blurred daytime street"), + "Third-person over-the-shoulder 3D game screenshot at night: a security " + "guard in the foreground facing a guest in front of a venue entrance, " + "with a minimal in-game HUD overlaid. Bottom left: an escalation meter " + "drawn as a shape bar reading Escalation: Blocking [###--] L3/5. Centre: " + "a small target name label above the guest. Top right: a shift clock, " + "reputation and crowd percentage. Left: a short stacked reaction log. " + "Bottom centre: a control hint bar. Realistic lighting, muted colours"), "regions": [ - (0.30, 0.02, 0.70, 0.10, "escalation shape meter"), - (0.30, 0.86, 0.70, 0.96, "target name + hint"), - (0.78, 0.02, 0.98, 0.14, "shift clock"), - (0.02, 0.60, 0.26, 0.95, "reaction log"), + (0.02, 0.80, 0.34, 0.95, "escalation shape meter"), + (0.35, 0.28, 0.75, 0.40, "target name label"), + (0.78, 0.02, 0.98, 0.16, "shift clock + crowd"), + (0.02, 0.20, 0.36, 0.65, "reaction log"), + (0.02, 0.94, 0.98, 0.99, "control hints"), ], }, ]