Files
settled-reach/docs/sprints/sprint-18/server.md
T
jpmschweitzerandClaude Opus 4.6 86aab2d14e docs(sprints): add Sprint 18: Touch briefings
14 tickets across server (9), client (3), copy (2). Examine mechanic,
NPC awareness, social propagation, minimap, dialogue UI, save state
data model, and blocker-clearing work for Sprint 19.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 13:42:52 +01:00

12 KiB

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 <id> 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<String, u8> 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<BackgroundSim> 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):

tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(simulation): description" --description "body" --base main --head server