--- title: "Sprint 18 — Server Briefing" description: "Examine mechanic, NPC player-awareness, character goal framework, social propagation" type: sprint status: archived sprint: 18 team: "server" --- # Sprint 18: Touch — Server Tasks **Goal:** The player can examine entities and objects to generate character-filtered observations; NPCs detect and react when watched; social actions propagate through the relationship graph; minimap renders POIs on the client. **Branch:** `server` **Agents:** Dudley (simulation), Tyre (architecture), Hoshe (QA) ## Carry-over from Sprint 17 None. Sprint 17 closed 19/19. ## New Tickets | # | Title | Blocked by | |---|-------|------------| | #242 | Examine mechanic | #240 (done) | | #244 | NPC player-awareness behavior | — | | #248 | Character goal/pressure framework | — | | #249 | Player-action social propagation | — | | #337 | Tell state derivation system | #323 (done) | | #91 | Skill system & combat flag | — | | #115 | NPC vision system | — | | #256 | Save state data model | — | | #95 | Background tier state machines | — | Use `db/connectors/ticket show ` for full details. ## Key Decisions - `decisions/architecture.md` — D-010 (information boundaries), D-020 (IPC architecture), D-026 (simulation tiers), D-041 (knowledge graph data model) - `decisions/perception.md` — D-011 (fog of perception — NPCs use same LOS), D-035 (symmetric shadowcasting) - `decisions/content.md` — D-024 (NPC 10-axis model, skill set axis, combat component), D-028 (dialogue architecture — examine verb is a dialogue layer entry point) - `decisions/scope.md` — D-027 (vertical slice criteria — character-specific observation) ## Notes ### #242 — Examine mechanic The interaction dispatcher (`server/src/simulation/interaction.rs`) already computes `VerbKind::ExamineNpc` and `VerbKind::ExamineObject` in the `NearbyInteractionBuffer`. The examine verb appears at close range (≤2 tiles, `CLOSE_RANGE`). `PlayerAction` dispatch and `process_player_input` are the entry points in `server/src/simulation/input.rs`. What this ticket must deliver: - A `process_examine_interaction` system that handles `PlayerAction::Examine { entity_id }` (or equivalent) - Generates a detailed `ObservationEvent` with low uncertainty for the target entity - Applies character-specific filtering via the observer's `KnowledgeGraph` (same NPC looks different to smuggler vs detective — smuggler reads cargo-handling posture, detective reads procedural tells) - Result is written to the observer's `KnowledgeGraph` via `KnowledgeEventQueue` as a `DirectObservation` entry with `KnowledgeConfidence::Direct` - Emits an examine result field in `ObserverSnapshot` so the client can display character-filtered detail text Integration points: `server/src/simulation/interaction.rs` (verb dispatch), `server/src/knowledge/graph.rs` (`KnowledgeGraph` write), `server/src/perception/observation.rs` (observation event pattern), `server/src/simulation/dialogue.rs` (examine result mirrors dialogue result pattern). ### #244 — NPC player-awareness behavior NPCs use the same LOS system as the player (D-011). The awareness system detects when an NPC's LOS query includes the player's `TilePosition`, and generates a behavioral response. What this ticket must deliver: - A new `PlayerAwareness` component on Active-tier NPCs tracking: whether the player is in this NPC's LOS, for how many consecutive ticks, and accumulated suspicion level - A `detect_player_awareness` system running after `compute_observer_snapshot` — iterate Active-tier NPCs, run a simplified LOS check or piggyback on existing shadowcast state - When awareness crosses threshold: routine deviation behavior (NPC changes path or posture), fed into the `DerivedTellState` pipeline (already exists in `server/src/npc/tell_state.rs`) - Feeds follow-verb suspicion in `server/src/simulation/follow.rs` Key file: `server/src/simulation/follow.rs` already has proximity + attention logic for follow suspicion — awareness system reuses this infrastructure. New system lives in `server/src/simulation/` or `server/src/npc/`. ### #248 — Character goal/pressure framework Defines systemic pressures per character that modulate monologue salience and observation priority. Not scripted arcs — emergent from interaction of existing axes (D-024). What this ticket must deliver: - A `CharacterPressure` component on the player character entity: `exposure_pressure: i32` (smuggler), `institutional_pressure: i32` (detective), `relationship_pressure: i32` (both) - Pressure inputs: exposure rises when NPCs notice the player (feeds from #244), relationship pressure from trust changes in `server/src/npc/relationships.rs`, institutional pressure from detective-specific interaction patterns - Pressure outputs: written into `ObserverSnapshot` HUD widget data; high pressure raises monologue trigger weight for anxiety-tagged lines - Coordinate with copy team — monologue lines using `mood: [anxious]` or `mood: [frustrated]` tags (D-035) are the output surface This is a design-and-implement ticket — start by defining the pressure struct, then wire inputs from existing systems. Monologue salience weighting is the primary v0.1 output. ### #249 — Player-action social propagation Player actions toward one NPC ripple through the relationship graph at three decay orders (D-029 topology principle). The `RelationshipGraph` resource (`server/src/npc/relationships.rs`) and `TrustEventQueue` are the integration points. What this ticket must deliver: - A `propagate_social_actions` system triggered when a `TrustEventQueue` event fires from player action - First-order: immediate full delta to the directly affected NPC - Second-order: `delta * 0.4` to NPCs with strong relationships to the first-order NPC (trust > 3 in `RelationshipGraph`) - Third-order: `delta * 0.15` to NPCs one further hop away, delayed by configurable ticks - Propagation topology varies per seed (D-029 anti-metagaming) — the same action produces different cascades depending on who knows whom - Write propagated trust changes back to `TrustEventQueue` or directly to `Relationships` components with a `PropagatedTrust` marker Gotcha: propagation must not loop (A affects B affects A). Visited-entity set per propagation pass prevents cycles. ### #337 — Tell state derivation system The `derive_tell_state` system already exists and is fully tested in `server/src/npc/tell_state.rs`. This ticket existed in the backlog because the mood state machine (#323) it depends on was not yet done. #323 is now done. What this ticket must deliver: - Verify `derive_tell_state` runs correctly in the current schedule (it is already registered in `server/src/npc/mod.rs` after `mood::update_mood`) - Wire `DerivedTellState` into the observer snapshot output — confirm `ObserverSnapshot.entities[].tell_state` is populated for visible entities - Integration test: NPC with Major secret + stress past midpoint shows `TellCategory::Nervous` in snapshot - This ticket is mostly verification + integration wiring, not new code — the system is complete, the sprint task is closing the loop into the snapshot ### #91 — Skill system & combat flag What this ticket must deliver: - A `SkillSet` component: `BTreeMap` of named skills with level values (BTreeMap per D-010 determinism requirement) - When a `SkillSet` contains `"combat_trained"` with value ≥ 1, the ECS system attaches a `CombatCapability` marker component to that NPC at spawn time - `SkillSet` added to the NPC generation pipeline in `server/src/npc/generate.rs` (already sets other D-024 axes) - The `CombatCapability` component is a zero-sized marker for now — future sprints add stats NPC skill sets are generated from content YAML at startup. The content loader in `server/src/content/` reads NPC definitions — add `skills: {}` as a YAML field on NPC templates. ### #115 — NPC vision system NPCs must use the same LOS shadowcasting system as the player (D-011 — "Applies to ALL entities"). The shadowcast machinery lives in `server/src/perception/shadowcast.rs`. What this ticket must deliver: - NPC vision is computed via `compute_los` (or equivalent call) for Active-tier NPCs each tick - Results stored in an `NpcVisionState` component: set of `StableId` values currently visible to this NPC, plus the player entity if visible - NPC memory: `NpcMemory` component tracking last-known-position of the player even after leaving LOS ("saw you enter building → knows you're inside" per D-011) - Inference stub: if player was seen entering a room, NPC `KnowledgeGraph` records `DirectObservation` of player at that room's zone, degrading to `KnowsOf` after configurable ticks This feeds #244 (awareness) — the `detect_player_awareness` system reads `NpcVisionState` rather than running its own LOS query. Performance note: only run LOS for NPCs whose `TilePosition` is within `ACTIVE_RADIUS` (already guaranteed by `ActiveSim` marker). Full shadowcast per NPC per tick is feasible at 30-80 active NPCs — Tyre has confirmed the budget. ### #256 — Save state data model Define the serialization format for full game state. Shares architecture with #96 (state serialization system, still backlog — this ticket is the data model design, not the save/load implementation). What this ticket must deliver: - A `SaveStateV1` struct (versioned from day one) covering: entity state, `KnowledgeGraph` per entity (already serializable via `serde` in `server/src/knowledge/graph.rs`), `RelationshipGraph`, game clock position (`SimulationTime`), seed value - Write format: MessagePack (consistent with IPC protocol per D-020) or RON for human-readable debugging — decide and document - The struct must roundtrip cleanly: serialize + deserialize produces identical ECS world state - Stub tests proving the roundtrip; full save/load flow is #257 (future sprint) The `KnowledgeGraph` is already `Serialize + Deserialize`. The main design work is enumerating which ECS components must be captured and in what order (deterministic serialization per D-010). ### #95 — Background tier state machines Background-tier NPCs (marked `BackgroundSim` in `server/src/simulation/tier.rs`) currently receive no simulation — tier markers exist but no background tick systems run. This ticket adds the four D-026 state machines for background NPCs. What this ticket must deliver: - A `background_tick` system gated by `With` that fires once per game-minute (every 10 ticks per D-031) - Four mini state machines per background NPC: 1. **Schedule**: advance NPC to next routine activity based on `DayPhase` (reads `DayPhase` from `server/src/simulation/time.rs`, updates `Routine` component) 2. **Mood**: simple mood drift toward neutral; significant events (stress > threshold) can shift from neutral 3. **Relationships**: trust drift toward baseline over time; no events-driven trust changes for background NPCs 4. **Job**: job performance score drift based on contentment (lower contentment → lower performance) - Background tick does NOT run pathfinding, LOS, or dialogue — those are Active-tier only - Background NPCs promoted to Active receive their current state machine state (no reset on promotion) ## Dependency Chain ``` #95 (background tier state machines) → standalone, no blockers #115 (NPC vision system) → #244 (player-awareness behavior) ↓ #248 (character goal/pressure framework) ← feeds from awareness events #337 (tell state wiring) → standalone, verify + wire into snapshot #242 (examine mechanic) → standalone (dispatcher already exists) #249 (social propagation) → standalone (relationships already exist) #91 (skill system & combat flag) → standalone #256 (save state data model) → standalone (design + stub) ``` Parallel tracks: #95, #91, #337, #256, #249, and #242 can all start in week 1. #244 starts after #115 is in review. ## PR Workflow When ready to submit, create a PR with `tea` CLI. **All flags are required** to avoid TTY prompts (see CLAUDE.md "Gitea access" section): ```bash tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(simulation): description" --description "body" --base main --head server ```