Add initiative system, and refactor systems to use new runstate

This commit is contained in:
Timothy Warren 2022-01-11 10:01:37 -05:00
parent 3def036868
commit f0ac291e6a
8 changed files with 151 additions and 102 deletions

View File

@ -1,7 +1,9 @@
mod animal_ai_system; mod animal_ai_system;
mod bystander_ai_system; mod bystander_ai_system;
mod initiative_system;
mod monster_ai_system; mod monster_ai_system;
pub use animal_ai_system::AnimalAI; pub use animal_ai_system::AnimalAI;
pub use bystander_ai_system::BystanderAI; pub use bystander_ai_system::BystanderAI;
pub use initiative_system::InitiativeSystem;
pub use monster_ai_system::MonsterAI; pub use monster_ai_system::MonsterAI;

View File

@ -2,7 +2,7 @@ use ::rltk::{DijkstraMap, DistanceAlg, Point};
use ::specs::prelude::*; use ::specs::prelude::*;
use crate::components::{ use crate::components::{
Carnivore, EntityMoved, Herbivore, Item, Position, Viewshed, WantsToMelee, Carnivore, EntityMoved, Herbivore, Item, MyTurn, Position, Viewshed, WantsToMelee,
}; };
use crate::{Map, RunState}; use crate::{Map, RunState};
@ -22,6 +22,7 @@ impl<'a> System<'a> for AnimalAI {
WriteStorage<'a, WantsToMelee>, WriteStorage<'a, WantsToMelee>,
WriteStorage<'a, EntityMoved>, WriteStorage<'a, EntityMoved>,
WriteStorage<'a, Position>, WriteStorage<'a, Position>,
ReadStorage<'a, MyTurn>,
); );
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
@ -37,15 +38,12 @@ impl<'a> System<'a> for AnimalAI {
mut wants_to_melee, mut wants_to_melee,
mut entity_moved, mut entity_moved,
mut position, mut position,
turns,
) = data; ) = data;
if *runstate != RunState::MonsterTurn {
return;
}
// Herbivores run away a lot // Herbivores run away a lot
for (entity, mut viewshed, _herbivore, mut pos) in for (entity, mut viewshed, _herbivore, mut pos, _turn) in
(&entities, &mut viewshed, &herbivore, &mut position).join() (&entities, &mut viewshed, &herbivore, &mut position, &turns).join()
{ {
let mut run_away_from: Vec<usize> = Vec::new(); let mut run_away_from: Vec<usize> = Vec::new();
for other_tile in viewshed.visible_tiles.iter() { for other_tile in viewshed.visible_tiles.iter() {
@ -85,8 +83,8 @@ impl<'a> System<'a> for AnimalAI {
} }
// Carnivores just want to eat everything // Carnivores just want to eat everything
for (entity, mut viewshed, _carnivore, mut pos) in for (entity, mut viewshed, _carnivore, mut pos, _turn) in
(&entities, &mut viewshed, &carnivore, &mut position).join() (&entities, &mut viewshed, &carnivore, &mut position, &turns).join()
{ {
let mut run_towards: Vec<usize> = Vec::new(); let mut run_towards: Vec<usize> = Vec::new();
let mut attacked = false; let mut attacked = false;

View File

@ -1,7 +1,7 @@
use ::rltk::{Point, RandomNumberGenerator}; use ::rltk::{Point, RandomNumberGenerator};
use specs::prelude::*; 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::game_log::GameLog;
use crate::{Map, RunState}; use crate::{Map, RunState};
@ -22,6 +22,7 @@ impl<'a> System<'a> for BystanderAI {
WriteExpect<'a, GameLog>, WriteExpect<'a, GameLog>,
WriteStorage<'a, Quips>, WriteStorage<'a, Quips>,
ReadStorage<'a, Name>, ReadStorage<'a, Name>,
ReadStorage<'a, MyTurn>,
); );
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
@ -38,14 +39,11 @@ impl<'a> System<'a> for BystanderAI {
mut gamelog, mut gamelog,
mut quips, mut quips,
names, names,
turns,
) = data; ) = data;
if *runstate != RunState::MonsterTurn { for (entity, mut viewshed, _bystander, mut pos, _turn) in
return; (&entities, &mut viewshed, &bystander, &mut position, &turns).join()
}
for (entity, mut viewshed, _bystander, mut pos) in
(&entities, &mut viewshed, &bystander, &mut position).join()
{ {
// Possibly quip // Possibly quip
if let Some(quip) = quips.get_mut(entity) { if let Some(quip) = quips.get_mut(entity) {

View File

@ -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;
}
}
}
}
}

View File

@ -1,7 +1,7 @@
use ::rltk::{Point, RGB}; use ::rltk::{Point, RGB};
use ::specs::prelude::*; 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::particle_system::ParticleBuilder;
use crate::{EntityMoved, Map, RunState}; use crate::{EntityMoved, Map, RunState};
@ -22,6 +22,7 @@ impl<'a> System<'a> for MonsterAI {
WriteStorage<'a, Confusion>, WriteStorage<'a, Confusion>,
WriteExpect<'a, ParticleBuilder>, WriteExpect<'a, ParticleBuilder>,
WriteStorage<'a, EntityMoved>, WriteStorage<'a, EntityMoved>,
ReadStorage<'a, MyTurn>,
); );
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
@ -38,14 +39,11 @@ impl<'a> System<'a> for MonsterAI {
mut confused, mut confused,
mut particle_builder, mut particle_builder,
mut entity_moved, mut entity_moved,
turns,
) = data; ) = data;
if *runstate != RunState::MonsterTurn { for (entity, mut viewshed, _monster, mut pos, _turn) in
return; (&entities, &mut viewshed, &monster, &mut position, &turns).join()
}
for (entity, mut viewshed, _monster, mut pos) in
(&entities, &mut viewshed, &monster, &mut position).join()
{ {
let mut can_act = true; let mut can_act = true;

View File

@ -1,6 +1,6 @@
use ::specs::prelude::*; use ::specs::prelude::*;
use crate::components::{HungerClock, HungerState, SufferDamage}; use crate::components::{HungerClock, HungerState, MyTurn, SufferDamage};
use crate::game_log::GameLog; use crate::game_log::GameLog;
use crate::RunState; use crate::RunState;
@ -15,30 +15,21 @@ impl<'a> System<'a> for HungerSystem {
ReadExpect<'a, RunState>, ReadExpect<'a, RunState>,
WriteStorage<'a, SufferDamage>, WriteStorage<'a, SufferDamage>,
WriteExpect<'a, GameLog>, WriteExpect<'a, GameLog>,
ReadStorage<'a, MyTurn>,
); );
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
let (entities, mut hunger_clock, player_entity, runstate, mut inflict_damage, mut log) = let (
data; entities,
mut hunger_clock,
player_entity,
runstate,
mut inflict_damage,
mut log,
turns,
) = data;
for (entity, mut clock) in (&entities, &mut hunger_clock).join() { for (entity, mut clock, _myturn) in (&entities, &mut hunger_clock, &turns).join() {
let mut proceed = false;
match *runstate {
RunState::PlayerTurn => {
if entity == *player_entity {
proceed = true;
}
}
RunState::MonsterTurn => {
if entity != *player_entity {
proceed = true;
}
}
_ => proceed = false,
}
if proceed {
clock.duration -= 1; clock.duration -= 1;
if clock.duration < 1 { if clock.duration < 1 {
@ -80,4 +71,3 @@ impl<'a> System<'a> for HungerSystem {
} }
} }
} }
}

