refactor(server): decompose observer pipeline and fix interaction boundary
Extract visibility geometry into a separate system behind a PerceptionQuery trait, enabling D-017 perception mode swapping. Two-stage pipeline: compute_visibility_geometry writes to VisibilityGeometry resource, compute_observer_snapshot reads it. Remove KnowledgeGraph from compute_nearby_interactions (simulation phase boundary violation). Verb availability stays in simulation; POI-based priority adjustment moves to observer via apply_poi_verb_priority helper. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
//! Perception query trait (D-017).
|
||||
//!
|
||||
//! Abstraction for perception mode geometry computation. Each mode
|
||||
//! (natural vision, thermal, EM, etc.) implements PerceptionQuery to
|
||||
//! provide mode-specific FOV and visibility sector computation.
|
||||
//! v0.1 implements only NaturalVision.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use bevy_ecs::prelude::*;
|
||||
|
||||
use crate::bridge::types::{FacingDirection, VisibilitySector, VisibleTile};
|
||||
use crate::perception::shadowcast::compute_fov;
|
||||
use crate::perception::vision_cone::{apply_vision_cone, VisionConeConfig};
|
||||
use crate::simulation::movement::{TilePosition, WalkabilityMap};
|
||||
|
||||
/// Cached FOV geometry for the current frame. Produced by
|
||||
/// compute_visibility_geometry, consumed by compute_observer_snapshot.
|
||||
/// D-017 perception modes swap the geometry producer while the consumer
|
||||
/// remains unchanged.
|
||||
#[derive(Resource, Default)]
|
||||
pub struct VisibilityGeometry {
|
||||
pub visible_tiles: Vec<VisibleTile>,
|
||||
pub visible_positions: HashSet<(i32, i32)>,
|
||||
pub sector_lookup: HashMap<(i32, i32), VisibilitySector>,
|
||||
pub observer_z: i32,
|
||||
}
|
||||
|
||||
/// Trait for perception mode geometry computation (D-017).
|
||||
///
|
||||
/// Each perception mode implements this to produce a VisibilityGeometry
|
||||
/// from the observer's position and facing. v0.1 only implements
|
||||
/// NaturalVision; D-017 adds Thermal, EM, etc.
|
||||
pub trait PerceptionQuery: Send + Sync {
|
||||
fn compute_geometry(
|
||||
&self,
|
||||
observer_pos: &TilePosition,
|
||||
facing: FacingDirection,
|
||||
walkability: &WalkabilityMap,
|
||||
) -> VisibilityGeometry;
|
||||
}
|
||||
|
||||
/// Natural vision — default perception mode.
|
||||
/// Uses symmetric shadowcasting (D-011) + directional vision cone (D-015).
|
||||
pub struct NaturalVision;
|
||||
|
||||
impl PerceptionQuery for NaturalVision {
|
||||
fn compute_geometry(
|
||||
&self,
|
||||
observer_pos: &TilePosition,
|
||||
facing: FacingDirection,
|
||||
walkability: &WalkabilityMap,
|
||||
) -> VisibilityGeometry {
|
||||
let config = VisionConeConfig::default();
|
||||
let z = observer_pos.z;
|
||||
|
||||
let fov = compute_fov(
|
||||
|x, y| !walkability.can_move_to(&TilePosition::new(x, y, z)),
|
||||
observer_pos.x,
|
||||
observer_pos.y,
|
||||
config.forward_range,
|
||||
z,
|
||||
);
|
||||
|
||||
let cone_tiles =
|
||||
apply_vision_cone(&fov, observer_pos.x, observer_pos.y, facing, &config);
|
||||
|
||||
let visible_tiles = cone_tiles
|
||||
.iter()
|
||||
.map(|&(x, y, sector)| VisibleTile {
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
visibility: sector,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let visible_positions = cone_tiles.iter().map(|&(x, y, _)| (x, y)).collect();
|
||||
|
||||
let sector_lookup = cone_tiles
|
||||
.iter()
|
||||
.map(|&(x, y, sector)| ((x, y), sector))
|
||||
.collect();
|
||||
|
||||
VisibilityGeometry {
|
||||
visible_tiles,
|
||||
visible_positions,
|
||||
sector_lookup,
|
||||
observer_z: z,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resource wrapping the active perception mode (D-017).
|
||||
/// Defaults to NaturalVision. Swap this resource to change perception modes.
|
||||
#[derive(Resource)]
|
||||
pub struct ActivePerceptionMode(pub Box<dyn PerceptionQuery>);
|
||||
|
||||
impl Default for ActivePerceptionMode {
|
||||
fn default() -> Self {
|
||||
Self(Box::new(NaturalVision))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user