Add body-camera timeline to the shift review
- New src/game/ui/event_timeline.gd flattens every recorded incident event (ADR-0005) into one time-ordered list and shows time, person, and a one-line summary. Step with Left/Right or the buttons; Esc returns to the review. No live 3D replay in this first version. - Shift review gains a "Body camera timeline" button that hands the review to the timeline; the review dim is hidden while it is open (stacked overlays made the screen nearly black) and restored on close. - ShiftCoordinator records started_at_ms so the review can stamp events as mm:ss. - 8 new string keys for the timeline UI and the review button. Tests/docs - Headless suite 113 -> 120 (test_event_timeline.gd), new smoke_timeline.gd that opens the timeline from the review, steps through every event, and closes back. - README, TODO section moved to DONE, MEMORY notes. Version 0.2.6.
This commit is contained in:
1 parent
921bab796e
commit
1b976bf351
14 files changed
+614
-71
No files matched your search
+93
-51
@@ -9,11 +9,11 @@ screenshots against.
|
||||
|
||||
Two commands:
|
||||
|
||||
generate write one reference design per screen to docs/screens/designs/.
|
||||
generate write one reference image per screen to docs/screens/designs/.
|
||||
Two modes:
|
||||
--ai photorealistic mockups via the OpenRouter image API
|
||||
(default; requires OPENROUTER_API_KEY, same as
|
||||
tools/asset_pipeline.py).
|
||||
--ai realistic 3D game scenes and in-game screen designs via
|
||||
the OpenRouter image API (default; requires
|
||||
OPENROUTER_API_KEY, same as tools/asset_pipeline.py).
|
||||
--draft deterministic wireframe sketches drawn with Pillow
|
||||
(no network or API key; good for CI and layout checks).
|
||||
|
||||
@@ -54,6 +54,23 @@ ASPECT = 16 / 9
|
||||
# the model asset_pipeline.py already trusts for non-alpha roles).
|
||||
DEFAULT_MODEL = "openai/gpt-5-image-mini"
|
||||
|
||||
# Shared art direction, appended to every prompt so the generated frames read
|
||||
# as one coherent game. "scene" entries are first-person 3D world views;
|
||||
# "ui" entries are in-game panels overlaid on a softened scene.
|
||||
SCENE_STYLE = (
|
||||
"rendered in a modern 3D game engine, first-person perspective, cinematic "
|
||||
"lighting, physically based materials, soft shadows, ambient occlusion, "
|
||||
"subtle depth of field, photorealistic detail, no watermark or UI clutter"
|
||||
)
|
||||
UI_STYLE = (
|
||||
"clean modern in-game user interface, dark translucent panels, readable "
|
||||
"sans-serif labels, consistent game UI colour theme, crisp vector text"
|
||||
)
|
||||
|
||||
# Landscape source ratio requested from the API before cropping to 16:9.
|
||||
# 3:2 is the validated non-square ratio for the default model (see MEMORY.md).
|
||||
SOURCE_ASPECT = "3:2"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Screen catalogue. Each entry is grounded in a doc; `prompt` feeds --ai and
|
||||
# `regions` feed --draft (normalised rects + a short label). `ref` names the
|
||||
@@ -63,6 +80,7 @@ DEFAULT_MODEL = "openai/gpt-5-image-mini"
|
||||
SCREENS = [
|
||||
{
|
||||
"name": "venue_overview",
|
||||
"kind": "scene",
|
||||
"title": "Venue overview",
|
||||
"source": "README screenshot 01",
|
||||
"ref": "01-venue-overview.png",
|
||||
@@ -71,11 +89,11 @@ SCREENS = [
|
||||
"sidewalk, street lamps, planters, crosswalk, and building "
|
||||
"signage/marquee under early-morning light.",
|
||||
"prompt": (
|
||||
"First-person video game screenshot, security guard standing at the "
|
||||
"top of wide concrete steps outside a Hollywood Boulevard building "
|
||||
"entrance, daytime early morning light, sidewalk with street lamps, "
|
||||
"planters and a crosswalk, building facade with awning and marquee "
|
||||
"signage, clean UI-free gameplay view, 16:9"),
|
||||
"Realistic 3D first-person game scene: a security guard's view from "
|
||||
"the top of wide concrete steps outside a grand Hollywood Boulevard "
|
||||
"building entrance, early-morning golden light, sidewalk with street "
|
||||
"lamps, planters, a crosswalk and distant pedestrians, detailed brick "
|
||||
"facade with a striped awning and glowing marquee sign"),
|
||||
"regions": [
|
||||
(0.00, 0.55, 1.00, 1.00, "steps + sidewalk"),
|
||||
(0.20, 0.08, 0.80, 0.60, "building facade / marquee"),
|
||||
@@ -85,6 +103,7 @@ SCREENS = [
|
||||
},
|
||||
{
|
||||
"name": "targeting_npc",
|
||||
"kind": "scene",
|
||||
"title": "Targeting an NPC",
|
||||
"source": "README screenshot 02 · ADR-0008 cone focus + head ring",
|
||||
"ref": "02-targeting-npc.png",
|
||||
@@ -93,10 +112,10 @@ SCREENS = [
|
||||
"the ring, and the HUD shows a target line with the "
|
||||
"interaction hint.",
|
||||
"prompt": (
|
||||
"First-person video game screenshot, a pedestrian NPC walking toward "
|
||||
"a security guard on a city street, a thin pale-gold ring floating "
|
||||
"around the NPC's head as a selection marker, small name label under "
|
||||
"the ring, minimal HUD text at the bottom of the screen, daytime, 16:9"),
|
||||
"Realistic 3D first-person game scene: a pedestrian NPC walking "
|
||||
"toward the camera on a city sidewalk, a thin pale-gold ring floating "
|
||||
"around the NPC's head as a selection marker, a small name label "
|
||||
"under the ring, minimal HUD target text at the bottom, daytime"),
|
||||
"regions": [
|
||||
(0.35, 0.15, 0.65, 0.60, "NPC in focus"),
|
||||
(0.44, 0.10, 0.56, 0.16, "head ring"),
|
||||
@@ -105,6 +124,7 @@ SCREENS = [
|
||||
},
|
||||
{
|
||||
"name": "access_inspection",
|
||||
"kind": "ui",
|
||||
"title": "Access inspection",
|
||||
"source": "README screenshot 03 · ADR-0003 access verification",
|
||||
"ref": "03-access-inspection.png",
|
||||
@@ -113,11 +133,11 @@ SCREENS = [
|
||||
"pattern, date, and event marking, plus allow and deny "
|
||||
"prompts.",
|
||||
"prompt": (
|
||||
"First-person video game screenshot with a centered access inspection "
|
||||
"panel: a card showing a wristband or ID credential with a photo "
|
||||
"placeholder, colour band, date and event marking, and two buttons "
|
||||
"labelled allow and deny, dark translucent panel over a city street "
|
||||
"scene, 16:9"),
|
||||
"Realistic 3D game screenshot with an in-game access inspection panel "
|
||||
"overlaid: a centered card showing a wristband or ID credential with a "
|
||||
"photo placeholder, colour band, date and event marking, and two "
|
||||
"buttons labelled Allow and Deny, dark translucent panel over a "
|
||||
"softly blurred city street background"),
|
||||
"regions": [
|
||||
(0.25, 0.10, 0.75, 0.90, "inspection panel"),
|
||||
(0.30, 0.18, 0.70, 0.60, "artifact card"),
|
||||
@@ -127,6 +147,7 @@ SCREENS = [
|
||||
},
|
||||
{
|
||||
"name": "radio_report",
|
||||
"kind": "ui",
|
||||
"title": "Radio report",
|
||||
"source": "README screenshot 04 · PLAN level 4 radio assistance",
|
||||
"ref": "04-radio-report.png",
|
||||
@@ -134,10 +155,10 @@ SCREENS = [
|
||||
"description, behaviour, whether weapons were seen, and "
|
||||
"whether anyone is in immediate danger, plus a send action.",
|
||||
"prompt": (
|
||||
"First-person video game screenshot with a radio dispatch report "
|
||||
"panel open: form fields for location, description, behaviour, "
|
||||
"weapons seen, and immediate danger, with a send button, dark "
|
||||
"translucent panel over a city street scene, 16:9"),
|
||||
"Realistic 3D game screenshot with an in-game radio dispatch panel "
|
||||
"overlaid: form fields for location, description, behaviour, weapons "
|
||||
"seen and immediate danger, and a Send button, dark translucent panel "
|
||||
"over a softly blurred city street background"),
|
||||
"regions": [
|
||||
(0.30, 0.08, 0.70, 0.92, "radio panel"),
|
||||
(0.34, 0.16, 0.66, 0.30, "location / description"),
|
||||
@@ -147,15 +168,17 @@ SCREENS = [
|
||||
},
|
||||
{
|
||||
"name": "late_night_avenue",
|
||||
"kind": "scene",
|
||||
"title": "Late night avenue",
|
||||
"source": "README screenshot 05",
|
||||
"ref": "05-late-night-avenue.png",
|
||||
"description": "Night-time view down Hollywood Boulevard: street lamps "
|
||||
"lit, marquee glowing, dark blue street, no flashlight.",
|
||||
"prompt": (
|
||||
"First-person video game screenshot at night, a dark Hollywood "
|
||||
"Boulevard avenue with lit street lamps, a glowing marquee sign, "
|
||||
"deep blue night sky, no flashlight beam, 16:9"),
|
||||
"Realistic 3D first-person game scene at night: a dark Hollywood "
|
||||
"Boulevard avenue receding into the distance, lit street lamps, a "
|
||||
"glowing neon marquee sign, deep blue night sky, wet reflective "
|
||||
"pavement, no flashlight"),
|
||||
"regions": [
|
||||
(0.00, 0.55, 1.00, 1.00, "dark avenue"),
|
||||
(0.15, 0.25, 0.85, 0.55, "glowing marquee"),
|
||||
@@ -165,15 +188,17 @@ SCREENS = [
|
||||
},
|
||||
{
|
||||
"name": "night_flashlight",
|
||||
"kind": "scene",
|
||||
"title": "Night flashlight",
|
||||
"source": "README screenshot 06",
|
||||
"ref": "06-night-flashlight.png",
|
||||
"description": "Night shift: the guard's flashlight beam cuts through the "
|
||||
"dark toward the entrance, illuminating the steps and door.",
|
||||
"prompt": (
|
||||
"First-person video game screenshot at night, a flashlight beam "
|
||||
"cutting through darkness toward a building entrance and steps, "
|
||||
"bright spotlight pool on the door, dark surroundings, 16:9"),
|
||||
"Realistic 3D first-person game scene at night: a flashlight beam "
|
||||
"cutting through darkness toward a building entrance and steps, a "
|
||||
"bright spotlight pool illuminating the door, dark surroundings and "
|
||||
"deep shadows"),
|
||||
"regions": [
|
||||
(0.35, 0.30, 0.65, 0.80, "flashlight beam pool"),
|
||||
(0.40, 0.20, 0.60, 0.35, "lit door"),
|
||||
@@ -182,6 +207,7 @@ SCREENS = [
|
||||
},
|
||||
{
|
||||
"name": "radial_menu",
|
||||
"kind": "ui",
|
||||
"title": "Radial action menu",
|
||||
"source": "ADR-0009 · content/strings.en.json",
|
||||
"ref": None,
|
||||
@@ -190,11 +216,11 @@ SCREENS = [
|
||||
"Wand, Warn, Block, Reason, Recognize, Radio. The focused "
|
||||
"person's display name sits in the centre.",
|
||||
"prompt": (
|
||||
"First-person video game screenshot with a radial pie menu centered "
|
||||
"on screen, twelve labelled slices in a circle reading Stop, Look, "
|
||||
"Point, No, Ask, Inspect, Wand, Warn, Block, Reason, Recognize, "
|
||||
"Radio, a highlighted slice, a name in the center of the ring, "
|
||||
"over a city street scene, 16:9"),
|
||||
"Realistic 3D game screenshot with an in-game radial action menu "
|
||||
"overlaid: a round doughnut wheel centered on screen with twelve "
|
||||
"labelled slices reading Stop, Look, Point, No, Ask, Inspect, Wand, "
|
||||
"Warn, Block, Reason, Recognize, Radio, one slice highlighted, a name "
|
||||
"in the center of the ring, over a softly blurred city street scene"),
|
||||
"regions": [
|
||||
(0.38, 0.12, 0.62, 0.88, "radial wheel (12 slices)"),
|
||||
(0.46, 0.44, 0.54, 0.56, "name"),
|
||||
@@ -203,6 +229,7 @@ SCREENS = [
|
||||
},
|
||||
{
|
||||
"name": "shift_review",
|
||||
"kind": "ui",
|
||||
"title": "Shift review",
|
||||
"source": "PLAN reputation / shift review · architecture.md ShiftReview",
|
||||
"ref": None,
|
||||
@@ -210,10 +237,10 @@ SCREENS = [
|
||||
"escalation choices, incident list, battery and crowd "
|
||||
"outcomes, and reputation/score feedback.",
|
||||
"prompt": (
|
||||
"Video game end-of-shift review screen: a performance summary panel "
|
||||
"listing incident decisions, escalation choices, reputation score, "
|
||||
"crowd reaction and battery use, with a continue button, dark themed "
|
||||
"UI, 16:9"),
|
||||
"Realistic 3D game end-of-shift review screen: a full-screen "
|
||||
"performance summary panel listing incident decisions, escalation "
|
||||
"choices, a reputation score, crowd reaction and battery use, with a "
|
||||
"Continue button, dark themed game UI"),
|
||||
"regions": [
|
||||
(0.30, 0.06, 0.70, 0.94, "review panel"),
|
||||
(0.36, 0.14, 0.64, 0.24, "score / reputation"),
|
||||
@@ -223,15 +250,16 @@ SCREENS = [
|
||||
},
|
||||
{
|
||||
"name": "start_menu",
|
||||
"kind": "ui",
|
||||
"title": "Start menu",
|
||||
"source": "MEMORY session shell (StartMenu, layer 20)",
|
||||
"ref": None,
|
||||
"description": "The boot screen: a venue-selection menu (Hollywood "
|
||||
"Boulevard, The Joke Shop, TV Studio) before a shift.",
|
||||
"prompt": (
|
||||
"Video game start menu screen: a title and three venue selection "
|
||||
"buttons labelled Hollywood Boulevard, The Joke Shop, and TV Studio, "
|
||||
"dark themed menu over a city street backdrop, 16:9"),
|
||||
"Realistic 3D game start menu screen: a game title and three venue "
|
||||
"selection buttons labelled Hollywood Boulevard, The Joke Shop and TV "
|
||||
"Studio, dark themed menu over a softly blurred city street backdrop"),
|
||||
"regions": [
|
||||
(0.25, 0.10, 0.75, 0.26, "title"),
|
||||
(0.36, 0.36, 0.64, 0.44, "Hollywood Boulevard"),
|
||||
@@ -241,14 +269,15 @@ SCREENS = [
|
||||
},
|
||||
{
|
||||
"name": "pause_overlay",
|
||||
"kind": "ui",
|
||||
"title": "Pause overlay",
|
||||
"source": "MEMORY session shell (PauseOverlay, layer 18)",
|
||||
"ref": None,
|
||||
"description": "Pause menu: resume, restart, settings, and quit-to-menu.",
|
||||
"prompt": (
|
||||
"Video game pause menu overlay: buttons labelled Resume, Restart, "
|
||||
"Settings, and Quit to Menu, dark translucent overlay over a paused "
|
||||
"city street scene, 16:9"),
|
||||
"Realistic 3D game pause menu overlay: buttons labelled Resume, "
|
||||
"Restart, Settings and Quit to Menu, dark translucent overlay over a "
|
||||
"paused, softly blurred city street scene"),
|
||||
"regions": [
|
||||
(0.38, 0.26, 0.62, 0.74, "pause menu"),
|
||||
(0.42, 0.32, 0.58, 0.38, "Resume"),
|
||||
@@ -259,13 +288,14 @@ SCREENS = [
|
||||
},
|
||||
{
|
||||
"name": "settings_panel",
|
||||
"kind": "ui",
|
||||
"title": "Settings",
|
||||
"source": "MEMORY session shell (SettingsPanel, layer 22)",
|
||||
"ref": None,
|
||||
"description": "Settings: look sensitivity and HUD font scale controls.",
|
||||
"prompt": (
|
||||
"Video game settings screen: sliders labelled look sensitivity and "
|
||||
"HUD font scale, dark themed settings panel, 16:9"),
|
||||
"Realistic 3D game settings screen: sliders labelled look sensitivity "
|
||||
"and HUD font scale, dark themed settings panel"),
|
||||
"regions": [
|
||||
(0.30, 0.16, 0.70, 0.84, "settings panel"),
|
||||
(0.36, 0.30, 0.64, 0.40, "look sensitivity"),
|
||||
@@ -274,15 +304,17 @@ SCREENS = [
|
||||
},
|
||||
{
|
||||
"name": "emergency_radio",
|
||||
"kind": "ui",
|
||||
"title": "Emergency radio",
|
||||
"source": "PLAN level 5 · strings radio.emergency_dispatched",
|
||||
"ref": None,
|
||||
"description": "Hold-to-confirm emergency radio panel with a confirmation "
|
||||
"prompt and a cooldown indicator.",
|
||||
"prompt": (
|
||||
"Video game emergency radio hold-to-confirm panel: a large warning "
|
||||
"prompt with a hold progress bar and a cooldown label, red accent, "
|
||||
"dark translucent overlay over a city street, 16:9"),
|
||||
"Realistic 3D game emergency radio hold-to-confirm panel: a large "
|
||||
"warning prompt with a hold progress bar and a cooldown label, red "
|
||||
"accent, dark translucent overlay over a softly blurred city street "
|
||||
"scene"),
|
||||
"regions": [
|
||||
(0.30, 0.28, 0.70, 0.72, "emergency panel"),
|
||||
(0.36, 0.36, 0.64, 0.46, "confirm prompt"),
|
||||
@@ -349,10 +381,19 @@ def _draw_wireframe(screen, path):
|
||||
# AI generation (reuses the OpenRouter backend from generate_textures).
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _compose_prompt(screen):
|
||||
"""Append the shared art direction to a screen's content prompt."""
|
||||
style = UI_STYLE if screen.get("kind") == "ui" else SCENE_STYLE
|
||||
return f"{screen['prompt']}. {style}. 16:9 widescreen composition."
|
||||
|
||||
|
||||
def _generate_ai(screen, model, request_size):
|
||||
prompt = _compose_prompt(screen)
|
||||
# Request a landscape source and crop to the 16:9 capture dimensions rather
|
||||
# than a square that loses framing when centre-cropped.
|
||||
image = generate_textures.generate_image(
|
||||
screen["prompt"], model, request_size, transparent=False,
|
||||
aspect_ratio="1:1")
|
||||
prompt, model, request_size, transparent=False,
|
||||
aspect_ratio=SOURCE_ASPECT)
|
||||
image = ImageOps.fit(image, (WIDTH, HEIGHT),
|
||||
method=Image.Resampling.LANCZOS)
|
||||
if image.mode in ("RGBA", "LA", "P"):
|
||||
@@ -360,7 +401,8 @@ def _generate_ai(screen, model, request_size):
|
||||
path = _design_path(screen["name"])
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
image.save(path)
|
||||
print(f"wrote {path.relative_to(ROOT)} {image.size}")
|
||||
shown = path.relative_to(ROOT) if path.is_relative_to(ROOT) else path
|
||||
print(f"wrote {shown} {image.size}")
|
||||
|
||||
|
||||
def generate(args):
|
||||
|
||||
Reference in new issue
Block a user