- 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.
28 KiB
DONE
Completed task sections moved from TODO.md. Sections are moved only when every item is finished and verified.
Project Setup
- Confirm Godot 4.x editor version to standardize on (Godot 4.3 stable, official build).
- Initialize a git repository and add a sensible root
.gitignorefor Godot and export artifacts. - Scaffold the Godot project (
project.godot, input map, folder structure). - Add the GUT test framework and a runnable headless test command. (Deviation: GUT requires the editor plugin; replaced with a lightweight custom harness in
src/test/test_runner.gdthat runs without the editor.) - Document setup, run, and test commands in
README.md.
Prototype Scene
- Build a placeholder Hollywood Boulevard building entrance with steps, sidewalk, and a restricted door (procedural
WorldBuilder). - Add a controllable guard character with first-person camera controls (
GuardController). - Add basic keyboard/mouse input: move, look, interact, and cancel.
- Add a debug overlay showing NPC intent, escalation level, and shift timer (
Ftoggles).
Core Simulation — Systems
- Implement
ShiftCoordinatorto start, advance, and end a shift. - Implement
VenueScenarioas aResourcewith locations, rules, NPC pools, and event pools. - Implement an event scheduler that emits routine traffic and occasional scripted events (
EventScheduler). - Implement
NPCControllerwith movement toward waypoints, doors, and queues (movement handled byNpcAvatar).
NPC Interaction
- Define
NpcProfileresources with intent, temperament, compliance, and escalation tolerances. - Implement a lightweight finite state machine for NPC states (
FSM). - Implement the observation phase (Level 0) without forced intervention.
- Implement simple deterrence (Level 1): raise hand, point, eye contact, and head shake.
- Implement polite dialogue prompts and reaction logic driven by escalation level and profile.
- Implement formal warnings (Level 2) and make NPC reactions depend on profile and level.
- Note (2026-10-31): the selectable dialogue tone-of-voice system (Professional/Friendly/Firm/ Suspicious) was intentionally removed from code and docs; reactions now depend solely on escalation level and NPC profile.
- Implement blocking and physical positioning (Level 3) without default force.
- Wire typical NPC reactions: leave, argue, ignore, record, move closer, or return later.
Access Verification
- Implement
AccessVerifierwith input from recognition, wristbands, IDs, tickets, credentials, and guest lists. - Add wristband inspection UI showing color, pattern, date, and event marking.
- Model valid, expired, counterfeit, mismatched, missing, and ambiguous access artifacts.
- Record admit, redirect, hold, and refuse decisions for shift review.
- Add feedback scoring for incorrectly denying an authorized person or admitting an unauthorized one.
Escalation and Radio — Core
- Implement
EscalationManagerwith explicit levels 0 through 5 and available-action gating. - Implement the security-assistance radio call (Level 4) with a report form and arrival delay.
- Implement the emergency radio (Level 5) with hold-to-confirm and a report form.
- Add radio cooldown and battery limits to prevent spam.
Incident, Reputation, and Shift Review
- Implement
IncidentRecorderemitting structured events (actor, behavior, action, evidence, escalation, outcome). - Implement body-camera evidence tied to incident events.
- Implement
ReputationEvaluatorfor peaceful resolutions, accuracy, complaints, damage, and entrance security. - Implement
CrowdSystemreactions to calm, aggressive, armed, and emergency situations. - Implement the end-of-shift review screen summarizing decisions, consequences, and justification.
Scenario Content — Venue Data
- Author a minimal Hollywood Boulevard building scenario with tourist, confused-visitor, and simple-trespasser NPCs.
- Author a rare violent-threat event that requires justified emergency escalation.
- Add The Joke Shop scenario data: ID checks, guest lists, and wand-screening policy.
- Add the Hollywood Boulevard Late Night TV Studio scenario data with the full NPC mix and credential checks.
- Add a mental-health-crisis event with careful, respectful de-escalation guidance.
Tooling and Quality
- Add headless test coverage for access verification rules.
- Add headless test coverage for escalation transitions and scoring.
- Add fixed-seed scenario tests for deterministic NPC behavior.
- Set up GitHub Actions to run headless tests on push (
ci.yml). - Add a smoke-test checklist for launching one full shift (in
README.md).
Bug Fixes — LATE shift phase
- Add a
Rules.Phase.LATEentry (start_s700) toshift_phasesin all three venues. - Confirm the LATE-tagged
violent_emergency_eventis now pulled fromEventScheduler.pull_due. - Add a test asserting the LATE phase is reached and the event is eligible in phase.
Missing PLAN Features — Protester NPC
- Add
PROTESTERtoRules.NpcTypeandNPC_TYPE_NAMES. - Add a protester profile (id
"protester") with ARGUE/IGNORE/RECORD/CALL_SOMEONE reactions. - Add a
TYPE_COLORSentry forPROTESTERinnpc_avatar.gd. - Add routine protester events to the Hollywood Boulevard and TV Studio event pools.
- Add scenario tests asserting the protester profile and events exist.
Missing PLAN Features — “Call someone else” reaction
- Add
CALL_SOMEONEtoRules.Reaction. - Handle
CALL_SOMEONEinNPCController._update_state_for_reaction(transitions to ARGUING). - Give influencer, trespasser, and protester profiles CALL_SOMEONE weights.
- Add a reaction log line in
GameManagerand aStringsentry. - Add a test that CALL_SOMEONE can be rolled and updates the FSM.
Missing PLAN Features — Door lock/secure
- Add a
DoorStatecore class with locked/unlocked state andattempt_force(). - Add a visible door lock indicator (green open / red locked) to the world.
- Add the
Lkey binding toggling the door lock, with HUD indication. - Make FORCE_ENTRY reactions fail while the door is locked (thwarted outcome).
- Add
Stringsentries and a headless test for the lock state machine.
Missing PLAN Features — Flashlight
- Add a
Gkey binding toggling the guard's flashlight. - Attach a
SpotLight3Dto the guard that toggles with the flashlight. - Widen NPC perception radius while lit at evening/late phases (
perceive_guardbonus). - Add
Stringsentries and a headless test for the perception effect.
Persistence
- Call
Save.save_shift_result(review)fromGameManager._on_shift_ended. - Load past results and show record count + best score on the shift review screen.
- Add a test that a saved review round-trips through the
Savehelper.
Navigation
- Add a
NavigationRegion3Dwith a hand-authored flat navmesh covering sidewalk, steps, and entrance. - Switch
NpcAvatarfrom direct movement toNavigationAgent3Dpath following (with direct-movement fallback). - Keep per-intent waypoint and returning-trespasser re-targeting on the navmesh.
- Make the step geometry visual-only (no collider) so pathing to the entrance works.
Localization — JSON strings
- Move the string table to
res://content/strings.en.json(41 keys). - Load the JSON at startup;
Strings.lookup/Strings.fmtsignatures unchanged. - Update the strings tests to confirm every JSON key resolves to a real value.
Distribution — Downloadable executables
- Commit
export_presets.cfgwith Linux, Windows, and macOS desktop presets. - Add
tools/build_releases.shfor headless release exports. - Add
docs/RELEASING.mdwith template installation, build, and Gitea release steps. - Add a prominent Downloads section at the top of
README.mdlinking to the release page. - Add
tools/publish_release.sh(Gitea API release + asset upload, requiresGITEA_TOKEN).
CI/CD — Gitea Actions full build
- Add
.gitea/workflows/build.ymlwith a fast checks job (import + headless tests on push/PR) and a full build job (export templates, Linux/Windows/macOS exports, artifact upload) on tags andworkflow_dispatch. - Tag builds automatically publish the Gitea release via
tools/publish_release.shusing theGITEA_TOKENsecret. - Remove the old
.github/workflows/ci.ymlto avoid double runs on hosts scanning both paths. - Document the CI flow in
docs/RELEASING.md.
Gameplay — Venue selection
- Add a
Vcontrol that cycles Hollywood Blvd → The Joke Shop → TV Studio and restarts the shift (GameManager._cycle_venue), with ahud.venue_switchedstring entry.
🎨 Visual polish pass (v0.2.0, pre-release graphics)
- Phase-based lighting: per-phase sun direction/energy/color, sky gradient
(ProceduralSkyMaterial), and ambient in
GameManager.PHASE_LIGHTING; applied on phase change and shift start. - Street lamps: poles with emissive bulbs + OmniLights on the sidewalk;
WorldBuilder.set_night_modeswitches bulbs/lights on at night. - Marquee signage: emissive “STEP LINE” strips + Label3D above the entrance.
- Street dressing: crosswalk stripes, planters, and low-poly trees.
- NPC visual variety: per-seed height/brightness variation (deterministic LCG).
- Denser routine traffic (tourists/performers/influencers/trespassers).
capture_screenshots.gdnow fast-forwards to the LATE phase and captures a night set (lamps + marquee + flashlight cone); README gallery is now 2×3 (day + night); six shots regenerated indocs/screenshots/.config/version→0.2.0;build/artifacts rebuilt and Linux binary smoke-tested.- New world/lighting headless tests (56 total).
Screenshot Refresh
- Regenerate and verify the six README screenshots from the current build.
🔴 PRIORITY — Ship downloadable builds (complete 2026-10-31)
- Install Godot 4.3 export templates locally.
./tools/build_releases.shproduces Linux/Windows/macOS artifacts (0.2.0).- Linux binary smoke-tested (headless, 0 script errors).
- Publish the release: no local token needed — the host has the
GITEA_TOKENsecret, so pushing thev0.2.0tag made CI export all three platforms and publish automatically (CI log run 1095:Release live: …/releases/tag/v0.2.0, Job succeeded). - Confirm links resolve: tag page and
/releasesreturn HTTP 200 publicly. Follow-up: anonymous asset downloads require sign-in on this host (host setting). - CI packaging follow-up was already live (
.gitea/workflows/build.ymlon tags).
🎨 AI texture generation (OpenRouter)
- Extend
tools/generate_textures.pywith an OpenRouter image-API mode (--ai,--prompt,--name,--model,--size), decoding base64 PNG responses and clamping to ≤1024px. - Verify a live
--aigeneration against a realOPENROUTER_API_KEY(generatedai/…set at 1024×1024; seedream minimum-pixel by requesting 2048 and downscaling.
🧹 Runtime hygiene + timeline/polish items
- RefCounted cycle cleanup:
FSMnow holds its owner viaWeakRef;ShiftCoordinatorisRefCounted(logic-only, no tree needs); persistence tests free theSavenode. Test suite now exits with zero “resources still in use” warnings. - Full-length shifts: the prototype now uses each scenario's
shift_duration_s(900s ≈ 10-15 min per PLAN.md) instead of a hardcoded 300s test duration. - Wand cooldown feel: a short
WAND_COOLDOWN_Srecharge between wand scans with awand.rechargemessage (Open Polish item).
📦 Installer packaging (Windows NSIS + Linux AppImage)
- Export presets:
binary_format/embed_pck=trueon all platforms — every binary is now self-contained (all game data in one file;.pckno longer shipped). - App icon:
assets/textures/icon.png(1024) + multi-sizeicon.ico, copied topackaging/windows/step-line.icoandpackaging/linux/step-line.pngvia the generator. - NSIS script
packaging/windows/step-line.nsi(installsStep Line.exe, uninstaller, shortcuts, registry entry;makensis /DVERSION=… /DARTIFACT=…). - AppImage:
packaging/linux/step-line.desktop,AppRun, appdata.xml, andbuild_appimage.sh(appimagetool,APPIMAGE_EXTRACT_AND_RUN=1). - Local end-to-end: rebuilt embedded exports and produced
step-line-0.2.0-x86_64.AppImage, ran it headless (0 script errors). - CI: NSIS + AppImage steps added to
.gitea/workflows/build.ymlbuild job; publish glob already uploadsbuild/step-line-*<ver>*(covers setup/AppImage names). - CI run 1110 verified end-to-end (v0.2.1): NSIS installer
step-line-setup-0.2.1.exebuilt,7z lshowsstep-line.exe+uninstall.exe, AppImage built (33.9 MB), releasev0.2.1published,Job succeeded.- Gotchas fixed on the way: makensis needs
-Ddefines (not/D) and absolute paths (it resolves relatives against the script dir); installed exe must be space-free (step-line.exe); icon.ico must use classic BMP entries (ImageMagick; PIL emits PNG-compressed ICOs NSIS rejects).
- Gotchas fixed on the way: makensis needs
🎮 Assets & detailed graphics pass (textures, rigs, UI feedback)
- Authored texture set via
tools/generate_textures.py(deterministic PIL): brick facade, seamless concrete, asphalt, awning, skyline, vignette, HUD icons, access-card art — all ≤1024px and power-of-two; imported underassets/textures/. - Environment: textured facade + glass door, concrete sidewalk, asphalt street, striped awning with “STEP LINE”, generated skyline backdrop planes, soft sun shadows.
- NPCs: humanoid rig (torso/head/limbs) with archetype props (camera, delivery box, hat, sign, backpack), procedural walk bob, head-turns-toward-guard cue.
- UI/feedback: inspection artifact cards, mode icons (flashlight/door/wand), escalation vignette, body-camera overlay (corner brackets + REC + shift clock).
- Assets convention documented in README;
test_asset_textures_present_and_pow2guards the pipeline (57 tests total). - (remaining, in TODO) crowd performance profiling and LOD/culling when the crowd grows.
CI — cache timeout mitigation
- Remove
actions/cache@v4from the checks job (runner cannot reach GitHub's cache service; ~2 min of retry timeouts per run; Godot import is fast without it). - Add
GODOT_SILENCE_ROOT_WARNING=1to both jobs. - Confirmed in run 1090: zero
CacheServicelines, 54/54 tests pass, job succeeds. - Document the cache situation and re-enable path in
docs/RELEASING.md. - Ignore runner logs in
.gitignore(.gitea/logs/).
Gameplay — Venue door-staff dialogue
- Add
VenueScenario.door_staff_line(context, rng)picking a seeded-random line fromdoor_staff_lines(empty for unknown contexts/venues). - Wire
GameManagerto log venue lines: id/ticket check line when inspection opens, wand-check line when screening starts, prohibited-item line on correct wand refusal, and refusal line on denied entry (The Joke Shop flavor only). - Add a headless test covering all Joke Shop contexts, unknown contexts, empty venues, and deterministic seeded picks.
Core Simulation — NPC Perception
- Implement a perception pass so NPCs react to the guard's gestures and proximity.
- Give NPCs local waypoints per intent (photo spots, queue, stage door, sidewalk exit) instead of all walking to the entrance.
- Add returning-trespasser behavior: an NPC that was directed away may circle back later in the shift.
Escalation and Radio — Responder Dispatch
- Spawn a dispatched security NPC / armed responder avatar that arrives at the venue after the call delay.
- Give the responder command, restraint, evacuation, and arrest outcomes that resolve the incident.
- Add a visible emergency-radio cooldown indicator in the HUD.
Scenario Content — Wand Screening (The Joke Shop)
- Implement the handheld wand inspection interaction with alerts from keys, belts, phones, and weapons.
- Enforce screening-before-entry flow and record false alarm / prohibited item decisions.
- Add venue-specific dialogue lines for door staff.
Polish and Accessibility
- Add aim-free targeting (Tab) and venue cycling (V). (The earlier dialogue tone picker was superseded and removed with the tone-of-voice system.)
- Add interaction alternatives that do not require precise aiming (e.g. scan-and-select pinned list of nearby NPCs).
- Add visual and audio feedback per escalation level (colors, tones, shake).
- Add audio for crowd ambience, radio responses, and confirmation prompts.
- Externalize dialogue and UI text for localization.
Generalized Game Asset Tooling
- Reuse the OpenRouter image backend for role-aware arbitrary asset generation.
- Add exact-dimension image crop and aspect-preserving resize commands.
- Audit existing assets for role-specific dimensions, aspect, alpha, and tile seams; report projection/perspective checks for visual review.
- Add automated tests and document asset-pipeline usage.
Generated Asset Integration
- Regenerate the AI asset set at role-specific aspect ratios and dimensions.
- Audit, process, and wire every generated asset into the game runtime.
- Add screenshot auditing and refresh the README screenshot gallery.
Access artifacts actually match the venue
Completed in the working tree (uncommitted with the control pass). Headless 74/74.
- Build artifacts from the event (
override_access, carried items) and the venue policy, not fromNpcTypealone. (src/core/access_loadout.gd; spawn signal is(profile, intent, npc_id, event).) - Joke Shop: valid ID+ticket, ID without ticket, expired/missing ID. Recognition does not
auto-grant on every
AUTHORIZED_GUEST. - Wand venues roll
carried_itemsper spawn (seeded); override can force a pocket. - Studio counterfeit / follow-through events carry
override_access. AccessVerifier.verify(..., already_screened)so a screened guest is not still denied.- Joke Shop / Studio
sidewalkandsteps_endwaypoints so film/photo intents do not walk to the entrance. - Wand shortcut is
C, notW. Tests:src/test/tests/test_access_loadout.gd.
Player control, focus ring, and radial action menu
Completed 2026-10-31 with ADR-0007–0010. Headless 77/77, plus a windowed smoke
(src/test/smoke_focus_radial.gd) that opens the wheel and saves screenshots.
Locomotion
- Wand off
W(nowC+ wheel).A/Dallow/deny only while inspect/wand is open (they step the wheel while it is open).Qremains a closed-menu polite shortcut. - Accel/decel on walk; gravity only when airborne;
floor_snap_length0.3. - Mouse look only while captured and
not input_locked. Freeze walk+look while radial / inspect / wand / radio / review is up. Recapture on close. cancel(Esc / RMB) closes the top modal first; with nothing open, Esc toggles mouse capture and RMB does nothing.
Focus and head ring
- Focus cone in
src/game/npc_focus.gd(~10 m, ~10°). Closest-to-forward wins; occlusion ray skips building hits.Tabstill cycles 12 m. - 8-frame miss grace so walk-bob does not drop focus.
- Billboarded torus on
Rig/Head; gold idle, brighter while the radial is open;Label3Ddisplay_name. HUD target line uses the display name, not the node name. - Screen-center crosshair.
Radial HUD
src/game/ui/action_radial.gd: LMB orSpaceopens when someone is focused. Opening click ignored for 120 ms. Slices: raise hand, point, head shake, polite, inspect, wand (grey if no wand), warning, block, radio assist. Emergency stays onR.- Highlight by mouse angle, or
A/D/ arrows. Click slice orSpaceconfirms.Esc/ RMB closes with no action (no recorder event). - Inspect / wand / radio slices close the wheel then open the existing panels. Freeze until that panel closes.
- Number keys
1–6still work when the wheel is closed.
NPC display
- Procedural rig: neck, shoes, hair cap from seed, two eye spheres. Props, height
variation, walk bob, notice-head-turn kept. Collision capsule scaled to rig height.
Named
Headis the ring attach point. Groupnpc.
Input, copy, tests
- Named input map:
action_menu(Space/LMB),confirm(Space/Enter),cancel(Esc/RMB),inspect(E),wand(C).GameManagerusesis_action_pressed. - Windowed smoke: focus ring + name, wheel open/step/confirm/cancel
(
src/test/smoke_focus_radial.gd, exits non-zero on failure). - README controls table, smoke checklist, test count, implemented/not-yet lists.
Visual polish — Skyline backdrop transparency
Found during the control smoke test; verified with PIL analysis.
- Regenerated
assets/textures/game_ready/skyline.pngwith transparent background (commented out opaque border line 234 intools/generate_textures.py). - Integrated backdrop into
WorldBuildervia_build_skyline_backdrop(): creates a PlaneMesh with T_SKYLINE texture at Vector3(-80, 12, -50) behind the building. - PIL verification confirms RGBA mode with transparent pixels in bottom rows.
Runtime boot and spawn fixes
Found while running the windowed smoke; all verified with a boot run and regenerated screenshots.
GameManagerdid not compile:radialwas typedCanvasLayer, soradial.is_open/confirm_selected()made:=inference fail. Typed asActionRadial(the parse error would have broken the whole main scene).- Camera sat at the guard's feet:
Headhad no height offset, and the new gravity pulled the guard to the floor.Headis now at y=1.6, and the guard spawns on the landing. - Guard spawned inside the building collider and fell through the world. Building volume moved back to z ∈ [-8, -4] so the door / awning / marquee sit on its front face; the guard spawns in front of the door facing the street.
- Steps rose away from the door; treads now rise toward the entrance.
- Event scheduler was polled every physics frame (~60 Hz), spawning >1200 NPCs in
seconds.
ShiftCoordinatornow accumulates time and pulls once per simulated second; regression test intest_scenarios.gd. - Live NPCs capped at
MAX_LIVE_NPCS(16); routine traffic is dropped at the cap and a scripted event evicts the oldest unfocused NPC. - Removed the skyline backdrop planes (opaque black texture). Tracked as a follow-up in
TODO.md. - README screenshots regenerated with the fixed eye height and entrance staging.
World and presentation follow-ups (found during the control smoke)
Completed 2026-10-31 (working tree). Headless 88/88; src/test/smoke_session_shell.gd and the windowed focus/radial smoke both pass.
- The skyline backdrop was removed:
assets/textures/ai/game_ready/skyline.pnghas an opaque black background, so the planes rendered as black slabs with window dots. Re-add the backdrop only with a transparent asset or a keyed shader. - Guard settles ~0.5 m above the ground after spawning on the landing. Check the capsule
offset and the spawn
y, and verify eye height while walking the steps. - Steps and the landing are visual-only (no collider). The guard walks through them; add simple step colliders or restage the landing so it does not intersect the walk path.
- Marquee text (
STEP LINE) is tiny from the sidewalk. Scale the Label3D up. - Focus ring and name tag are small at 2–3 m. Consider a larger torus / font after a real playtest.
- NPC crowd cap (
MAX_LIVE_NPCS) silently drops routine traffic at 16. Add a short despawn/lifetime for ordinary guests so the entrance does not fill up with standers.
Session shell
Completed 2026-10-31 (working tree). Headless 88/88; src/test/smoke_session_shell.gd and the windowed focus/radial smoke both pass.
The game boots into a live 900 s shift. Save declares settings.json and never uses it.
V cycles venues by restarting the shift.
- Startup menu: pick venue, start shift, quit. Keyboard + click. Replaces
Vas the player-facing path (Vmay stay as a debug bind). - Pause overlay when
Escreleases the mouse (or a dedicated pause key): resume, restart shift, settings, quit to menu. - Settings: mouse look sensitivity, persist through
Save/settings.json. Load on boot. - Shift review "Run another shift" returns to the venue menu, not a silent restart of the same scenario.
Documentation drift
Completed 2026-10-31 (working tree).
- README controls table, smoke checklist, test count, and implemented/not-yet lists refreshed for the control pass (77 tests).
PLAN.mdTech Stack / Planned Commands still say GUT and GitHub Actions; the project usessrc/test/test_runner.gdand.gitea/workflows/build.yml.- MEMORY: door_staff_lines log through
GameManager; 300 s default removed.
Remaining PLAN verbs (radial exists)
Completed 2026-10-31 (working tree). Headless 98/98; src/test/smoke_plan_verbs.gd exercises all five verbs.
- Eye contact as a Level 1 action (PLAN / glossary). Distinct from raise-hand. Add a radial slice if it ships.
- "I recognize this person" is a player decision recorded as evidence, not a pre-attached
artifact on every staff/guest type. Staff still auto-recognize when
override_accessis empty (AccessLoadout). - Formal warning can activate the body-camera / security-light flag (PLAN Level 2). Overlay is currently always on; toggling should write an incident event.
- Blocking stations the guard between the person and the door (PLAN Level 3). Today it only
sends
apply_player_action(BLOCKING). - "Request reason for entry" as a Level 3 ask, with a short NPC reply from
dialogue_lines.
Crowd presentation
Completed 2026-10-31 (working tree). Headless 105/105; src/test/smoke_crowd.gd verifies rubbernecking and panic clearing. A 16-NPC entrance measured ~1.2 ms/frame (824 fps, vsync off), so no LOD/culling was added at the current cap.
CrowdSystem is a mood/attention float. PLAN wants people watching, gathering, and panicking.
- On
aggressive_behavior/emergency_responder, nearby idle NPCs stop and face the incident (rubberneck). Oncrowd_panic, they move away from the door. - Profile a busy entrance with textured materials and props; add LOD or culling for distant NPCs if frame time drops.
Emergency aftermath
Completed 2026-10-31 (working tree). Headless 113/113; src/test/smoke_emergency.gd covers the unjustified-call investigation, the evacuation aftermath, and the review copy.
Responder outcomes (command / restraint / evacuation / arrest) are log lines. PLAN also
wants evacuation, a closed street, and an investigation when the call was unjustified.
evacuationoutcome actually clears the entrance (NPCs leave, door stays locked).- Unjustified emergency (low danger, no weapon) writes an investigation note on the shift review and applies the existing false-report / reputation hit.
- Review copy names the responder outcome, not only battery and crowd %.
Body-camera replay
Completed 2026-10-31 (working tree). Headless 120/120; src/test/smoke_timeline.gd opens the timeline from the review, steps through every event, and returns to the review.
- Shift review: a timeline of recorded incident events (ADR-0005 already stores them). Scrub or step through; no live 3D replay required in the first version.