feat(simulation): wire Sprint 15 systems + protocol v13

Register all new systems in NpcPlugin and SimulationPlugin with correct
ordering constraints. Protocol bumped to v13: tell_state on VisibleEntity,
follow_state and PostConversationQueue on ObserverSnapshot, Follow verb
on VerbKind. Observer snapshot populates tell state from DerivedTellState
and follow state from FollowTarget. System ordering: tolerance after
mood, deviation after activity, tell after mood+deviation, follow after
visibility geometry, event monologue after conversations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 14:40:09 +01:00
co-authored by Claude Opus 4.6
parent 517318c8a7
commit 8538f916af
8 changed files with 98 additions and 11 deletions
+10 -1
View File
@@ -173,6 +173,11 @@ impl Plugin for BridgePlugin {
.after(crate::perception::anomaly::detect_anomalies),
crate::simulation::monologue::process_sprint_anomaly_monologue
.after(crate::simulation::monologue::trigger_recognition_monologue),
crate::simulation::monologue::trigger_event_monologue
.after(crate::simulation::monologue::process_sprint_anomaly_monologue)
.after(crate::simulation::sound::collect_sound_events)
.after(crate::simulation::conversation::run_npc_conversations)
.after(crate::simulation::dialogue::process_walk_away),
crate::simulation::dialogue::process_talk_interaction
.after(crate::simulation::input::process_player_input),
crate::simulation::dialogue::process_walk_away
@@ -180,10 +185,14 @@ impl Plugin for BridgePlugin {
.after(crate::simulation::dialogue::process_talk_interaction),
crate::simulation::dialogue::process_confrontation_response
.after(crate::simulation::input::process_player_input),
crate::simulation::follow::update_follow_state
.after(crate::perception::observer::compute_visibility_geometry)
.after(crate::simulation::movement::validate_movement)
.before(crate::perception::observer::compute_observer_snapshot),
crate::perception::observer::compute_observer_snapshot
.after(crate::perception::observer::compute_visibility_geometry)
.after(crate::simulation::interaction::compute_nearby_interactions)
.after(crate::simulation::monologue::process_sprint_anomaly_monologue)
.after(crate::simulation::monologue::trigger_event_monologue)
.after(crate::simulation::dialogue::process_talk_interaction)
.after(crate::simulation::dialogue::process_confrontation_response)
.before(crate::simulation::time::advance_tick),
+5
View File
@@ -240,6 +240,7 @@ mod tests {
visibility: VisibilitySector::Forward,
relationship: RelationshipState::Unknown,
observation: EntityVisibility::Visible,
tell_state: None,
},
VisibleEntity {
entity_id: 100,
@@ -250,6 +251,7 @@ mod tests {
visibility: VisibilitySector::Forward,
relationship: RelationshipState::Known,
observation: EntityVisibility::Visible,
tell_state: None,
},
VisibleEntity {
entity_id: 200,
@@ -260,6 +262,7 @@ mod tests {
visibility: VisibilitySector::Forward,
relationship: RelationshipState::Unknown,
observation: EntityVisibility::Visible,
tell_state: None,
},
],
visible_tiles: vec![VisibleTile {
@@ -302,6 +305,7 @@ mod tests {
scan_events: vec![],
conversation_events: vec![],
conversation_ended: vec![],
follow_state: None,
sound_events: vec![],
rng_seed: None,
}
@@ -431,6 +435,7 @@ mod tests {
scan_events: vec![],
conversation_events: vec![],
conversation_ended: vec![],
follow_state: None,
sound_events: vec![],
rng_seed: None,
};
+19 -1
View File
@@ -15,7 +15,7 @@ pub use crate::simulation::time::{DayPhase, TickRate};
/// negotiation is unnecessary. Client should reject snapshots with version !=
/// PROTOCOL_VERSION. New fields use #[serde(default)] only during the migration
/// period, then the default is removed once both sides are updated.
pub const PROTOCOL_VERSION: u8 = 12;
pub const PROTOCOL_VERSION: u8 = 13;
/// The ONLY data structure crossing the client-server boundary (D-020)
/// Contains all information visible to the observer at a given tick.
@@ -32,6 +32,8 @@ pub const PROTOCOL_VERSION: u8 = 12;
/// rng_seed (#527, deterministic replay — completes WRONG button loop).
/// v11 adds: zone_id on VisibleTile (#523, D-077 OQ-09 resolution + D-073 crossfade).
/// v12 adds: conversation_events, conversation_ended (#247, D-078 NPC-to-NPC conversations).
/// v13 adds: tell_state on VisibleEntity (#90, D-024 tell system — for future client use),
/// follow_state (#241, follow mechanic HUD state).
/// Future fields: ambient sound events, HUD state (D-020 expansion).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ObserverSnapshot {
@@ -98,6 +100,11 @@ pub struct ObserverSnapshot {
/// Client dismisses the passive dialogue panel for these pairs.
#[serde(default)]
pub conversation_ended: Vec<crate::simulation::conversation::ConversationEndEvent>,
/// Follow-mode state for client HUD display (#241).
/// Present when the player is actively following an NPC.
/// Client shows follow indicator with distance, LOS, and tension.
#[serde(default)]
pub follow_state: Option<crate::simulation::follow::FollowStateWire>,
/// RNG seed active at this tick for deterministic replay (#527).
/// The WRONG button writes this to seed.txt so replays reproduce observed bugs.
/// None when the RNG resource is unavailable (should not occur in practice).
@@ -270,6 +277,11 @@ pub struct VisibleEntity {
/// Visible = in LOS right now. Remembered = known but not in LOS.
#[serde(default)]
pub observation: EntityVisibility,
/// Current observable tell category for NPC entities (#90, D-024).
/// None for non-NPC entities or NPCs with no active tell this tick.
/// v0.1: field is emitted for future client use; client may ignore.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tell_state: Option<crate::npc::tell_state::TellCategory>,
}
/// Category of visible entity
@@ -452,6 +464,12 @@ pub enum VerbKind {
/// Furniture — sit/use
Sit,
// --- NPC extended verbs ---
/// Follow an NPC — designate as follow target (#241).
/// Close range only. Enters follow mode: server tracks distance, LOS,
/// and NPC suspicion. Replaces previous follow target if any.
Follow,
// --- Phase 2 verbs (observer, KG-gated, #422) ---
/// Confront an NPC about known facts/contradictions.
/// Phase 2 only: injected when observer has KnowsDetails+ confidence.