fix(simulation): PR #66 review — dedup vision, spawn components, doc fixes
- vision.rs: deduplicate own-tile entity iteration, add same-tile test - spawn.rs: add NpcVisionState, NpcMemory, PlayerAwareness to content- spawned NPCs (matching generate_npc) - pressure.rs: update who_knows_full_scan doc to reflect actual call frequency, document O(N) acceptability at call site - types.rs: fix stale protocol version comment (13→14) - generate.rs: format!() → .to_string() (clippy) - vision.rs: hardcoded 10 → TICKS_PER_GAME_MINUTE - relationships.rs: fix comment "15%" → "20%" to match code - pressure.rs: document total() floor-truncation - save_state.rs: document NpcMemory exclusion as intentional Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -30,7 +30,7 @@ use crate::perception::shadowcast::compute_fov;
|
||||
use crate::perception::vision_cone::{apply_vision_cone, Facing, VisionConeConfig};
|
||||
use crate::simulation::movement::{PlayerCharacter, TilePosition, WalkabilityMap};
|
||||
use crate::simulation::spatial::{NaiveSpatialIndex, SpatialIndex};
|
||||
use crate::simulation::time::SimulationTime;
|
||||
use crate::simulation::time::{SimulationTime, TICKS_PER_GAME_MINUTE};
|
||||
use crate::simulation::tier::ActiveSim;
|
||||
|
||||
/// NPC vision range in tiles (matches player forward range from VisionConeConfig).
|
||||
@@ -140,11 +140,10 @@ pub fn compute_npc_vision(
|
||||
let mut new_visible = BTreeSet::new();
|
||||
let mut player_vis = false;
|
||||
|
||||
// Query entities within vision range, then filter by FOV tile set
|
||||
let nearby = spatial_index.entities_in_range(npc_pos, NPC_VISION_RANGE as u32);
|
||||
let at_origin = spatial_index.entities_at(npc_pos);
|
||||
// Single-pass query: all entities within vision range including own tile
|
||||
let candidates = spatial_index.entities_within(npc_pos, NPC_VISION_RANGE as u32);
|
||||
|
||||
for entity in nearby.into_iter().chain(at_origin.into_iter()) {
|
||||
for entity in candidates {
|
||||
if entity == npc_entity {
|
||||
continue;
|
||||
}
|
||||
@@ -262,7 +261,7 @@ pub fn degrade_npc_inferences(
|
||||
time: Res<SimulationTime>,
|
||||
mut npc_query: Query<&mut NpcMemory, (With<Npc>, With<ActiveSim>)>,
|
||||
) {
|
||||
if time.tick % 10 != 0 {
|
||||
if time.tick % TICKS_PER_GAME_MINUTE != 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -494,6 +493,38 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn npc_sees_non_npc_entity_on_same_tile() {
|
||||
let mut world = setup_world(32, 32);
|
||||
let mut registry = EntityRegistry::new(0);
|
||||
let mut spatial = NaiveSpatialIndex::new();
|
||||
|
||||
// NPC at (16, 16)
|
||||
let npc = world
|
||||
.spawn((Npc, ActiveSim, pos(16, 16), NpcVisionState::default()))
|
||||
.id();
|
||||
registry.register(npc);
|
||||
spatial.update(npc, pos(16, 16));
|
||||
|
||||
// Non-NPC entity on the same tile (e.g. dropped item)
|
||||
let item = world.spawn(pos(16, 16)).id();
|
||||
let item_sid = registry.register(item);
|
||||
spatial.update(item, pos(16, 16));
|
||||
|
||||
world.insert_resource(registry);
|
||||
world.insert_resource(spatial);
|
||||
|
||||
let mut schedule = bevy_ecs::schedule::Schedule::default();
|
||||
schedule.add_systems(compute_npc_vision);
|
||||
schedule.run(&mut world);
|
||||
|
||||
let vision = world.get::<NpcVisionState>(npc).unwrap();
|
||||
assert!(
|
||||
vision.visible_entities.contains(&item_sid),
|
||||
"NPC should see non-NPC entity sharing its tile"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn background_npc_not_processed() {
|
||||
let mut world = setup_world(32, 32);
|
||||
|
||||
Reference in New Issue
Block a user