diff --git a/src/ai/adjacent_ai_system.rs b/src/ai/adjacent_ai_system.rs index 0cbdd7c..5e27e0d 100644 --- a/src/ai/adjacent_ai_system.rs +++ b/src/ai/adjacent_ai_system.rs @@ -1,5 +1,4 @@ use crate::{raws::Reaction, Faction, Map, Position, TakingTurn, WantsToMelee}; -use rltk::prelude::*; use specs::prelude::*; pub struct AdjacentAI {} diff --git a/src/ai/mod.rs b/src/ai/mod.rs index 5415dfd..0b54d07 100644 --- a/src/ai/mod.rs +++ b/src/ai/mod.rs @@ -14,3 +14,5 @@ mod monster_ai_system; pub use monster_ai_system::MonsterAI; mod adjacent_ai_system; pub use adjacent_ai_system::AdjacentAI; +mod visible_ai_system; +pub use visible_ai_system::VisibleAI; diff --git a/src/ai/visible_ai_system.rs b/src/ai/visible_ai_system.rs new file mode 100644 index 0000000..b675d0b --- /dev/null +++ b/src/ai/visible_ai_system.rs @@ -0,0 +1,111 @@ +use crate::{ + raws::Reaction, Faction, Map, Mind, Position, TakingTurn, Telepath, Viewshed, WantsToApproach, WantsToFlee, +}; +use specs::prelude::*; +use std::collections::HashSet; + +pub struct VisibleAI {} + +impl<'a> System<'a> for VisibleAI { + #[allow(clippy::type_complexity)] + type SystemData = ( + WriteStorage<'a, TakingTurn>, + ReadStorage<'a, Faction>, + ReadStorage<'a, Position>, + ReadExpect<'a, Map>, + WriteStorage<'a, WantsToApproach>, + WriteStorage<'a, WantsToFlee>, + Entities<'a>, + ReadExpect<'a, Entity>, + ReadStorage<'a, Viewshed>, + ReadStorage<'a, Telepath>, + ReadStorage<'a, Mind>, + ); + + fn run(&mut self, data: Self::SystemData) { + let ( + mut turns, + factions, + positions, + map, + mut wants_to_approach, + mut wants_to_flee, + entities, + player, + viewsheds, + telepaths, + minds, + ) = data; + + for (entity, _turn, faction, pos, viewshed) in (&entities, &turns, &factions, &positions, &viewsheds).join() { + if entity == *player { + continue; + } + let this_idx = map.xy_idx(pos.x, pos.y); + let mut reactions: Vec<(usize, Reaction)> = Vec::new(); + let mut flee: Vec = Vec::new(); + let mut idxs: HashSet = HashSet::new(); + for visible_tile in viewshed.visible_tiles.iter() { + let idx = map.xy_idx(visible_tile.x, visible_tile.y); + if this_idx != idx { + evaluate(idx, &map, &factions, &faction.name, &mut reactions, None); + idxs.insert(idx); + } + } + if let Some(is_telepath) = telepaths.get(entity) { + for telepath_tile in is_telepath.telepath_tiles.iter() { + let idx = map.xy_idx(telepath_tile.x, telepath_tile.y); + // If we didn't already evaluate this idx (if it's not contained in the HashSet), + // and it's not the idx we're standing on, then evaluate here w/ minds taken into + // account. + if this_idx != idx && idxs.contains(&idx) { + evaluate(idx, &map, &factions, &faction.name, &mut reactions, Some(&minds)); + } + } + } + let mut done = false; + for reaction in reactions.iter() { + match reaction.1 { + Reaction::Attack => { + wants_to_approach + .insert(entity, WantsToApproach { idx: reaction.0 as i32 }) + .expect("Error inserting WantsToApproach"); + done = true; + } + Reaction::Flee => { + flee.push(reaction.0); + } + _ => {} + } + } + if !done && !flee.is_empty() { + wants_to_flee.insert(entity, WantsToFlee { indices: flee }).expect("Unable to insert"); + } + } + } +} + +fn evaluate( + idx: usize, + map: &Map, + factions: &ReadStorage, + this_faction: &str, + reactions: &mut Vec<(usize, Reaction)>, + minds: Option<&ReadStorage>, +) { + for other_entity in map.tile_content[idx].iter() { + // If minds are passed, we assume we're using telepathy here, + // so if the other entity is mindless, we skip it. + if minds.is_some() { + if minds.unwrap().get(*other_entity).is_none() { + continue; + } + } + if let Some(faction) = factions.get(*other_entity) { + reactions.push(( + idx, + crate::raws::faction_reaction(this_faction, &faction.name, &crate::raws::RAWS.lock().unwrap()), + )); + } + } +} diff --git a/src/main.rs b/src/main.rs index d2e27fe..73713b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -97,6 +97,7 @@ impl State { let mut turn_status_system = ai::TurnStatusSystem {}; let mut quip_system = ai::QuipSystem {}; let mut adjacent_ai = ai::AdjacentAI {}; + let mut visible_ai = ai::VisibleAI {}; let mut mob = ai::MonsterAI {}; let mut bystanders = ai::BystanderAI {}; let mut trigger_system = trigger_system::TriggerSystem {}; @@ -118,6 +119,7 @@ impl State { turn_status_system.run_now(&self.ecs); quip_system.run_now(&self.ecs); adjacent_ai.run_now(&self.ecs); + visible_ai.run_now(&self.ecs); mob.run_now(&self.ecs); bystanders.run_now(&self.ecs); trigger_system.run_now(&self.ecs);