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::{Coordinates, Player, PointLike}, log::Log, map::{ITileType, Map, MapConfig}, }; #[derive(Clone, Copy, Debug, Default, Reflect)] #[reflect(Component)] pub struct BlocksVisibility; #[derive(Clone, Debug, Default, Deref, DerefMut, Reflect)] #[reflect(Component)] pub struct RevealedTiles(pub Vec); #[derive(Clone, Debug)] pub struct Viewshed { pub visible: HashSet<(i32, i32)>, pub range: u32, } impl Default for Viewshed { fn default() -> Self { Self { range: 15, visible: HashSet::new(), } } } #[allow(dead_code)] 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])); } } #[derive(Default, Deref, DerefMut)] struct PreviousIndex(HashMap); fn map_visibility_indexing( mut map: Query<(&Map, &mut VisibilityBlocked)>, mut prev_index: ResMut, query: Query< (Entity, &Coordinates, &BlocksVisibility), 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; } let tile = map.base.tiles[*prev_idx]; let mut new_visibility_blocked = tile.blocks_visibility(); if !new_visibility_blocked { for e in &map.entities[*prev_idx] { if visibility_blockers.get(*e).is_ok() { new_visibility_blocked = true; break; } } } visibility_blocked[*prev_idx] = new_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() { if let Ok(coordinates) = coordinates.get_component::(entity) { prev_index.remove(&entity); for (map, mut visibility_blocked) in map.iter_mut() { let idx = (**coordinates).to_index(map.width()); let tile = map.base.tiles[idx]; let mut new_visibility_blocked = tile.blocks_visibility(); for e in &map.entities[idx] { new_visibility_blocked = new_visibility_blocked || blocks_visibility .get_component::(*e) .is_ok(); } visibility_blocked[idx] = new_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( mut viewers: Query< (&mut Viewshed, &Coordinates), Or<(Changed, Changed)>, >, map: Query<(&Map, &VisibilityBlocked)>, ) { for (mut viewshed, start) in viewers.iter_mut() { for (map, visibility_blocked) in map.iter() { 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 map_visibility( mut map: Query< ( &Map, &VisibilityBlocked, &mut RevealedTiles, &mut VisibleTiles, ), Or<(Changed, Changed)>, >, viewers: Query<(&Player, &Viewshed)>, ) { for (_, viewshed) in viewers.iter() { for (map, _, mut revealed_tiles, mut visible_tiles) in map.iter_mut() { 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