feat(simulation): add proximity detection and interaction verbs
Implement compute_nearby_interactions system that detects entities within close (≤2) and mid (≤5) Manhattan distance, computes available verbs per D-060 spec. NPCs get Talk+Observe at close range, Observe-only at mid range; PersonOfInterest flips priority. Objects get Examine. Results populate nearby_interactions[] on ObserverSnapshot v4. Bump protocol version 3→4. Implements #404. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -199,6 +199,7 @@ mod tests {
|
||||
world.init_resource::<crate::knowledge::KnowledgeEventQueue>();
|
||||
world.init_resource::<EntityRegistry>();
|
||||
world.init_resource::<ObservationEventQueue>();
|
||||
world.init_resource::<crate::simulation::interaction::NearbyInteractionBuffer>();
|
||||
world
|
||||
}
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@ mod tests {
|
||||
world.init_resource::<SnapshotBuffer>();
|
||||
world.init_resource::<KnowledgeEventQueue>();
|
||||
world.init_resource::<EntityRegistry>();
|
||||
world.init_resource::<crate::simulation::interaction::NearbyInteractionBuffer>();
|
||||
world
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::bridge::types::*;
|
||||
use crate::knowledge::{EntityRegistry, KnowledgeGraph};
|
||||
use crate::perception::shadowcast::compute_fov;
|
||||
use crate::perception::vision_cone::{apply_vision_cone, Facing, VisionConeConfig};
|
||||
use crate::simulation::interaction::NearbyInteractionBuffer;
|
||||
use crate::simulation::movement::{PlayerCharacter, TilePosition, WalkabilityMap};
|
||||
use crate::simulation::time::SimulationTime;
|
||||
|
||||
@@ -22,6 +23,7 @@ pub fn compute_observer_snapshot(
|
||||
time: Res<SimulationTime>,
|
||||
walkability: Res<WalkabilityMap>,
|
||||
registry: Res<EntityRegistry>,
|
||||
interaction_buffer: Res<NearbyInteractionBuffer>,
|
||||
observer_query: Query<(&TilePosition, Option<&Facing>, &KnowledgeGraph), With<PlayerCharacter>>,
|
||||
all_entities: Query<(
|
||||
Entity,
|
||||
@@ -185,7 +187,8 @@ pub fn compute_observer_snapshot(
|
||||
day: time.day(),
|
||||
time_of_day: time.time_of_day_minutes(),
|
||||
day_phase: time.day_phase(),
|
||||
paused: time.paused,
|
||||
paused: time.paused(),
|
||||
tick_rate: time.tick_rate,
|
||||
};
|
||||
|
||||
tracing::trace!(
|
||||
@@ -196,14 +199,15 @@ pub fn compute_observer_snapshot(
|
||||
visible_tiles.len(),
|
||||
);
|
||||
|
||||
// Step 8: Assemble snapshot (v3: added relationship + observation fields)
|
||||
// Step 8: Assemble snapshot (v4: added nearby_interactions)
|
||||
buffer.snapshot = Some(ObserverSnapshot {
|
||||
version: 3,
|
||||
version: 4,
|
||||
tick: time.tick,
|
||||
game_time,
|
||||
player_facing: facing,
|
||||
entities,
|
||||
visible_tiles,
|
||||
nearby_interactions: interaction_buffer.interactions.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -221,6 +225,7 @@ mod tests {
|
||||
world.insert_resource(WalkabilityMap::new(width, height, 1));
|
||||
world.init_resource::<SnapshotBuffer>();
|
||||
world.init_resource::<EntityRegistry>();
|
||||
world.init_resource::<NearbyInteractionBuffer>();
|
||||
world
|
||||
}
|
||||
|
||||
@@ -240,7 +245,7 @@ mod tests {
|
||||
|
||||
let buffer = world.resource::<SnapshotBuffer>();
|
||||
let snapshot = buffer.snapshot.as_ref().expect("snapshot should exist");
|
||||
assert_eq!(snapshot.version, 3);
|
||||
assert_eq!(snapshot.version, 4);
|
||||
assert_eq!(snapshot.entities.len(), 1);
|
||||
assert!(matches!(snapshot.entities[0].kind, EntityKind::Player));
|
||||
assert_eq!(snapshot.entities[0].observation, EntityVisibility::Visible);
|
||||
@@ -359,10 +364,10 @@ mod tests {
|
||||
#[test]
|
||||
fn game_time_populated() {
|
||||
let mut world = setup_world(32, 32);
|
||||
world.insert_resource(SimulationTime {
|
||||
tick: 7200, // 720 minutes = Evening
|
||||
paused: true,
|
||||
});
|
||||
let mut time = SimulationTime::default();
|
||||
time.tick = 7200; // 720 minutes = Evening
|
||||
time.tick_rate = crate::simulation::time::TickRate::Paused;
|
||||
world.insert_resource(time);
|
||||
world.spawn((
|
||||
PlayerCharacter,
|
||||
TilePosition::new(16, 16, 0),
|
||||
@@ -479,7 +484,7 @@ mod tests {
|
||||
.id();
|
||||
registry.register(player);
|
||||
world.insert_resource(registry);
|
||||
world.insert_resource(SimulationTime { tick: 100, paused: false });
|
||||
world.insert_resource({ let mut t = SimulationTime::default(); t.tick = 100; t });
|
||||
|
||||
let mut schedule = bevy_ecs::schedule::Schedule::default();
|
||||
schedule.add_systems(compute_observer_snapshot);
|
||||
|
||||
Reference in New Issue
Block a user