diff --git a/src/map.rs b/src/map.rs index bc7d4aa..30aadb1 100644 --- a/src/map.rs +++ b/src/map.rs @@ -294,17 +294,15 @@ fn spawn_portals( } } if spawn_portal { - let x = x as f32; - let y = y as f32; + let x = x as f32 + 0.5; + let y = y as f32 + 0.5; if !portals.contains(&(x, y)) { portals.push((x, y)); } } } } - for portal in portals { - let x = portal.0 as f32 + 0.5; - let y = portal.1 as f32 + 0.5; + for (x, y) in portals { let portal = commands .spawn_bundle(PortalBundle { transform: Transform::from_translation(Vec3::new(x, y, 0.)), @@ -387,9 +385,12 @@ impl Plugin for MapPlugin { } let config = app.world.get_resource::().unwrap().clone(); app.register_type::() - .add_system(spawn_colliders::) - .add_system(spawn_portals::) - .add_system(spawn_portal_colliders::); + .add_system_to_stage( + CoreStage::PreUpdate, + spawn_colliders::.before(crate::visibility::UPDATE_VIEWSHED_LABEL), + ) + .add_system_to_stage(CoreStage::PreUpdate, spawn_portals::) + .add_system_to_stage(CoreStage::PreUpdate, spawn_portal_colliders::); if config.speak_area_descriptions { app.add_system_to_stage(CoreStage::PostUpdate, area_description); } diff --git a/src/visibility.rs b/src/visibility.rs index 7ce3cd8..44807ac 100644 --- a/src/visibility.rs +++ b/src/visibility.rs @@ -5,7 +5,7 @@ use std::{ }; use bevy::{core::FixedTimestep, prelude::*}; -use bevy_rapier2d::na::{self, Point2}; + use coord_2d::{Coord, Size}; use shadowcast::{vision_distance, Context, InputGrid}; @@ -14,7 +14,7 @@ use crate::{ commands::RunIfExistsExt, core::{Angle, Player, PointLike}, log::Log, - map::{ITileType, Map, MapConfig}, + map::{Map, MapConfig, MapObstruction}, }; #[derive(Component, Clone, Copy, Debug, Default, Reflect)] @@ -57,19 +57,20 @@ impl Viewshed { rapier_context: &RapierContext, map: &Map, visible_query: &Query<&Visible>, + obstructions_query: &Query<&MapObstruction>, events: &mut EventWriter, cache: &mut HashMap)>, ) { let mut context: Context = Context::default(); let vision_distance = vision_distance::Circle::new(self.range); let shape = Collider::cuboid(0.5 - f32::EPSILON, 0.5 - f32::EPSILON); - let origin = Point2::new(start.x().trunc(), start.y().trunc()); let mut new_visible_entities = HashSet::new(); + let size = (map.width as u32, map.height as u32); let visibility_grid = VisibilityGrid( - map, + size, RefCell::new(Box::new(|coord: Coord| { - let dest = Point2::new(coord.x as f32, coord.y as f32); - if na::distance(&origin, &dest) >= self.range as f32 { + let shape_pos = Vec2::new(coord.x as f32 + 0.5, coord.y as f32 + 0.5); + if start.distance(&shape_pos) > self.range as f32 + 1. { return u8::MAX; } if let Some((opacity, entities)) = cache.get(&coord) { @@ -82,14 +83,8 @@ impl Viewshed { return *opacity; } } - let tile = map.at(coord.x as usize, coord.y as usize); - if tile.blocks_visibility() { - cache.insert(coord, (u8::MAX, HashSet::new())); - return u8::MAX; - } let mut opacity = 0; let mut entities = HashSet::new(); - let shape_pos = Vec2::new(dest.x + 0.5, dest.y + 0.5); rapier_context.intersections_with_shape( shape_pos, 0., @@ -97,20 +92,24 @@ impl Viewshed { InteractionGroups::all(), Some(&|v| visible_query.get(v).is_ok()), |entity| { + let obstruction = obstructions_query.get(entity).is_ok(); + if obstruction { + entities.clear(); + } entities.insert(entity); if let Ok(visible) = visible_query.get(entity) { opacity = opacity.max(**visible); } - true + !obstruction }, ); for e in &entities { new_visible_entities.insert(*e); } + cache.insert(coord, (opacity, entities.clone())); if entities.contains(viewer_entity) { 0 } else { - cache.insert(coord, (opacity, entities)); opacity } })), @@ -119,9 +118,9 @@ impl Viewshed { context.for_each_visible( Coord::new(start.x_i32(), start.y_i32()), &visibility_grid, - &visibility_grid, + &size, vision_distance, - 255, + u8::MAX, |coord, _directions, _visibility| { new_visible.insert((coord.x, coord.y)); }, @@ -230,22 +229,18 @@ fn viewshed_removed( } } -pub struct VisibilityGrid<'a, D: 'static + Clone + Default + Send + Sync, F>( - pub &'a Map, - pub RefCell>, -); +pub struct VisibilityGrid(pub (u32, u32), pub RefCell>); -impl<'a, D, F> InputGrid for VisibilityGrid<'a, D, F> +impl InputGrid for VisibilityGrid where - D: 'static + Clone + Default + Send + Sync, F: FnMut(Coord) -> u8, { - type Grid = VisibilityGrid<'a, D, F>; + type Grid = (u32, u32); type Opacity = u8; fn size(&self, grid: &Self::Grid) -> Size { - Size::new(grid.0.width as u32, grid.0.height as u32) + Size::new(grid.0, grid.1) } fn get_opacity(&self, _grid: &Self::Grid, coord: Coord) -> Self::Opacity { @@ -261,6 +256,7 @@ fn update_viewshed( Or<(Changed, Changed)>, >, visible: Query<&Visible>, + obstructions: Query<&MapObstruction>, mut viewers: Query<(Entity, &mut Viewshed, &mut VisibleEntities, &Transform)>, map: Query<&Map>, rapier_context: Res, @@ -321,6 +317,7 @@ fn update_viewshed( &*rapier_context, map, &visible, + &obstructions, &mut changed, &mut cache, ); @@ -335,6 +332,7 @@ fn remove_visible( map: Query<&Map>, rapier_context: Res, visible: Query<&Visible>, + obstructions: Query<&MapObstruction>, mut changed: EventWriter, ) { for removed in removed.iter() { @@ -352,6 +350,7 @@ fn remove_visible( &*rapier_context, map, &visible, + &obstructions, &mut changed, &mut cache, ); @@ -430,19 +429,22 @@ impl Default for VisibilityPlugin } } +pub const UPDATE_VIEWSHED_LABEL: &str = "UPDATE_VIEWSHED"; + impl Plugin for VisibilityPlugin { fn build(&self, app: &mut App) { app.add_event::() - .add_system(add_visibility_indices::) + .add_system_to_stage(CoreStage::PreUpdate, add_visibility_indices::) .add_system_set_to_stage( - CoreStage::PostUpdate, + CoreStage::PreUpdate, SystemSet::new() .with_run_criteria(FixedTimestep::step(0.05)) - .with_system(update_viewshed::), + .with_system(update_viewshed::) + .label(UPDATE_VIEWSHED_LABEL), ) .add_system_to_stage(CoreStage::PostUpdate, viewshed_removed) .add_system_to_stage(CoreStage::PostUpdate, remove_visible::) - .add_system_to_stage(CoreStage::PostUpdate, update_revealed_tiles::) - .add_system_to_stage(CoreStage::PostUpdate, log_visible); + .add_system_to_stage(CoreStage::PreUpdate, update_revealed_tiles::) + .add_system_to_stage(CoreStage::PreUpdate, log_visible); } }