51 lines
6.4 KiB
Markdown
51 lines
6.4 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
|
|
|
|
- Core simulation, three scenarios, headless tests (41 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).
|
|
- Polish/accessibility: tone picker (`T` cycles `selected_tone` via `TONE_ORDER`; HUD `tone_label`), 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`) for localization. NOTE: `Strings.get` was renamed to `Strings.lookup` because `get` collides with `Object.get`.
|
|
- Run tests: `godot --headless --path . --script res://src/test/test_runner.gd`
|
|
- Boot check: `godot --headless --path . --quit-after 120`
|
|
- Screenshot tool: `godot --path . --script res://src/test/take_screenshot.gd` (writes to `user://screenshot.png`).
|
|
|
|
## Known Issues / Notes
|
|
|
|
- RefCounted reference cycle: `NPCController` holds an `FSM` that holds a back-reference to its owner, so test suites report "resources still in use at exit". Benign (process exit), but worth breaking later (e.g. `WeakRef` or explicit `dispose()`).
|
|
- 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.
|
|
|
|
## Open Questions
|
|
|
|
- Whether the responder avatar should be playable/controllable or purely scripted.
|
|
- Localization target languages.
|
|
|
|
## Plan vs TODO Gaps (2026-10-31 evaluation)
|
|
|
|
PLAN.md describes features that are neither implemented nor tracked in TODO.md. Recorded here so they are not lost; promote to TODO.md when work begins.
|
|
|
|
- **Protesters NPC type** — PLAN.md premise lists "protesters" among the crowd, but there is no `PROTESTER` archetype in `Rules.NpcType`, no profile, and no events.
|
|
- **Door lock/secure mechanic** — PLAN.md Level 3 lists "Lock or secure the door" as an action; no door-lock mechanic exists.
|
|
- **Flashlight interaction** — PLAN.md Level 3 lists "Use a flashlight or radio warning tone at night"; `flashlight` appears in scenario `equipment` lists but has no interaction.
|
|
- **"Call someone else" NPC reaction** — PLAN.md Level 2 lists "Call someone else" as a possible NPC response; the `Rules.Reaction` enum has no such outcome.
|
|
- **LATE shift phase never fires (bug)** — `violent_emergency_event` is tagged `Rules.Phase.LATE`, but no scenario defines a LATE entry in `shift_phases` (only EARLY/MIDDAY/EVENING). `EventScheduler.pull_due` filters by phase, so the violent-threat event can never spawn.
|
|
- **Shift results not persisted** — `Save` autoload exposes `save_shift_result`/`load_shift_results`, but nothing calls them; PLAN.md says shift results use JSON in `user://`.
|
|
- **Navigation not implemented** — PLAN.md says `NavigationRegion3D`/`NavigationAgent3D` drive movement; NPCs currently use direct `move_and_slide` toward waypoints (no navmesh).
|
|
- **Localized text not in JSON** — PLAN.md says localized dialogue/text live in JSON files; the `Strings` table is currently a GDScript constant, not JSON.
|