From f9e73479d03bf2bfe63f0e3ce883196f21c78e1b Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Wed, 12 Jan 2022 10:45:13 -0500 Subject: [PATCH] Extract spatial indexing (tile contents, and if a tile is blocked) into its own module --- src/ai/adjacent_ai_system.rs | 20 +++--- src/ai/approach_ai_system.rs | 13 ++-- src/ai/chase_ai_system.rs | 22 ++---- src/ai/default_move_system.rs | 20 +++--- src/ai/flee_ai_system.rs | 7 +- src/ai/visible_ai_system.rs | 20 +++--- src/inventory_system.rs | 10 +-- src/main.rs | 1 + src/map.rs | 20 ++---- src/map/dungeon.rs | 5 +- src/map_indexing_system.rs | 19 ++---- src/player.rs | 122 ++++++++++++++++++---------------- src/saveload_system.rs | 3 +- src/spatial.rs | 121 +++++++++++++++++++++++++++++++++ src/trigger_system.rs | 20 +++--- src/visibility_system.rs | 19 +++--- 16 files changed, 264 insertions(+), 178 deletions(-) create mode 100644 src/spatial.rs diff --git a/src/ai/adjacent_ai_system.rs b/src/ai/adjacent_ai_system.rs index 6a0ec23..79ab337 100644 --- a/src/ai/adjacent_ai_system.rs +++ b/src/ai/adjacent_ai_system.rs @@ -1,8 +1,8 @@ use ::specs::prelude::*; use crate::components::{Faction, MyTurn, Position, WantsToMelee}; -use crate::raws::Reaction; -use crate::Map; +use crate::raws::{self, Reaction, RAWS}; +use crate::{spatial, Map}; pub struct AdjacentAI {} @@ -116,21 +116,17 @@ impl<'a> System<'a> for AdjacentAI { fn evaluate( idx: usize, - map: &Map, + _map: &Map, factions: &ReadStorage, my_faction: &str, reactions: &mut Vec<(Entity, Reaction)>, ) { - for other_entity in map.tile_content[idx].iter() { - if let Some(faction) = factions.get(*other_entity) { + spatial::for_each_tile_content(idx, |other_entity| { + if let Some(faction) = factions.get(other_entity) { reactions.push(( - *other_entity, - crate::raws::faction_reaction( - my_faction, - &faction.name, - &crate::raws::RAWS.lock().unwrap(), - ), + other_entity, + raws::faction_reaction(my_faction, &faction.name, &RAWS.lock().unwrap()), )) } - } + }); } diff --git a/src/ai/approach_ai_system.rs b/src/ai/approach_ai_system.rs index c3f3cc1..c891675 100644 --- a/src/ai/approach_ai_system.rs +++ b/src/ai/approach_ai_system.rs @@ -2,7 +2,7 @@ use ::rltk::a_star_search; use ::specs::prelude::*; use crate::components::{EntityMoved, MyTurn, Position, Viewshed, WantsToApproach}; -use crate::Map; +use crate::{spatial, Map}; pub struct ApproachAI {} @@ -12,7 +12,7 @@ impl<'a> System<'a> for ApproachAI { WriteStorage<'a, MyTurn>, WriteStorage<'a, WantsToApproach>, WriteStorage<'a, Position>, - WriteExpect<'a, Map>, + ReadExpect<'a, Map>, WriteStorage<'a, Viewshed>, WriteStorage<'a, EntityMoved>, Entities<'a>, @@ -23,7 +23,7 @@ impl<'a> System<'a> for ApproachAI { mut turns, mut want_approach, mut positions, - mut map, + map, mut viewsheds, mut entity_moved, entities, @@ -48,16 +48,15 @@ impl<'a> System<'a> for ApproachAI { ); if path.success && path.steps.len() > 1 { - let mut idx = map.xy_idx(pos.x, pos.y); - map.blocked[idx] = false; + let idx = map.xy_idx(pos.x, pos.y); pos.x = path.steps[1] as i32 % map.width; pos.y = path.steps[1] as i32 / map.width; entity_moved .insert(entity, EntityMoved {}) .expect("Unable to insert moved marker"); - idx = map.xy_idx(pos.x, pos.y); - map.blocked[idx] = true; + let new_idx = map.xy_idx(pos.x, pos.y); + spatial::move_entity(entity, idx, new_idx); viewshed.dirty = true; } } diff --git a/src/ai/chase_ai_system.rs b/src/ai/chase_ai_system.rs index 935b954..376ddab 100644 --- a/src/ai/chase_ai_system.rs +++ b/src/ai/chase_ai_system.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use ::specs::prelude::*; use crate::components::{Chasing, EntityMoved, MyTurn, Position, Viewshed}; -use crate::Map; +use crate::{spatial, Map}; pub struct ChaseAI {} @@ -13,22 +13,15 @@ impl<'a> System<'a> for ChaseAI { WriteStorage<'a, MyTurn>, WriteStorage<'a, Chasing>, WriteStorage<'a, Position>, - WriteExpect<'a, Map>, + ReadExpect<'a, Map>, WriteStorage<'a, Viewshed>, WriteStorage<'a, EntityMoved>, Entities<'a>, ); fn run(&mut self, data: Self::SystemData) { - let ( - mut turns, - mut chasing, - mut positions, - mut map, - mut viewsheds, - mut entity_moved, - entities, - ) = data; + let (mut turns, mut chasing, mut positions, map, mut viewsheds, mut entity_moved, entities) = + data; let mut targets: HashMap = HashMap::new(); let mut end_chase: Vec = Vec::new(); @@ -58,17 +51,16 @@ impl<'a> System<'a> for ChaseAI { ); if path.success && path.steps.len() > 1 && path.steps.len() < 15 { - let mut idx = map.xy_idx(pos.x, pos.y); - map.blocked[idx] = false; + let idx = map.xy_idx(pos.x, pos.y); pos.x = path.steps[1] as i32 % map.width; pos.y = path.steps[1] as i32 / map.width; entity_moved .insert(entity, EntityMoved {}) .expect("Unable to insert movement marker"); - idx = map.xy_idx(pos.x, pos.y); - map.blocked[idx] = true; + let new_idx = map.xy_idx(pos.x, pos.y); viewshed.dirty = true; + spatial::move_entity(entity, idx, new_idx); turn_done.push(entity); } else { end_chase.push(entity); diff --git a/src/ai/default_move_system.rs b/src/ai/default_move_system.rs index ac61920..8f39725 100644 --- a/src/ai/default_move_system.rs +++ b/src/ai/default_move_system.rs @@ -2,7 +2,7 @@ use ::rltk::RandomNumberGenerator; use ::specs::prelude::*; use crate::components::{EntityMoved, MoveMode, Movement, MyTurn, Position, Viewshed}; -use crate::{tile_walkable, Map}; +use crate::{spatial, tile_walkable, Map}; pub struct DefaultMoveAI {} @@ -12,7 +12,7 @@ impl<'a> System<'a> for DefaultMoveAI { WriteStorage<'a, MyTurn>, WriteStorage<'a, MoveMode>, WriteStorage<'a, Position>, - WriteExpect<'a, Map>, + ReadExpect<'a, Map>, WriteStorage<'a, Viewshed>, WriteStorage<'a, EntityMoved>, WriteExpect<'a, RandomNumberGenerator>, @@ -24,7 +24,7 @@ impl<'a> System<'a> for DefaultMoveAI { mut turns, mut move_mode, mut positions, - mut map, + map, mut viewsheds, mut entity_moved, mut rng, @@ -59,15 +59,14 @@ impl<'a> System<'a> for DefaultMoveAI { if x > 0 && x < map.width - 1 && y > 0 && y < map.height - 1 { let dest_idx = map.xy_idx(x, y); - if !map.blocked[dest_idx] { + if !spatial::is_blocked(dest_idx) { let idx = map.xy_idx(pos.x, pos.y); - map.blocked[idx] = false; pos.x = x; pos.y = y; entity_moved .insert(entity, EntityMoved {}) .expect("Unable to insert movement marker"); - map.blocked[dest_idx] = true; + crate::spatial::move_entity(entity, idx, dest_idx); viewshed.dirty = true; } } @@ -75,18 +74,17 @@ impl<'a> System<'a> for DefaultMoveAI { Movement::RandomWaypoint { path } => { if let Some(path) = path { // We have a target - go there - let mut idx = map.xy_idx(pos.x, pos.y); + let idx = map.xy_idx(pos.x, pos.y); if path.len() > 1 { - if !map.blocked[path[1]] { - map.blocked[idx] = false; + if !spatial::is_blocked(path[1]) { pos.x = (path[1] as i32 % map.width) as i32; pos.y = (path[1] as i32 / map.width) as i32; entity_moved .insert(entity, EntityMoved {}) .expect("Unable to insert movement marker"); - idx = map.xy_idx(pos.x, pos.y); - map.blocked[idx] = true; + let new_idx = map.xy_idx(pos.x, pos.y); + spatial::move_entity(entity, idx, new_idx); viewshed.dirty = true; path.remove(0); // Remove the first step in the path } diff --git a/src/ai/flee_ai_system.rs b/src/ai/flee_ai_system.rs index 54fde06..592d27b 100644 --- a/src/ai/flee_ai_system.rs +++ b/src/ai/flee_ai_system.rs @@ -2,7 +2,7 @@ use ::rltk::DijkstraMap; use ::specs::prelude::*; use crate::components::{EntityMoved, MyTurn, Position, Viewshed, WantsToFlee}; -use crate::Map; +use crate::{spatial, Map}; pub struct FleeAI {} @@ -46,9 +46,8 @@ impl<'a> System<'a> for FleeAI { let flee_map = DijkstraMap::new(map.width, map.height, &flee.indices, &*map, 100.0); if let Some(flee_target) = DijkstraMap::find_highest_exit(&flee_map, my_idx, &*map) { - if !map.blocked[flee_target] { - map.blocked[my_idx] = false; - map.blocked[flee_target] = true; + if !spatial::is_blocked(flee_target) { + spatial::move_entity(entity, my_idx, flee_target); viewshed.dirty = true; pos.x = flee_target as i32 % map.width; pos.y = flee_target as i32 / map.width; diff --git a/src/ai/visible_ai_system.rs b/src/ai/visible_ai_system.rs index aef546b..2e7ce14 100644 --- a/src/ai/visible_ai_system.rs +++ b/src/ai/visible_ai_system.rs @@ -3,8 +3,8 @@ use ::specs::prelude::*; use crate::components::{ Chasing, Faction, MyTurn, Position, Viewshed, WantsToApproach, WantsToFlee, }; -use crate::raws::Reaction; -use crate::Map; +use crate::raws::{self, Reaction, RAWS}; +use crate::{spatial, Map}; pub struct VisibleAI {} @@ -87,22 +87,18 @@ impl<'a> System<'a> for VisibleAI { fn evaluate( idx: usize, - map: &Map, + _map: &Map, factions: &ReadStorage, my_faction: &str, reactions: &mut Vec<(usize, Reaction, Entity)>, ) { - for other_entity in map.tile_content[idx].iter() { - if let Some(faction) = factions.get(*other_entity) { + spatial::for_each_tile_content(idx, |other_entity| { + if let Some(faction) = factions.get(other_entity) { reactions.push(( idx, - crate::raws::faction_reaction( - my_faction, - &faction.name, - &crate::raws::RAWS.lock().unwrap(), - ), - *other_entity, + raws::faction_reaction(my_faction, &faction.name, &RAWS.lock().unwrap()), + other_entity, )); } - } + }); } diff --git a/src/inventory_system.rs b/src/inventory_system.rs index 51bd2ee..6680e4f 100644 --- a/src/inventory_system.rs +++ b/src/inventory_system.rs @@ -4,7 +4,7 @@ use ::specs::prelude::*; use crate::components::*; use crate::game_log::GameLog; use crate::particle_system::ParticleBuilder; -use crate::{Map, RunState}; +use crate::{spatial, Map, RunState}; pub struct ItemCollectionSystem {} @@ -116,9 +116,7 @@ impl<'a> System<'a> for ItemUseSystem { None => { // Single target in tile let idx = map.xy_idx(target.x, target.y); - for mob in map.tile_content[idx].iter() { - targets.push(*mob); - } + spatial::for_each_tile_content(idx, |mob| targets.push(mob)); } Some(area_effect) => { // AoE @@ -130,9 +128,7 @@ impl<'a> System<'a> for ItemUseSystem { for tile_idx in blast_tiles.iter() { let idx = map.xy_idx(tile_idx.x, tile_idx.y); - for mob in map.tile_content[idx].iter() { - targets.push(*mob); - } + spatial::for_each_tile_content(idx, |mob| targets.push(mob)); particle_builder.request( tile_idx.x, diff --git a/src/main.rs b/src/main.rs index 2ab5cc4..5a0b3c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ pub mod raws; mod rect; mod rex_assets; pub mod saveload_system; +mod spatial; mod spawner; mod trigger_system; mod visibility_system; diff --git a/src/map.rs b/src/map.rs index e0d2b31..215093d 100644 --- a/src/map.rs +++ b/src/map.rs @@ -6,13 +6,13 @@ use std::collections::HashSet; use ::rltk::{Algorithm2D, BaseMap, Point, SmallVec}; use ::serde::{Deserialize, Serialize}; -use ::specs::prelude::*; pub use dungeon::*; use rltk::RGB; pub use themes::*; pub use tiletype::{tile_opaque, tile_walkable, TileType}; use crate::map::tiletype::tile_cost; +use crate::spatial; #[derive(Default, Serialize, Deserialize, Clone)] pub struct Map { @@ -21,17 +21,12 @@ pub struct Map { pub height: i32, pub revealed_tiles: Vec, pub visible_tiles: Vec, - pub blocked: Vec, pub depth: i32, pub bloodstains: HashSet, pub view_blocked: HashSet, pub name: String, pub outdoors: bool, pub light: Vec, - - #[serde(skip_serializing)] - #[serde(skip_deserializing)] - pub tile_content: Vec>, } impl Map { @@ -46,24 +41,21 @@ impl Map { let idx = self.xy_idx(x, y); - !self.blocked[idx] + !spatial::is_blocked(idx) } pub fn populate_blocked(&mut self) { - for (i, tile) in self.tiles.iter_mut().enumerate() { - self.blocked[i] = !tile_walkable(*tile); - } + spatial::populate_blocked_from_map(self); } pub fn clear_content_index(&mut self) { - for content in self.tile_content.iter_mut() { - content.clear(); - } + spatial::clear(); } /// Generates an empty map, consisting entirely of solid walls pub fn new(new_depth: i32, width: i32, height: i32, name: S) -> Map { let map_tile_count = (width * height) as usize; + crate::spatial::set_size(map_tile_count); Map { tiles: vec![TileType::Wall; map_tile_count], @@ -71,8 +63,6 @@ impl Map { height, revealed_tiles: vec![false; map_tile_count], visible_tiles: vec![false; map_tile_count], - blocked: vec![false; map_tile_count], - tile_content: vec![Vec::new(); map_tile_count], depth: new_depth, bloodstains: HashSet::new(), view_blocked: HashSet::new(), diff --git a/src/map/dungeon.rs b/src/map/dungeon.rs index 00d4ab9..22f026f 100644 --- a/src/map/dungeon.rs +++ b/src/map/dungeon.rs @@ -26,10 +26,7 @@ impl MasterDungeonMap { pub fn get_map(&self, depth: i32) -> Option { if self.maps.contains_key(&depth) { - let mut result = self.maps[&depth].clone(); - result.tile_content = vec![Vec::new(); (result.width * result.height) as usize]; - - Some(result) + Some(self.maps[&depth].clone()) } else { None } diff --git a/src/map_indexing_system.rs b/src/map_indexing_system.rs index a42d7dd..c3fd676 100644 --- a/src/map_indexing_system.rs +++ b/src/map_indexing_system.rs @@ -1,33 +1,26 @@ use ::specs::prelude::*; -use crate::{BlocksTile, Map, Position}; +use crate::{spatial, BlocksTile, Map, Position}; pub struct MapIndexingSystem {} impl<'a> System<'a> for MapIndexingSystem { type SystemData = ( - WriteExpect<'a, Map>, + ReadExpect<'a, Map>, ReadStorage<'a, Position>, ReadStorage<'a, BlocksTile>, Entities<'a>, ); fn run(&mut self, data: Self::SystemData) { - let (mut map, position, blockers, entities) = data; + let (map, position, blockers, entities) = data; - map.populate_blocked(); - map.clear_content_index(); + spatial::clear(); + spatial::populate_blocked_from_map(&*map); for (entity, position) in (&entities, &position).join() { let idx = map.xy_idx(position.x, position.y); - - // If it's a blocking entity, note that in the map object - if let Some(_p) = blockers.get(entity) { - map.blocked[idx] = true; - } - - // Push a copy of the entity to the indexed slot - map.tile_content[idx].push(entity); + spatial::index_entity(entity, idx, blockers.get(entity).is_some()); } } } diff --git a/src/player.rs b/src/player.rs index ead955e..2bc7218 100644 --- a/src/player.rs +++ b/src/player.rs @@ -8,8 +8,8 @@ use crate::components::{ Item, Player, Pools, Position, Renderable, Viewshed, WantsToMelee, WantsToPickupItem, }; use crate::game_log::GameLog; -use crate::raws::Reaction; -use crate::{Map, RunState, State, TileType}; +use crate::raws::{self, Reaction, RAWS}; +use crate::{spatial, Map, RunState, State, TileType}; pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState { let mut positions = ecs.write_storage::(); @@ -41,68 +41,74 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState } let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y); - for potential_target in map.tile_content[destination_idx].iter() { - let mut hostile = true; - if combat_stats.get(*potential_target).is_some() { - if let Some(faction) = factions.get(*potential_target) { - let reaction = crate::raws::faction_reaction( - &faction.name, - "Player", - &crate::raws::RAWS.lock().unwrap(), - ); - if reaction != Reaction::Attack { - hostile = false; + result = + spatial::for_each_tile_content_with_gamemode(destination_idx, |potential_target| { + let mut hostile = true; + if combat_stats.get(potential_target).is_some() { + if let Some(faction) = factions.get(potential_target) { + let reaction = crate::raws::faction_reaction( + &faction.name, + "Player", + &RAWS.lock().unwrap(), + ); + if reaction != Reaction::Attack { + hostile = false; + } } } - } - if !hostile { - // Note that we want to move the bystander - swap_entities.push((*potential_target, pos.x, pos.y)); + if !hostile { + // Note that we want to move the bystander + swap_entities.push((potential_target, pos.x, pos.y)); - // Move the player - pos.x = min(map.width - 1, max(0, pos.x + delta_x)); - pos.y = min(map.height - 1, max(0, pos.y + delta_y)); - entity_moved - .insert(entity, EntityMoved {}) - .expect("Unable to insert marker"); + // Move the player + pos.x = min(map.width - 1, max(0, pos.x + delta_x)); + pos.y = min(map.height - 1, max(0, pos.y + delta_y)); + entity_moved + .insert(entity, EntityMoved {}) + .expect("Unable to insert marker"); - viewshed.dirty = true; - let mut ppos = ecs.write_resource::(); - ppos.x = pos.x; - ppos.y = pos.y; - } else { - let target = combat_stats.get(*potential_target); - if let Some(_target) = target { + viewshed.dirty = true; + let mut ppos = ecs.write_resource::(); + ppos.x = pos.x; + ppos.y = pos.y; + + return Some(RunState::Ticking); + } else if combat_stats.get(potential_target).is_some() { wants_to_melee .insert( entity, WantsToMelee { - target: *potential_target, + target: potential_target, }, ) .expect("Add target failed"); - return RunState::Ticking; + return Some(RunState::Ticking); } - } - let door = doors.get_mut(*potential_target); - if let Some(door) = door { - door.open = true; - blocks_visibility.remove(*potential_target); - blocks_movement.remove(*potential_target); - let glyph = renderables.get_mut(*potential_target).unwrap(); - glyph.glyph = rltk::to_cp437('/'); - viewshed.dirty = true; - result = RunState::Ticking; - } - } - if !map.blocked[destination_idx] { + if let Some(door) = doors.get_mut(potential_target) { + door.open = true; + blocks_visibility.remove(potential_target); + blocks_movement.remove(potential_target); + let glyph = renderables.get_mut(potential_target).unwrap(); + glyph.glyph = rltk::to_cp437('/'); + viewshed.dirty = true; + + return Some(RunState::Ticking); + } + + None + }); + + if !spatial::is_blocked(destination_idx) { + let old_idx = map.xy_idx(pos.x, pos.y); pos.x = min(map.width - 1, max(0, pos.x + delta_x)); pos.y = min(map.height - 1, max(0, pos.y + delta_y)); + let new_idx = map.xy_idx(pos.x, pos.y); entity_moved .insert(entity, EntityMoved {}) .expect("Failed to add EntityMoved flag to player"); + spatial::move_entity(entity, old_idx, new_idx); viewshed.dirty = true; let mut ppos = ecs.write_resource::(); @@ -120,8 +126,13 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState for m in swap_entities.iter() { let their_pos = positions.get_mut(m.0); if let Some(their_pos) = their_pos { + let old_idx = map.xy_idx(their_pos.x, their_pos.y); their_pos.x = m.1; their_pos.y = m.2; + let new_idx = map.xy_idx(their_pos.x, their_pos.y); + spatial::move_entity(m.0, old_idx, new_idx); + + result = RunState::Ticking; } } @@ -205,21 +216,16 @@ fn skip_turn(ecs: &mut World) -> RunState { let viewshed = viewshed_components.get(*player_entity).unwrap(); for tile in viewshed.visible_tiles.iter() { let idx = worldmap_resource.xy_idx(tile.x, tile.y); - for entity_id in worldmap_resource.tile_content[idx].iter() { - match factions.get(*entity_id) { - None => {} - Some(faction) => { - let reaction = crate::raws::faction_reaction( - &faction.name, - "Player", - &crate::raws::RAWS.lock().unwrap(), - ); - if reaction == Reaction::Attack { - can_heal = false; - } + spatial::for_each_tile_content(idx, |entity_id| match factions.get(entity_id) { + None => {} + Some(faction) => { + let reaction = + raws::faction_reaction(&faction.name, "Player", &RAWS.lock().unwrap()); + if reaction == Reaction::Attack { + can_heal = false; } } - } + }); } // Don't allow healing when hungry or starving diff --git a/src/saveload_system.rs b/src/saveload_system.rs index 60840c2..aab6034 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -11,6 +11,7 @@ use ::specs::saveload::{ use crate::components::*; use crate::map::{Map, MasterDungeonMap}; +use crate::spatial; macro_rules! serialize_individually { ($ecs:expr, $ser:expr, $data:expr, $( $type:ty),*,) => { @@ -233,7 +234,7 @@ pub fn load_game(ecs: &mut World) { for (e, h) in (&entities, &helper).join() { let mut worldmap = ecs.write_resource::(); *worldmap = h.map.clone(); - worldmap.tile_content = vec![Vec::new(); (worldmap.height * worldmap.width) as usize]; + spatial::set_size((worldmap.height * worldmap.width) as usize); deleteme = Some(e); } for (e, h) in (&entities, &helper2).join() { diff --git a/src/spatial.rs b/src/spatial.rs new file mode 100644 index 0000000..f6eb854 --- /dev/null +++ b/src/spatial.rs @@ -0,0 +1,121 @@ +use std::sync::Mutex; + +use ::specs::prelude::*; + +use crate::{tile_walkable, Map, RunState}; + +struct SpatialMap { + blocked: Vec<(bool, bool)>, + tile_content: Vec>, +} + +impl SpatialMap { + fn new() -> Self { + Self { + blocked: Vec::new(), + tile_content: Vec::new(), + } + } +} + +lazy_static! { + static ref SPATIAL_MAP: Mutex = Mutex::new(SpatialMap::new()); +} + +pub fn set_size(map_tile_count: usize) { + let mut lock = SPATIAL_MAP.lock().unwrap(); + lock.blocked = vec![(false, false); map_tile_count]; + lock.tile_content = vec![Vec::new(); map_tile_count]; +} + +pub fn clear() { + let mut lock = SPATIAL_MAP.lock().unwrap(); + lock.blocked.iter_mut().for_each(|b| { + b.0 = false; + b.1 = false; + }); + for content in lock.tile_content.iter_mut() { + content.clear(); + } +} + +pub fn populate_blocked_from_map(map: &Map) { + let mut lock = SPATIAL_MAP.lock().unwrap(); + for (i, tile) in map.tiles.iter().enumerate() { + lock.blocked[i].0 = !tile_walkable(*tile); + } +} + +pub fn index_entity(entity: Entity, idx: usize, blocks_tile: bool) { + let mut lock = SPATIAL_MAP.lock().unwrap(); + lock.tile_content[idx].push((entity, blocks_tile)); + if blocks_tile { + lock.blocked[idx].1 = true; + } +} + +pub fn is_blocked(idx: usize) -> bool { + let lock = SPATIAL_MAP.lock().unwrap(); + + lock.blocked[idx].0 || lock.blocked[idx].1 +} + +pub fn for_each_tile_content(idx: usize, mut f: F) +where + F: FnMut(Entity), +{ + let lock = SPATIAL_MAP.lock().unwrap(); + for entity in lock.tile_content[idx].iter() { + f(entity.0); + } +} + +pub fn for_each_tile_content_with_gamemode(idx: usize, mut f: F) -> RunState +where + F: FnMut(Entity) -> Option, +{ + let lock = SPATIAL_MAP.lock().unwrap(); + for entity in lock.tile_content[idx].iter() { + if let Some(rs) = f(entity.0) { + return rs; + } + } + + RunState::AwaitingInput +} + +/// Move an entity on the map from `moving_from` index to the `moving_to` index. +/// This also recalculates if these two tiles are blocked based on the entities +/// within these tiles. +pub fn move_entity(entity: Entity, moving_from: usize, moving_to: usize) { + let mut lock = SPATIAL_MAP.lock().unwrap(); + let mut entity_blocks = false; + + lock.tile_content[moving_from].retain(|(e, blocks)| { + if *e == entity { + entity_blocks = *blocks; + false + } else { + true + } + }); + lock.tile_content[moving_to].push((entity, entity_blocks)); + + // Recalculate blocks for both tiles + let mut from_blocked = false; + let mut to_blocked = false; + lock.tile_content[moving_from] + .iter() + .for_each(|(_, blocks)| { + if *blocks { + from_blocked = true; + } + }); + lock.tile_content[moving_to].iter().for_each(|(_, blocks)| { + if *blocks { + to_blocked = true; + } + }); + lock.blocked[moving_from].1 = from_blocked; + lock.blocked[moving_to].1 = to_blocked; +} diff --git a/src/trigger_system.rs b/src/trigger_system.rs index 31c148f..38177a3 100644 --- a/src/trigger_system.rs +++ b/src/trigger_system.rs @@ -6,7 +6,7 @@ use crate::components::{ }; use crate::game_log::GameLog; use crate::particle_system::ParticleBuilder; -use crate::Map; +use crate::{spatial, Map}; pub struct TriggerSystem {} @@ -48,22 +48,22 @@ impl<'a> System<'a> for TriggerSystem { for (entity, mut _entity_moved, pos) in (&entities, &mut entity_moved, &position).join() { let idx = map.xy_idx(pos.x, pos.y); - for entity_id in map.tile_content[idx].iter() { + spatial::for_each_tile_content(idx, |entity_id| { // Do not bother to check yourself for being a trap! - if entity != *entity_id { - match entry_trigger.get(*entity_id) { + if entity != entity_id { + match entry_trigger.get(entity_id) { None => {} Some(_trigger) => { // We triggered it - if let Some(name) = names.get(*entity_id) { + if let Some(name) = names.get(entity_id) { log.append(format!("{} triggers!", &name.name)); } // The trap is no longer hidden - hidden.remove(*entity_id); + hidden.remove(entity_id); // If the trap is damage inflicting, do it - if let Some(damage) = inflicts_damage.get(*entity_id) { + if let Some(damage) = inflicts_damage.get(entity_id) { particle_builder.request( pos.x, pos.y, @@ -82,13 +82,13 @@ impl<'a> System<'a> for TriggerSystem { } // If it is single activation, it needs to be removed - if let Some(_sa) = single_activation.get(*entity_id) { - remove_entities.push(*entity_id); + if single_activation.get(entity_id).is_some() { + remove_entities.push(entity_id); } } } } - } + }); } // Remove any single activation traps diff --git a/src/visibility_system.rs b/src/visibility_system.rs index c7b3e77..284cc38 100644 --- a/src/visibility_system.rs +++ b/src/visibility_system.rs @@ -3,7 +3,7 @@ use ::specs::prelude::*; use crate::components::{BlocksVisibility, Hidden, Name}; use crate::game_log::GameLog; -use crate::{Map, Player, Position, Viewshed}; +use crate::{spatial, Map, Player, Position, Viewshed}; pub struct VisibilitySystem {} @@ -53,7 +53,7 @@ impl<'a> System<'a> for VisibilitySystem { .retain(|p| p.x >= 0 && p.x < map.width && p.y >= 0 && p.y < map.height); // if this is the player, reveal what they can see - if let Some(_p) = player.get(ent) { + if player.get(ent).is_some() { for t in map.visible_tiles.iter_mut() { *t = false } @@ -64,17 +64,18 @@ impl<'a> System<'a> for VisibilitySystem { map.visible_tiles[idx] = true; // Chance to reveal hidden things - for e in map.tile_content[idx].iter() { - if let Some(_maybe_hidden) = hidden.get(*e) { + spatial::for_each_tile_content(idx, |e| { + let maybe_hidden = hidden.get(e); + if let Some(_maybe_hidden) = maybe_hidden { if rng.roll_dice(1, 24) == 1 { - if let Some(name) = names.get(*e) { - log.append(format!("You spotted a {}.", &name.name)); + let name = names.get(e); + if let Some(name) = name { + log.entries.push(format!("You spotted a {}.", &name.name)); } - - hidden.remove(*e); + hidden.remove(e); } } - } + }); } } }