Compare commits

...
1 Commits
Author SHA1 Message Date
zwitschi 3025ba1cef Capture the wand panel, escalation HUD and body-camera timeline
Full build / Full desktop build + optional release (push) Successful in 3m56s
Full build / Import + headless tests (push) Successful in 35s
The three newest catalogue screens had AI mockups but no staged capture, so
compare and judge skipped them. capture_screenshots.gd now continues past the
night set:

- switches to The Joke Shop at 620 s (EVENING still has routine spawns), frames a
  guest, and retries the wand until it alerts; if nobody carries an alerting item
  it stages the documented keys false-alarm case.
- applies a formal warning (arms the body camera) then blocking, jumps to 760 s
  for night lighting, and captures the HUD at L3.
- closes the open incident and opens the body-camera timeline, which now shows
  the warning / body-camera / blocking rows.

Wired the refs for wand_screening, escalation_hud and body_camera_timeline, and
regenerated the escalation mockup as a third-person over-the-shoulder night shot
so the reference matches the game camera. Added Hud.clear_log() to drop
target-cycling noise from staged shots. README gallery lists the three captures.

Verification: headless 182/182, Python tooling 53/53, compare 9 good / 0 bad,
judge: wand match 0.90, timeline match 1.00, escalation review, 0 diff.
Version 0.3.3.
2026-11-01 00:29:58 +01:00
17 changed files with 145 additions and 18 deletions

No files matched your search

+18
View File
@@ -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.
+5
View File
@@ -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
+6 -4
View File
@@ -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
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 838 KiB

After

Width:  |  Height:  |  Size: 836 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 KiB

+1 -1
View File
@@ -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")
+6
View File
@@ -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:
+94 -1
View File
@@ -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:
+15 -12
View File
@@ -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"),
],
},
]