use std::collections::{HashMap, HashSet}; use bevy::prelude::*; use coord_2d::{Coord, Size}; use derive_more::{Deref, DerefMut}; use shadowcast::{vision_distance, Context, InputGrid}; use crate::{ core::{Angle, Coordinates, Player, PointLike}, log::Log, map::{ITileType, Map, MapConfig}, }; #[derive(Clone, Copy, Debug, Default, Reflect)] #[reflect(Component)] pub struct BlocksVisibility; #[derive(Clone, Copy, Debug, Default, Reflect)] #[reflect(Component)] pub struct DontLogWhenVisible; #[derive(Clone, Debug, Default, Deref, DerefMut, Reflect)] #[reflect(Component)] pub struct RevealedTiles(pub Vec); #[derive(Clone, Debug, Eq, PartialEq)] pub struct Viewshed { pub visible: HashSet<(i32, i32)>, pub range: u32, } impl Default for Viewshed { fn default() -> Self { Self { range: 15, visible: HashSet::new(), } } } impl Viewshed { pub fn is_visible(&self, point: &dyn PointLike) -> bool { self.visible.contains(&point.into()) } } #[derive(Clone, Debug, Default, Deref, DerefMut, Reflect)] #[reflect(Component)] pub struct VisibilityBlocked(pub Vec); #[derive(Clone, Debug, Default, Deref, DerefMut, Reflect)] #[reflect(Component)] pub struct VisibleTiles(pub Vec); fn add_visibility_indices( mut commands: Commands, query: Query< (Entity, &Map), ( Added, Without, Without, Without, ), >, map_config: Res, ) { for (entity, map) in query.iter() { let mut v = vec![]; for tile in &map.base.tiles { v.push(tile.blocks_visibility()); } commands.entity(entity).insert(VisibilityBlocked(v)); let count = map.count(); commands .entity(entity) .insert(VisibleTiles(vec![false; count])); commands .entity(entity) .insert(RevealedTiles(vec![map_config.start_revealed; count])); } } pub(crate) fn set_visibility_blocked( map: &Map, index: usize, visibility_blockers: &Query<&BlocksVisibility>, visibility_blocked: &mut VisibilityBlocked, ) { let mut new_visibility_blocked = map.base.tiles[index].blocks_visibility(); if !new_visibility_blocked { for e in &map.entities[index] { if visibility_blockers.get(*e).is_ok() { new_visibility_blocked = true; break; } } } visibility_blocked[index] = new_visibility_blocked; } #[derive(Default, Deref, DerefMut)] struct PrevIndex(HashMap); fn map_visibility_indexing( mut map: Query<(&Map, &mut VisibilityBlocked)>, mut prev_index: ResMut, query: Query< (Entity, &Coordinates), ( With, Or<(Changed, Changed)>, ), >, visibility_blockers: Query<&BlocksVisibility>, ) { for (entity, coordinates) in query.iter() { for (map, mut visibility_blocked) in map.iter_mut() { let idx = coordinates.to_index(map.width()); if let Some(prev_idx) = prev_index.get(&entity) { if *prev_idx == idx { continue; } set_visibility_blocked( &map, *prev_idx, &visibility_blockers, &mut visibility_blocked, ); } visibility_blocked[idx] = true; prev_index.insert(entity, idx); } } } fn remove_blocks_visibility( mut prev_index: ResMut, mut map: Query<(&Map, &mut VisibilityBlocked)>, removed: RemovedComponents, coordinates: Query<&Coordinates>, blocks_visibility: Query<&BlocksVisibility>, ) { for entity in removed.iter() { for (map, mut visibility_blocked) in map.iter_mut() { let prev = prev_index.get(&entity).cloned(); if let Some(prev) = prev { prev_index.remove(&entity); set_visibility_blocked(&map, prev, &blocks_visibility, &mut visibility_blocked); } if let Ok(coordinates) = coordinates.get_component::(entity) { let idx = coordinates.to_index(map.width()); set_visibility_blocked(&map, idx, &blocks_visibility, &mut visibility_blocked); } } } } struct VisibilityGrid(Map, VisibilityBlocked); impl InputGrid for VisibilityGrid { type Grid = VisibilityGrid; type Opacity = u8; fn size(&self, grid: &Self::Grid) -> Size { Size::new(grid.0.width() as u32, grid.0.height() as u32) } fn get_opacity(&self, grid: &Self::Grid, coord: Coord) -> Self::Opacity { let point = (coord.x, coord.y); let index = point.to_index(grid.0.width()); if grid.1 .0[index] { 255 } else { 0 } } } fn update_viewshed( viewshed: &mut Viewshed, start: &Coordinates, map: &Map, visibility_blocked: &VisibilityBlocked, ) { let mut context: Context = Context::default(); let vision_distance = vision_distance::Circle::new(viewshed.range); let coord = Coord::new(start.x_i32(), start.y_i32()); viewshed.visible.clear(); let visibility_grid = VisibilityGrid(map.clone(), visibility_blocked.clone()); context.for_each_visible( coord, &visibility_grid, &visibility_grid, vision_distance, 255, |coord, _directions, _visibility| { viewshed.visible.insert((coord.x, coord.y)); }, ); } fn update_viewshed_for_coordinates( mut viewers: Query<(&mut Viewshed, &Coordinates), Changed>, map: Query<(&Map, &VisibilityBlocked)>, ) { for (mut viewshed, start) in viewers.iter_mut() { for (map, visibility_blocked) in map.iter() { update_viewshed(&mut viewshed, start, &map, &visibility_blocked); } } } #[derive(Clone, Debug, Default, Deref, DerefMut)] struct PrevVisibilityBlocked(Vec); fn visibility_blocked_added( mut prev_visibility_blocked: ResMut, added: Query<&VisibilityBlocked, Added>, ) { for visibility_blocked in added.iter() { **prev_visibility_blocked = visibility_blocked.to_vec(); } } fn visibility_blocked_removed( mut prev_visibility_blocked: ResMut, removed: RemovedComponents, ) { for _ in removed.iter() { prev_visibility_blocked.clear(); } } fn update_viewshed_for_visibility_blocked( mut prev_visibility_blocked: ResMut, map: Query<(&Map, &VisibilityBlocked), Changed>, mut queryset: QuerySet<( Query<(Entity, &Viewshed)>, Query<(&mut Viewshed, &Coordinates)>, )>, ) { for (map, visibility_blocked) in map.iter() { let mut to_check = vec![]; for (index, new) in visibility_blocked.iter().enumerate() { let changed = if let Some(prev) = prev_visibility_blocked.get(index) { prev != new } else { true }; if changed { let coords = (index % map.width(), index / map.width()); for (entity, viewshed) in queryset.q0().iter() { if viewshed.is_visible(&coords) { to_check.push(entity); } } } } to_check.dedup(); for entity in to_check { if let Ok((mut viewshed, coordinates)) = queryset.q1_mut().get_mut(entity) { update_viewshed(&mut viewshed, coordinates, &map, &visibility_blocked); } } **prev_visibility_blocked = visibility_blocked.to_vec(); } } fn update_visible_and_revealed_tiles( mut map: Query< (&Map, &mut RevealedTiles, &mut VisibleTiles), ( With, Or<(Changed, Changed)>, ), >, viewers: Query<&Viewshed, With>, ) { for (map, mut revealed_tiles, mut visible_tiles) in map.iter_mut() { for viewshed in viewers.iter() { for t in visible_tiles.iter_mut() { *t = false } for v in viewshed.visible.iter() { let idx = v.to_index(map.width()); revealed_tiles[idx] = true; visible_tiles[idx] = true; } } } } fn log_visible( time: Res