- Add a 'Verify Windows installer contents' step (7z l shows Step Line.exe + uninstall.exe) so the TODO verification items are proven by CI output - Rebuild 0.2.1 binaries (embedded PCK) and AppImage locally; smoke clean; 57/57 tests
108 lines
11 KiB
Markdown
108 lines
11 KiB
Markdown
# MEMORY
|
||
|
||
Session memory for durable decisions, state, and gotchas. Keep concise, remove obsolete entries.
|
||
|
||
## Decisions
|
||
|
||
- Engine: Godot 4.3 stable (official editor build, `~/.local/bin/godot`). The system's preinstalled `godot-runner` (4.7.2, Fedora) cannot load projects from arbitrary paths because it was compiled without path overrides; use the official editor binary instead.
|
||
- Language: GDScript. All cross-file references use explicit `preload()` constants (not `class_name` globals) so scripts compile in headless `--script` runs without the editor's class cache.
|
||
- Test framework deviation: the project uses a custom minimal headless test runner (`src/test/test_runner.gd`) instead of GUT, because GUT needs the editor plugin. CI runs it with `--headless`.
|
||
- Eventing: no autoload signal bus. `ShiftCoordinator` owns an `EventBus` instance (`src/core/event_bus.gd`) passed to subsystems; subsystems emit only when a bus is assigned. Autoloads at parse time are unreliable under `--script`.
|
||
- Static signals are not available in Godot 4.3 GDScript ("Expected func or var after static") — do not use them.
|
||
- GDScript lambdas capture local variables **by value**; mutating a captured local inside a lambda does not affect the outer variable. Use member functions for accumulators in tests.
|
||
- Prototype shift duration defaults to 300 s for testing; production targets 10-15 minutes per PLAN.md.
|
||
- RNG: NPC reactions and event scheduling are seedable for deterministic tests.
|
||
|
||
## Current State
|
||
|
||
- **Release v0.2.0 is LIVE**: pushing the `v0.2.0` tag made Gitea Actions run the build job
|
||
and publish automatically (host has the `GITEA_TOKEN` secret). Tag page and `/releases` are
|
||
public (HTTP 200). Caveat: anonymous **asset downloads** redirect to login on this host
|
||
(host setting — tracked in TODO). Future releases = bump `config/version`, `git tag vX.Y.Z`,
|
||
push. No local token needed.
|
||
- Core simulation, three scenarios, headless tests (**57 passing**), and a first-person prototype with interaction HUD, access inspection, radio (assist + emergency hold-to-confirm), debug overlay, and shift review are implemented and green.
|
||
- NPC avatars now walk to per-intent waypoints (`VenueScenario.waypoint_for_intent`): enter/trespass/deliver/arrive → entrance, photo/film/perform/wander/roam → sidewalk, sit → steps_end. `NpcAvatar.setup` takes an explicit `door_position`.
|
||
- Returning-trespasser behavior: `NpcProfile.return_probability` (trespasser 0.6, mental-crisis 0.4, others 0). `NPCController._maybe_schedule_return()` moves a directed-away NPC to `LOITERING_DISTANT`; `tick_return(delta)` fires after 20-60s and transitions back to `APPROACH`. `NpcAvatar._advance_fsm` re-targets the door on return.
|
||
- Perception pass: `NPCController.perceive_guard(distance)` returns `{noticed, band, reaction}` with bands `unaware` (>6m), `aware` (2-6m), `near` (<2m). Near + temperament ≥0.6 → ARGUE, else COMPLY. `GameManager._perception_pass()` feeds real guard distance each physics tick and logs first-time notice.
|
||
- Responder dispatch: `RadioSystem` tracks `pending_calls` and decrements `arrival_delay_s` in `tick`; emits `responder_arrived` when a call's delay elapses. `RadioSystem.responder_outcome(call)` maps danger/weapons → command/restraint/evacuation/arrest. `GameManager._on_responder_arrived` spawns a `RESPONDER` avatar at the entrance and logs the outcome. HUD shows an emergency-radio cooldown label via `_update_radio_cooldown`.
|
||
- Wand screening (Joke Shop): `WandScreener` (new `src/core/wand_screener.gd`) classifies carried items as prohibited (`weapon/knife/gun/blade`) vs false-alarm (`keys/belt/phone/coins/watch/buckle`) vs clear. `NpcProfile.carried_items` holds guest items. `GameManager` binds `W` to `_toggle_wand_screening` and `A`/`D` to `decide_wand`; `decide_access` blocks admission at wand venues until `controller.screened` is true. `VenueScenario.door_staff_lines` holds venue-specific door-staff dialogue (Joke Shop has id/ticket/wand/refusal/prohibited lines).
|
||
- Design change (2026-10-31): the selectable dialogue tone-of-voice system was **removed**.
|
||
`Rules.Tone`/`TONE_NAMES`, `NpcProfile.tone_modifiers`, and the tone param on
|
||
`NPCController.roll_reaction/apply_player_action` no longer exist; `NpcProfile.dialogue_lines`
|
||
is a flat `Array[String]`; `GameManager` uses `_apply_interaction(level, action_name)`;
|
||
the `T` key is gone. HUD `tone_label` removed and `hud.tone_prefix` string deleted from JSON.
|
||
- Polish/accessibility: venue cycling (`V` cycles `VENUE_ORDER` and restarts the shift), aim-free
|
||
targeting (`Tab` cycles nearby NPCs within `NEARBY_RADIUS`), color-coded escalation indicator
|
||
(`Hud.set_escalation`), procedural audio (`AudioFeedback` node generates sine tones for
|
||
confirm/radio/escalation/deny), and a centralized `Strings` table (`src/core/strings.gd`, `lookup`/`fmt`)
|
||
backed by `content/strings.en.json`. NOTE: `Strings.get` was renamed to `Strings.lookup` because
|
||
`get` collides with `Object.get`.
|
||
- Newly added: LATE shift phase (start 700s) with a reachability test; PROTESTER archetype + events + colors; CALL_SOMEONE reaction; `DoorState` core class + `L` binding + visual indicator; flashlight (`G`, SpotLight3D, perception bonus at evening/late); persistence wired (`Save.save_shift_result` on shift end, history + best score on the review screen); `NavigationRegion3D` flat navmesh + `NavigationAgent3D` NPC paths (steps now visual-only); strings moved to `res://content/strings.en.json` (41 keys, loader keeps `lookup`/`fmt`); `export_presets.cfg` (Linux/Windows/macOS) + `tools/build_releases.sh` + `docs/RELEASING.md`; README Downloads section.
|
||
- Run tests: `godot --headless --path . --script res://src/test/test_runner.gd`
|
||
- Boot check: `godot --headless --path . --quit-after 120`
|
||
- Screenshot tools: `godot --path . -s res://src/test/take_screenshot.gd` (single shot to `user://screenshot.png`); `godot --path . -s res://src/test/capture_screenshots.gd` (staged README gallery shots to `user://screenshots/`, copied to `docs/screenshots/`).
|
||
- Release build verified locally: export templates for 4.3 installed at `~/.local/share/godot/export_templates/4.3.stable/`; `./tools/build_releases.sh` produced Linux/Windows/macOS artifacts for 0.1.0 in `build/` and the Linux binary passed a headless smoke test. Publishing to Gitea still needs a token (see `tools/publish_release.sh`, `docs/RELEASING.md`).
|
||
- CI: the repo now carries a **Gitea Actions** workflow at `.gitea/workflows/build.yml` (checks on
|
||
push/PR; full export + artifact upload + optional release publish on tags and `workflow_dispatch`).
|
||
A repo secret `GITEA_TOKEN` is required for tag releases. The old `.github/workflows/ci.yml` was
|
||
removed so hosts scanning both paths do not double-run.
|
||
- Venue cycling: `V` switches Hollywood Blvd → The Joke Shop → TV Studio and restarts the shift.
|
||
- Venue door-staff dialogue: `VenueScenario.door_staff_line(context, rng)` and GameManager logging
|
||
(id/ticket/wand-check, refusal, prohibited-item) — Joke Shop flavor.
|
||
- CI cache: `.gitea/workflows/build.yml` no longer uses `actions/cache` (the runner can't reach
|
||
the GitHub cache service; import is fast). `GODOT_SILENCE_ROOT_WARNING=1` set on jobs.
|
||
Logs live in `.gitea/logs/` after each run.
|
||
- Visual polish (v0.2.0): phase lighting (GameManager.PHASE_LIGHTING + ProceduralSkyMaterial),
|
||
street lamps + night mode in WorldBuilder.set_night_mode, marquee Label3D, crosswalk/planters/
|
||
trees, NPC per-seed variation. capture_screenshots.gd shoots day + fast-forwarded LATE night
|
||
(6 shots in docs/screenshots/). Version bumped to 0.2.0; binaries rebuilt.
|
||
- Assets pipeline: `python3 tools/generate_textures.py` produces all authored PNGs under
|
||
`assets/textures/` (≤1024px, pow2); regenerate after changing the script, then run
|
||
`godot --headless --import` before tests. 57 headless tests guard the pipeline.
|
||
- AI texture mode: `python3 tools/generate_textures.py --ai` generates textures via the
|
||
OpenRouter image API (`POST https://openrouter.ai/api/v1/images`, model
|
||
`bytedance-seed/seedream-4.5` by default, base64 PNG in `data[].b64_json`). Requires
|
||
`OPENROUTER_API_KEY` (a gitignored `.env` in the repo root holds it locally; source it
|
||
with `set -a && . ./.env && set +a`). `--prompt`/`--name`/`--model`/`--size` override the
|
||
default batch. Output is downscaled to ≤1024px. NOTE: seedream rejects sizes below
|
||
3,686,400 px (1920×1920), so the script requests `GEN_SIZE=2048` and downscales — do not
|
||
lower `GEN_SIZE` below 1920. Verified end-to-end 2026-10-31 (3 textures generated).
|
||
`AI_TEXTURES` now covers all 15 game textures (world surfaces, awning, skyline, vignette,
|
||
4 HUD icons, 5 access cards) with a `transparent` flag per entry. NOTE: seedream-4.5
|
||
ignores `background: transparent` and returns fully opaque images (alpha=255), so the
|
||
`ai/` icons/vignette/skyline are opaque — they are NOT drop-in replacements for the
|
||
authored transparent PNGs. Use a transparency-capable model (or post-process) if alpha
|
||
is needed.
|
||
- Detailed graphics: textured facade/awning/skyline, humanoid NPC rigs + props + walk bob +
|
||
attention head-turn, inspection artifact cards, mode icons, escalation vignette, body-cam
|
||
overlay (REC + shift clock). Screenshot set regenerated (`docs/screenshots/`, day + night).
|
||
- Polish: wand scan cooldown (`WAND_COOLDOWN_S` + `wand.recharge` message).
|
||
- NOTE: collaborators occasionally add uncommitted work to this shared tree (e.g. the radio
|
||
easter egg); check `git status` before committing and leave foreign changes untouched unless asked.
|
||
|
||
## Known Issues / Notes
|
||
|
||
- Reference cycles are fixed: `FSM` holds its owner via `WeakRef`, `ShiftCoordinator` is
|
||
`RefCounted`, and persistence tests free the `Save` node — the suite exits with no
|
||
"resources still in use" warnings (was ~13). If a future subsystem introduces a cycle,
|
||
repeat the WeakRef pattern.
|
||
- Shift length: `GameManager` uses the scenario's `shift_duration_s` (900s ≈ 10-15 min);
|
||
tests and the screenshot tool override elapsed time directly.
|
||
- Headless (dummy renderer) logs "Parameter m is null" from `mesh_get_surface_count`; it does not occur with a real GL driver and does not affect logic.
|
||
- Steps are visual-only (no collider) so NPCs can path to the entrance; the guard spawns on the landing so it never needs to climb.
|
||
- Persistence tests write into the real `user://shift_results.json` (harmless local pollution of the demo history).
|
||
- macOS export required `rendering/textures/vram_compression/import_etc2_astc=true` in `project.godot` (universal build demands ETC2 ASTC enabled).
|
||
- Joke Shop `door_staff_lines` authored but not yet surfaced in the prototype UI (tracked in TODO).
|
||
|
||
## Open Questions
|
||
|
||
- Whether the responder avatar should be playable/controllable or purely scripted.
|
||
- Localization target languages beyond English.
|
||
- macOS notarization approach for broad distribution.
|
||
|
||
## Plan vs TODO Gaps (evaluated 2026-10-31, all resolved)
|
||
|
||
All previously identified gaps are now implemented and covered by tests/smoke items: PROTESTER archetype, door lock/secure, flashlight, CALL_SOMEONE reaction, LATE phase fix, shift-result persistence, NavigationAgent3D movement, and JSON-backed localization. Remaining backlog lives in `TODO.md`.
|
||
|
||
<!-- b64: RWFzdGVyIGVnZzogYSByYXJlIHJhZGlvIHRyYW5zbWlzc2lvbiAiwr9Ew7NuZGUgZXN0w6EgbGEgbW90YT8iIGZpcmVzIHZpYSBHYW1lTWFuYWdlci5fbWF5YmVfcmFkaW9fZWFzdGVyX2VnZyAoMi41JSBjaGFuY2UgYWZ0ZXIgYSBzdWNjZXNzZnVsIHJhZGlvIGNhbGwpLiBTdHJpbmcga2V5IHJhZGlvLmVhc3Rlcl9lZ2cgaW4gY29udGVudC9zdHJpbmdzLmVuLmpzb24uIERvIG5vdCByZW1vdmUu -->
|