Server-tracked zone_id on VisibleTile for D-073 zone crossfade and D-059 deep fog temperature tint. ZoneMap resource backed by BTreeMap, observer enrichment in snapshot assembly. Backwards-compatible: v10 payloads deserialize with zone_id: None. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
118 lines
3.9 KiB
Rust
118 lines
3.9 KiB
Rust
//! 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.
|
|
//!
|
|
//! Note: HashMap is used for `sector_lookup` — a per-frame scratch buffer
|
|
//! looked up only by key. Iteration order is irrelevant here. Not subject to
|
|
//! the simulation determinism constraint (see server/.clippy.toml).
|
|
#![allow(clippy::disallowed_types)]
|
|
|
|
use std::collections::{BTreeSet, HashMap};
|
|
|
|
use bevy_ecs::prelude::*;
|
|
|
|
use crate::bridge::types::{FacingDirection, TileKind, 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: BTreeSet<(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 mut visible_tiles: Vec<VisibleTile> = cone_tiles
|
|
.iter()
|
|
.map(|&(x, y, sector)| {
|
|
let tile_kind = if walkability.can_move_to(&TilePosition::new(x, y, z)) {
|
|
TileKind::Floor
|
|
} else {
|
|
TileKind::Wall
|
|
};
|
|
VisibleTile {
|
|
x,
|
|
y,
|
|
z,
|
|
visibility: sector,
|
|
tile_kind,
|
|
zone_id: None,
|
|
}
|
|
})
|
|
.collect();
|
|
visible_tiles.sort_by_key(|t| (t.x, t.y));
|
|
|
|
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))
|
|
}
|
|
}
|