View File

@ -62,8 +62,7 @@ const SHOW_MAPGEN_VISUALIZER: bool = false;
pub enum RunState { pub enum RunState {
AwaitingInput, AwaitingInput,
PreRun, PreRun,
PlayerTurn, Ticking,
MonsterTurn,
ShowInventory, ShowInventory,
ShowDropItem, ShowDropItem,
ShowTargeting { ShowTargeting {
@ -107,15 +106,18 @@ impl State {
} }
fn run_systems(&mut self) { fn run_systems(&mut self) {
let mut mapindex = MapIndexingSystem {};
mapindex.run_now(&self.ecs);
let mut vis = VisibilitySystem {}; let mut vis = VisibilitySystem {};
vis.run_now(&self.ecs); vis.run_now(&self.ecs);
let mut initiative = ai::InitiativeSystem {};
initiative.run_now(&self.ecs);
let mut mob = ai::MonsterAI {}; let mut mob = ai::MonsterAI {};
mob.run_now(&self.ecs); mob.run_now(&self.ecs);
let mut mapindex = MapIndexingSystem {};
mapindex.run_now(&self.ecs);
let mut animal = ai::AnimalAI {}; let mut animal = ai::AnimalAI {};
animal.run_now(&self.ecs); animal.run_now(&self.ecs);
@ -203,21 +205,15 @@ impl GameState for State {
RunState::AwaitingInput => { RunState::AwaitingInput => {
newrunstate = player_input(self, ctx); newrunstate = player_input(self, ctx);
} }
RunState::PlayerTurn => { RunState::Ticking => {
self.run_systems(); self.run_systems();
self.ecs.maintain(); self.ecs.maintain();
match *self.ecs.fetch::<RunState>() { newrunstate = match *self.ecs.fetch::<RunState>() {
RunState::MagicMapReveal { .. } => { RunState::AwaitingInput => RunState::AwaitingInput,
newrunstate = RunState::MagicMapReveal { row: 0 } RunState::MagicMapReveal { .. } => RunState::MagicMapReveal { row: 0 },
} _ => RunState::Ticking,
_ => newrunstate = RunState::MonsterTurn, };
}
}
RunState::MonsterTurn => {
self.run_systems();
self.ecs.maintain();
newrunstate = RunState::AwaitingInput;
} }
RunState::ShowInventory => { RunState::ShowInventory => {
let result = gui::show_inventory(self, ctx); let result = gui::show_inventory(self, ctx);
@ -245,7 +241,7 @@ impl GameState for State {
) )
.expect("failed to add intent to use item"); .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"); .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"); .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"); .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 { if row == map.height - 1 {
newrunstate = RunState::MonsterTurn; newrunstate = RunState::Ticking;
} else { } else {
newrunstate = RunState::MagicMapReveal { row: row + 1 }; newrunstate = RunState::MagicMapReveal { row: row + 1 };
} }

View File

@ -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::<Point>(); let mut ppos = ecs.write_resource::<Point>();
ppos.x = pos.x; ppos.x = pos.x;
ppos.y = pos.y; ppos.y = pos.y;
result = RunState::PlayerTurn; result = RunState::Ticking;
} else if combat_stats.get(*potential_target).is_some() { } else if combat_stats.get(*potential_target).is_some() {
wants_to_melee wants_to_melee
.insert( .insert(
@ -70,7 +70,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState
) )
.expect("Add target failed"); .expect("Add target failed");
return RunState::PlayerTurn; return RunState::Ticking;
} }
if let Some(door) = doors.get_mut(*potential_target) { 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] { result = match map.tiles[destination_idx] {
TileType::DownStairs => RunState::NextLevel, TileType::DownStairs => RunState::NextLevel,
TileType::UpStairs => RunState::PreviousLevel, 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); 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 { 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."); .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 { 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
} }