feat(simulation): protocol v14 — POI list, examine result, player knowledge wire types
Add three new ObserverSnapshot fields for client Sprint 18 tickets: poi_list (Vec<PoiWire>) for minimap #151, examine_result (Option<ExamineResultWire>) for #174, player_knowledge (Option<PlayerKnowledgeWire>) for journal #264. POI list populated live from KG-discovered PointOfInterest components. KG dump serializes entity/fact knowledge with confidence, source, and state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ use crate::simulation::interaction::NearbyInteractionBuffer;
|
||||
use crate::simulation::inventory::{CarriedBy, InventorySlot, ItemName};
|
||||
use crate::simulation::monologue::{MonologueBuffer, SprintAnomalyQueue};
|
||||
use crate::simulation::movement::{PlayerCharacter, TilePosition, WalkabilityMap};
|
||||
use crate::simulation::poi::PointOfInterest;
|
||||
use crate::simulation::rng::SimRng;
|
||||
use crate::simulation::sound::SoundEventQueue;
|
||||
use crate::simulation::stance::Stance;
|
||||
@@ -94,6 +95,7 @@ pub fn compute_observer_snapshot(
|
||||
Option<&crate::npc::tell_state::DerivedTellState>,
|
||||
)>,
|
||||
inventory_items: Query<(Entity, &CarriedBy, &ItemName, &InventorySlot)>,
|
||||
poi_query: Query<&PointOfInterest>,
|
||||
mut buffer: ResMut<SnapshotBuffer>,
|
||||
sim_rng: Option<Res<SimRng>>,
|
||||
) {
|
||||
@@ -272,6 +274,97 @@ pub fn compute_observer_snapshot(
|
||||
})
|
||||
});
|
||||
|
||||
// Collect discovered POIs for minimap (#151, D-013)
|
||||
// A POI is "discovered" if the observer's KG contains fact "poi.{poi_id}".
|
||||
let poi_list: Vec<PoiWire> = poi_query
|
||||
.iter()
|
||||
.filter(|poi| observer_kg.knows_fact(&poi.fact_id()))
|
||||
.map(|poi| PoiWire {
|
||||
poi_id: poi.poi_id.clone(),
|
||||
name: poi.name.clone(),
|
||||
x: poi.position.x,
|
||||
y: poi.position.y,
|
||||
z: poi.position.z,
|
||||
category: poi.category,
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Build player knowledge dump for journal panel (#264, D-041).
|
||||
// Sends full KG state — client-side filtering for display grouping.
|
||||
let player_knowledge = {
|
||||
let kg_entities: Vec<KnownEntityWire> = observer_kg
|
||||
.known_entities_iter()
|
||||
.map(|(sid, ek)| {
|
||||
let name = ek
|
||||
.known_attributes
|
||||
.get("name")
|
||||
.cloned()
|
||||
.unwrap_or_else(|| "Unknown".to_string());
|
||||
let source = match &ek.source {
|
||||
crate::knowledge::types::KnowledgeSource::DirectObservation { .. } => {
|
||||
"DirectObservation".to_string()
|
||||
}
|
||||
crate::knowledge::types::KnowledgeSource::Heard { .. } => "Heard".to_string(),
|
||||
crate::knowledge::types::KnowledgeSource::ToldBy { source_id, .. } => {
|
||||
format!("ToldBy({})", source_id.0)
|
||||
}
|
||||
crate::knowledge::types::KnowledgeSource::Inferred { .. } => {
|
||||
"Inferred".to_string()
|
||||
}
|
||||
crate::knowledge::types::KnowledgeSource::Background => {
|
||||
"Background".to_string()
|
||||
}
|
||||
};
|
||||
KnownEntityWire {
|
||||
entity_id: sid.0,
|
||||
name,
|
||||
confidence: ek.confidence,
|
||||
source,
|
||||
state: ek.state,
|
||||
relationship: ek.relationship,
|
||||
last_observed_tick: ek.last_observed_tick,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let kg_facts: Vec<KnownFactWire> = observer_kg
|
||||
.known_facts_iter()
|
||||
.map(|(fid, fk)| {
|
||||
let source = match &fk.source {
|
||||
crate::knowledge::types::KnowledgeSource::DirectObservation { .. } => {
|
||||
"DirectObservation".to_string()
|
||||
}
|
||||
crate::knowledge::types::KnowledgeSource::Heard { .. } => "Heard".to_string(),
|
||||
crate::knowledge::types::KnowledgeSource::ToldBy { source_id, .. } => {
|
||||
format!("ToldBy({})", source_id.0)
|
||||
}
|
||||
crate::knowledge::types::KnowledgeSource::Inferred { .. } => {
|
||||
"Inferred".to_string()
|
||||
}
|
||||
crate::knowledge::types::KnowledgeSource::Background => {
|
||||
"Background".to_string()
|
||||
}
|
||||
};
|
||||
KnownFactWire {
|
||||
fact_id: fid.0.clone(),
|
||||
confidence: fk.confidence,
|
||||
source,
|
||||
state: fk.state,
|
||||
acquired_tick: fk.acquired_tick,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if kg_entities.is_empty() && kg_facts.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(PlayerKnowledgeWire {
|
||||
entities: kg_entities,
|
||||
facts: kg_facts,
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
buffer.snapshot = Some(ObserverSnapshot {
|
||||
version: crate::bridge::types::PROTOCOL_VERSION,
|
||||
tick: time.tick,
|
||||
@@ -292,6 +385,9 @@ pub fn compute_observer_snapshot(
|
||||
follow_state,
|
||||
sound_events,
|
||||
rng_seed: sim_rng.as_deref().map(|r| r.seed()),
|
||||
poi_list,
|
||||
examine_result: None, // Populated by examine system when #242 lands
|
||||
player_knowledge,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user