diff --git a/src/ai.rs b/src/ai.rs index ebef657..7a4f1b2 100644 --- a/src/ai.rs +++ b/src/ai.rs @@ -1,7 +1,9 @@ mod animal_ai_system; mod bystander_ai_system; +mod initiative_system; mod monster_ai_system; pub use animal_ai_system::AnimalAI; pub use bystander_ai_system::BystanderAI; +pub use initiative_system::InitiativeSystem; pub use monster_ai_system::MonsterAI; diff --git a/src/ai/animal_ai_system.rs b/src/ai/animal_ai_system.rs index b2eff2e..6af932c 100644 --- a/src/ai/animal_ai_system.rs +++ b/src/ai/animal_ai_system.rs @@ -2,7 +2,7 @@ use ::rltk::{DijkstraMap, DistanceAlg, Point}; use ::specs::prelude::*; use crate::components::{ - Carnivore, EntityMoved, Herbivore, Item, Position, Viewshed, WantsToMelee, + Carnivore, EntityMoved, Herbivore, Item, MyTurn, Position, Viewshed, WantsToMelee, }; use crate::{Map, RunState}; @@ -22,6 +22,7 @@ impl<'a> System<'a> for AnimalAI { WriteStorage<'a, WantsToMelee>, WriteStorage<'a, EntityMoved>, WriteStorage<'a, Position>, + ReadStorage<'a, MyTurn>, ); fn run(&mut self, data: Self::SystemData) { @@ -37,15 +38,12 @@ impl<'a> System<'a> for AnimalAI { mut wants_to_melee, mut entity_moved, mut position, + turns, ) = data; - if *runstate != RunState::MonsterTurn { - return; - } - // Herbivores run away a lot - for (entity, mut viewshed, _herbivore, mut pos) in - (&entities, &mut viewshed, &herbivore, &mut position).join() + for (entity, mut viewshed, _herbivore, mut pos, _turn) in + (&entities, &mut viewshed, &herbivore, &mut position, &turns).join() { let mut run_away_from: Vec = Vec::new(); for other_tile in viewshed.visible_tiles.iter() { @@ -85,8 +83,8 @@ impl<'a> System<'a> for AnimalAI { } // Carnivores just want to eat everything - for (entity, mut viewshed, _carnivore, mut pos) in - (&entities, &mut viewshed, &carnivore, &mut position).join() + for (entity, mut viewshed, _carnivore, mut pos, _turn) in + (&entities, &mut viewshed, &carnivore, &mut position, &turns).join() { let mut run_towards: Vec = Vec::new(); let mut attacked = false; diff --git a/src/ai/bystander_ai_system.rs b/src/ai/bystander_ai_system.rs index 5352bcc..e46db05 100644 --- a/src/ai/bystander_ai_system.rs +++ b/src/ai/bystander_ai_system.rs @@ -1,7 +1,7 @@ use ::rltk::{Point, RandomNumberGenerator}; use specs::prelude::*; -use crate::components::{Bystander, EntityMoved, Name, Position, Quips, Viewshed}; +use crate::components::{Bystander, EntityMoved, MyTurn, Name, Position, Quips, Viewshed}; use crate::game_log::GameLog; use crate::{Map, RunState}; @@ -22,6 +22,7 @@ impl<'a> System<'a> for BystanderAI { WriteExpect<'a, GameLog>, WriteStorage<'a, Quips>, ReadStorage<'a, Name>, + ReadStorage<'a, MyTurn>, ); fn run(&mut self, data: Self::SystemData) { @@ -38,14 +39,11 @@ impl<'a> System<'a> for BystanderAI { mut gamelog, mut quips, names, + turns, ) = data; - if *runstate != RunState::MonsterTurn { - return; - } - - for (entity, mut viewshed, _bystander, mut pos) in - (&entities, &mut viewshed, &bystander, &mut position).join() + for (entity, mut viewshed, _bystander, mut pos, _turn) in + (&entities, &mut viewshed, &bystander, &mut position, &turns).join() { // Possibly quip if let Some(quip) = quips.get_mut(entity) { diff --git a/src/ai/initiative_system.rs b/src/ai/initiative_system.rs new file mode 100644 index 0000000..e0ff4e4 --- /dev/null +++ b/src/ai/initiative_system.rs @@ -0,0 +1,67 @@ +use ::rltk::RandomNumberGenerator; +use ::specs::prelude::*; + +use crate::components::{Attributes, Initiative, MyTurn, Position}; +use crate::RunState; + +pub struct InitiativeSystem {} + +impl<'a> System<'a> for InitiativeSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + WriteStorage<'a, Initiative>, + ReadStorage<'a, Position>, + WriteStorage<'a, MyTurn>, + Entities<'a>, + WriteExpect<'a, RandomNumberGenerator>, + ReadStorage<'a, Attributes>, + WriteExpect<'a, RunState>, + ReadExpect<'a, Entity>, + ); + + fn run(&mut self, data: Self::SystemData) { + let ( + mut initiatives, + positions, + mut turns, + entities, + mut rng, + attributes, + mut runstate, + player, + ) = data; + + if *runstate != RunState::Ticking { + return; + } // We'll be adding Ticking in a moment + + // Clear any remaining MyTurn we left by mistake + turns.clear(); + + // Roll initiative + for (entity, initiative, _pos) in (&entities, &mut initiatives, &positions).join() { + initiative.current -= 1; + if initiative.current < 1 { + // It's my turn + turns + .insert(entity, MyTurn {}) + .expect("Unable to insert turn"); + + // Re-roll + initiative.current = 6 + rng.roll_dice(1, 6); + + // Give a bonus for quickness + if let Some(attr) = attributes.get(entity) { + initiative.current -= attr.quickness.bonus; + } + + // TODO: More initiative granting boosts/penalties will go here later + + // if its the player, we want to go to an AwaitingInput state + if entity == *player { + *runstate = RunState::AwaitingInput; + } + } + } + } +} diff --git a/src/ai/monster_ai_system.rs b/src/ai/monster_ai_system.rs index 23e077c..5a8952f 100644 --- a/src/ai/monster_ai_system.rs +++ b/src/ai/monster_ai_system.rs @@ -1,7 +1,7 @@ use ::rltk::{Point, RGB}; use ::specs::prelude::*; -use crate::components::{Confusion, Monster, Position, Viewshed, WantsToMelee}; +use crate::components::{Confusion, Monster, MyTurn, Position, Viewshed, WantsToMelee}; use crate::particle_system::ParticleBuilder; use crate::{EntityMoved, Map, RunState}; @@ -22,6 +22,7 @@ impl<'a> System<'a> for MonsterAI { WriteStorage<'a, Confusion>, WriteExpect<'a, ParticleBuilder>, WriteStorage<'a, EntityMoved>, + ReadStorage<'a, MyTurn>, ); fn run(&mut self, data: Self::SystemData) { @@ -38,14 +39,11 @@ impl<'a> System<'a> for MonsterAI { mut confused, mut particle_builder, mut entity_moved, + turns, ) = data; - if *runstate != RunState::MonsterTurn { - return; - } - - for (entity, mut viewshed, _monster, mut pos) in - (&entities, &mut viewshed, &monster, &mut position).join() + for (entity, mut viewshed, _monster, mut pos, _turn) in + (&entities, &mut viewshed, &monster, &mut position, &turns).join() { let mut can_act = true; diff --git a/src/hunger_system.rs b/src/hunger_system.rs index 31a096b..4b48e8e 100644 --- a/src/hunger_system.rs +++ b/src/hunger_system.rs @@ -1,6 +1,6 @@ use ::specs::prelude::*; -use crate::components::{HungerClock, HungerState, SufferDamage}; +use crate::components::{HungerClock, HungerState, MyTurn, SufferDamage}; use crate::game_log::GameLog; use crate::RunState; @@ -15,66 +15,56 @@ impl<'a> System<'a> for HungerSystem { ReadExpect<'a, RunState>, WriteStorage<'a, SufferDamage>, WriteExpect<'a, GameLog>, + ReadStorage<'a, MyTurn>, ); fn run(&mut self, data: Self::SystemData) { - let (entities, mut hunger_clock, player_entity, runstate, mut inflict_damage, mut log) = - data; + let ( + entities, + mut hunger_clock, + player_entity, + runstate, + mut inflict_damage, + mut log, + turns, + ) = data; - for (entity, mut clock) in (&entities, &mut hunger_clock).join() { - let mut proceed = false; + for (entity, mut clock, _myturn) in (&entities, &mut hunger_clock, &turns).join() { + clock.duration -= 1; - match *runstate { - RunState::PlayerTurn => { - if entity == *player_entity { - proceed = true; + if clock.duration < 1 { + match clock.state { + HungerState::WellFed => { + clock.state = HungerState::Normal; + clock.duration = 200; + + if entity == *player_entity { + log.append("You are no longer well fed."); + } } - } - RunState::MonsterTurn => { - if entity != *player_entity { - proceed = true; + HungerState::Normal => { + clock.state = HungerState::Hungry; + clock.duration = 200; + + if entity == *player_entity { + log.append("You are hungry."); + } } - } - _ => proceed = false, - } + HungerState::Hungry => { + clock.state = HungerState::Starving; + clock.duration = 200; - if proceed { - clock.duration -= 1; - - if clock.duration < 1 { - match clock.state { - HungerState::WellFed => { - clock.state = HungerState::Normal; - clock.duration = 200; - - if entity == *player_entity { - log.append("You are no longer well fed."); - } + if entity == *player_entity { + log.append("You are starving!"); } - HungerState::Normal => { - clock.state = HungerState::Hungry; - clock.duration = 200; - - if entity == *player_entity { - log.append("You are hungry."); - } + } + HungerState::Starving => { + // Inflict damage from hunger + if entity == *player_entity { + log.append("Your hunger pangs are getting painful!"); } - HungerState::Hungry => { - clock.state = HungerState::Starving; - clock.duration = 200; - if entity == *player_entity { - log.append("You are starving!"); - } - } - HungerState::Starving => { - // Inflict damage from hunger - if entity == *player_entity { - log.append("Your hunger pangs are getting painful!"); - } - - SufferDamage::new_damage(&mut inflict_damage, entity, 1, false); - } + SufferDamage::new_damage(&mut inflict_damage, entity, 1, false); } } } diff --git a/src/main.rs b/src/main.rs index d7725b8..0b175ab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,8 +62,7 @@ const SHOW_MAPGEN_VISUALIZER: bool = false; pub enum RunState { AwaitingInput, PreRun, - PlayerTurn, - MonsterTurn, + Ticking, ShowInventory, ShowDropItem, ShowTargeting { @@ -107,15 +106,18 @@ impl State { } fn run_systems(&mut self) { + let mut mapindex = MapIndexingSystem {}; + mapindex.run_now(&self.ecs); + let mut vis = VisibilitySystem {}; vis.run_now(&self.ecs); + let mut initiative = ai::InitiativeSystem {}; + initiative.run_now(&self.ecs); + let mut mob = ai::MonsterAI {}; mob.run_now(&self.ecs); - let mut mapindex = MapIndexingSystem {}; - mapindex.run_now(&self.ecs); - let mut animal = ai::AnimalAI {}; animal.run_now(&self.ecs); @@ -203,21 +205,15 @@ impl GameState for State { RunState::AwaitingInput => { newrunstate = player_input(self, ctx); } - RunState::PlayerTurn => { + RunState::Ticking => { self.run_systems(); self.ecs.maintain(); - match *self.ecs.fetch::() { - RunState::MagicMapReveal { .. } => { - newrunstate = RunState::MagicMapReveal { row: 0 } - } - _ => newrunstate = RunState::MonsterTurn, - } - } - RunState::MonsterTurn => { - self.run_systems(); - self.ecs.maintain(); - newrunstate = RunState::AwaitingInput; + newrunstate = match *self.ecs.fetch::() { + RunState::AwaitingInput => RunState::AwaitingInput, + RunState::MagicMapReveal { .. } => RunState::MagicMapReveal { row: 0 }, + _ => RunState::Ticking, + }; } RunState::ShowInventory => { let result = gui::show_inventory(self, ctx); @@ -245,7 +241,7 @@ impl GameState for State { ) .expect("failed to add intent to use item"); - newrunstate = RunState::PlayerTurn; + newrunstate = RunState::Ticking; } } } @@ -265,7 +261,7 @@ impl GameState for State { ) .expect("failed to add intent to drop item"); - newrunstate = RunState::PlayerTurn; + newrunstate = RunState::Ticking; } } } @@ -284,7 +280,7 @@ impl GameState for State { ) .expect("Unable to insert intent to remove item"); - newrunstate = RunState::PlayerTurn; + newrunstate = RunState::Ticking; } } } @@ -306,7 +302,7 @@ impl GameState for State { ) .expect("failed to add intent to use item"); - newrunstate = RunState::PlayerTurn; + newrunstate = RunState::Ticking; } } } @@ -361,7 +357,7 @@ impl GameState for State { } if row == map.height - 1 { - newrunstate = RunState::MonsterTurn; + newrunstate = RunState::Ticking; } else { newrunstate = RunState::MagicMapReveal { row: row + 1 }; } diff --git a/src/player.rs b/src/player.rs index 6739730..159a487 100644 --- a/src/player.rs +++ b/src/player.rs @@ -59,7 +59,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState let mut ppos = ecs.write_resource::(); ppos.x = pos.x; ppos.y = pos.y; - result = RunState::PlayerTurn; + result = RunState::Ticking; } else if combat_stats.get(*potential_target).is_some() { wants_to_melee .insert( @@ -70,7 +70,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState ) .expect("Add target failed"); - return RunState::PlayerTurn; + return RunState::Ticking; } if let Some(door) = doors.get_mut(*potential_target) { @@ -100,7 +100,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState result = match map.tiles[destination_idx] { TileType::DownStairs => RunState::NextLevel, TileType::UpStairs => RunState::PreviousLevel, - _ => RunState::PlayerTurn, + _ => RunState::Ticking, } } } @@ -218,7 +218,7 @@ fn skip_turn(ecs: &mut World) -> RunState { pools.hit_points.current = i32::min(pools.hit_points.current + 1, pools.hit_points.max); } - RunState::PlayerTurn + RunState::Ticking } fn use_consumable_hotkey(gs: &mut State, key: i32) -> RunState { @@ -259,10 +259,10 @@ fn use_consumable_hotkey(gs: &mut State, key: i32) -> RunState { ) .expect("Unable to insert intent to use item."); - return RunState::PlayerTurn; + return RunState::Ticking; } - RunState::PlayerTurn + RunState::Ticking } pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState { @@ -366,5 +366,5 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState { }, } - RunState::PlayerTurn + RunState::Ticking }