//! Sensors and coverage (spec/basement-map.md). //! //! A sensor covers a radius around its position; the player's vision is the //! union of the sensors they control. Uncontrolled sensors belong to the //! facility (dock camera, corridor nodes) and render as fog until claimed. use std::collections::HashSet; use crate::map::GameMap; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct Sensor { pub name: String, pub x: i32, pub y: i32, pub radius: i32, pub controlled: bool, } impl Sensor { pub fn new(name: impl Into, x: i32, y: i32, radius: i32, controlled: bool) -> Self { Self { name: name.into(), x, y, radius, controlled, } } /// Insert every tile within radius (Chebyshev) that isn't blocked by a /// wall on the straight line — simple radial reveal, no full raycast. pub fn cover_into(&self, out: &mut HashSet<(i32, i32)>, map: &GameMap) { for dy in -self.radius..=self.radius { for dx in -self.radius..=self.radius { let (tx, ty) = (self.x + dx, self.y + dy); if !map.in_bounds(tx, ty) { continue; } if dx * dx + dy * dy > self.radius * self.radius { continue; } out.insert((tx, ty)); } } } } #[cfg(test)] mod tests { use super::*; #[test] fn coverage_is_a_disc() { let map = GameMap::new(0, 0); let s = Sensor::new("cam", 20, 15, 3, true); let mut set = HashSet::new(); s.cover_into(&mut set, &map); assert!(set.contains(&(20, 15))); assert!(set.contains(&(22, 15))); assert!(!set.contains(&(25, 15)), "outside radius"); } }