Complete section 5.14, having refactored basically every system

This commit is contained in:
Timothy Warren 2022-01-11 15:35:59 -05:00
parent 148e448d78
commit eb29e28ec6
12 changed files with 245 additions and 91 deletions

View File

@ -1,5 +1,6 @@
mod adjacent_ai_system; mod adjacent_ai_system;
mod approach_ai_system; mod approach_ai_system;
mod chase_ai_system;
mod default_move_system; mod default_move_system;
mod flee_ai_system; mod flee_ai_system;
mod initiative_system; mod initiative_system;
@ -9,6 +10,7 @@ mod visible_ai_system;
pub use adjacent_ai_system::AdjacentAI; pub use adjacent_ai_system::AdjacentAI;
pub use approach_ai_system::ApproachAI; pub use approach_ai_system::ApproachAI;
pub use chase_ai_system::ChaseAI;
pub use default_move_system::DefaultMoveAI; pub use default_move_system::DefaultMoveAI;
pub use flee_ai_system::FleeAI; pub use flee_ai_system::FleeAI;
pub use initiative_system::InitiativeSystem; pub use initiative_system::InitiativeSystem;

85
src/ai/chase_ai_system.rs Normal file
View File

@ -0,0 +1,85 @@
use std::collections::HashMap;
use ::specs::prelude::*;
use crate::components::{Chasing, EntityMoved, MyTurn, Position, Viewshed};
use crate::Map;
pub struct ChaseAI {}
impl<'a> System<'a> for ChaseAI {
#[allow(clippy::type_complexity)]
type SystemData = (
WriteStorage<'a, MyTurn>,
WriteStorage<'a, Chasing>,
WriteStorage<'a, Position>,
WriteExpect<'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 targets: HashMap<Entity, (i32, i32)> = HashMap::new();
let mut end_chase: Vec<Entity> = Vec::new();
for (entity, _turn, chasing) in (&entities, &turns, &chasing).join() {
if let Some(target_pos) = positions.get(chasing.target) {
targets.insert(entity, (target_pos.x, target_pos.y));
} else {
end_chase.push(entity);
}
}
for done in end_chase.iter() {
chasing.remove(*done);
}
end_chase.clear();
let mut turn_done: Vec<Entity> = Vec::new();
for (entity, mut pos, _chase, mut viewshed, _myturn) in
(&entities, &mut positions, &chasing, &mut viewsheds, &turns).join()
{
turn_done.push(entity);
let target_pos = targets[&entity];
let path = ::rltk::a_star_search(
map.xy_idx(pos.x, pos.y) as i32,
map.xy_idx(target_pos.0, target_pos.1) as i32,
&*map,
);
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;
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;
viewshed.dirty = true;
turn_done.push(entity);
} else {
end_chase.push(entity);
}
}
for done in end_chase.iter() {
chasing.remove(*done);
}
for done in turn_done.iter() {
turns.remove(*done);
}
}
}

View File

@ -2,7 +2,7 @@ use ::rltk::RandomNumberGenerator;
use ::specs::prelude::*; use ::specs::prelude::*;
use crate::components::{EntityMoved, MoveMode, Movement, MyTurn, Position, Viewshed}; use crate::components::{EntityMoved, MoveMode, Movement, MyTurn, Position, Viewshed};
use crate::Map; use crate::{tile_walkable, Map};
pub struct DefaultMoveAI {} pub struct DefaultMoveAI {}
@ -10,7 +10,7 @@ impl<'a> System<'a> for DefaultMoveAI {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
type SystemData = ( type SystemData = (
WriteStorage<'a, MyTurn>, WriteStorage<'a, MyTurn>,
ReadStorage<'a, MoveMode>, WriteStorage<'a, MoveMode>,
WriteStorage<'a, Position>, WriteStorage<'a, Position>,
WriteExpect<'a, Map>, WriteExpect<'a, Map>,
WriteStorage<'a, Viewshed>, WriteStorage<'a, Viewshed>,
@ -22,7 +22,7 @@ impl<'a> System<'a> for DefaultMoveAI {
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
let ( let (
mut turns, mut turns,
move_mode, mut move_mode,
mut positions, mut positions,
mut map, mut map,
mut viewsheds, mut viewsheds,
@ -32,10 +32,10 @@ impl<'a> System<'a> for DefaultMoveAI {
) = data; ) = data;
let mut turn_done: Vec<Entity> = Vec::new(); let mut turn_done: Vec<Entity> = Vec::new();
for (entity, mut pos, mode, mut viewshed, _myturn) in ( for (entity, mut pos, mut mode, mut viewshed, _myturn) in (
&entities, &entities,
&mut positions, &mut positions,
&move_mode, &mut move_mode,
&mut viewsheds, &mut viewsheds,
&turns, &turns,
) )
@ -43,7 +43,7 @@ impl<'a> System<'a> for DefaultMoveAI {
{ {
turn_done.push(entity); turn_done.push(entity);
match mode.mode { match &mut mode.mode {
Movement::Static => {} Movement::Static => {}
Movement::Random => { Movement::Random => {
let mut x = pos.x; let mut x = pos.x;
@ -72,6 +72,46 @@ 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);
if path.len() > 1 {
if !map.blocked[path[1]] {
map.blocked[idx] = false;
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;
viewshed.dirty = true;
path.remove(0); // Remove the first step in the path
}
} else {
// Otherwise we wait a turn to see if the path clears up
mode.mode = Movement::RandomWaypoint { path: None };
}
} else {
let target_x = rng.roll_dice(1, map.width - 2);
let target_y = rng.roll_dice(1, map.height - 2);
let idx = map.xy_idx(target_x, target_y);
if tile_walkable(map.tiles[idx]) {
let path = ::rltk::a_star_search(
map.xy_idx(pos.x, pos.y) as i32,
map.xy_idx(target_x, target_y) as i32,
&*map,
);
if path.success && path.steps.len() > 1 {
mode.mode = Movement::RandomWaypoint {
path: Some(path.steps),
};
}
}
}
}
} }
} }

View File

@ -1,4 +1,4 @@
use ::rltk::RandomNumberGenerator; use ::rltk::{DistanceAlg, Point, RandomNumberGenerator};
use ::specs::prelude::*; use ::specs::prelude::*;
use crate::components::{Attributes, Initiative, MyTurn, Position}; use crate::components::{Attributes, Initiative, MyTurn, Position};
@ -17,6 +17,7 @@ impl<'a> System<'a> for InitiativeSystem {
ReadStorage<'a, Attributes>, ReadStorage<'a, Attributes>,
WriteExpect<'a, RunState>, WriteExpect<'a, RunState>,
ReadExpect<'a, Entity>, ReadExpect<'a, Entity>,
ReadExpect<'a, Point>,
); );
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
@ -29,23 +30,21 @@ impl<'a> System<'a> for InitiativeSystem {
attributes, attributes,
mut runstate, mut runstate,
player, player,
player_pos,
) = data; ) = data;
if *runstate != RunState::Ticking { if *runstate != RunState::Ticking {
return; return;
} // We'll be adding Ticking in a moment }
// Clear any remaining MyTurn we left by mistake // Clear any remaining MyTurn we left by mistake
turns.clear(); turns.clear();
// Roll initiative // Roll initiative
for (entity, initiative, _pos) in (&entities, &mut initiatives, &positions).join() { for (entity, initiative, pos) in (&entities, &mut initiatives, &positions).join() {
initiative.current -= 1; initiative.current -= 1;
if initiative.current < 1 { if initiative.current < 1 {
// It's my turn let mut myturn = true;
turns
.insert(entity, MyTurn {})
.expect("Unable to insert turn");
// Re-roll // Re-roll
initiative.current = 6 + rng.roll_dice(1, 6); initiative.current = 6 + rng.roll_dice(1, 6);
@ -60,6 +59,19 @@ impl<'a> System<'a> for InitiativeSystem {
// if its the player, we want to go to an AwaitingInput state // if its the player, we want to go to an AwaitingInput state
if entity == *player { if entity == *player {
*runstate = RunState::AwaitingInput; *runstate = RunState::AwaitingInput;
} else {
let distance =
DistanceAlg::Pythagoras.distance2d(*player_pos, Point::from(*pos));
if distance > 20.0 {
myturn = false;
}
}
// It's my turn
if myturn {
turns
.insert(entity, MyTurn {})
.expect("Unable to insert turn");
} }
} }
} }

View File

@ -1,6 +1,8 @@
use ::specs::prelude::*; use ::specs::prelude::*;
use crate::components::{Faction, MyTurn, Position, Viewshed, WantsToApproach, WantsToFlee}; use crate::components::{
Chasing, Faction, MyTurn, Position, Viewshed, WantsToApproach, WantsToFlee,
};
use crate::raws::Reaction; use crate::raws::Reaction;
use crate::Map; use crate::Map;
@ -18,6 +20,7 @@ impl<'a> System<'a> for VisibleAI {
Entities<'a>, Entities<'a>,
ReadExpect<'a, Entity>, ReadExpect<'a, Entity>,
ReadStorage<'a, Viewshed>, ReadStorage<'a, Viewshed>,
WriteStorage<'a, Chasing>,
); );
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
@ -30,15 +33,16 @@ impl<'a> System<'a> for VisibleAI {
mut want_flee, mut want_flee,
entities, entities,
player, player,
viewshed, viewsheds,
mut chasing,
) = data; ) = data;
for (entity, _turn, my_faction, pos, viewshed) in for (entity, _turn, my_faction, pos, viewshed) in
(&entities, &turns, &factions, &positions, &viewshed).join() (&entities, &turns, &factions, &positions, &viewsheds).join()
{ {
if entity != *player { if entity != *player {
let my_idx = map.xy_idx(pos.x, pos.y); let my_idx = map.xy_idx(pos.x, pos.y);
let mut reactions: Vec<(usize, Reaction)> = Vec::new(); let mut reactions: Vec<(usize, Reaction, Entity)> = Vec::new();
let mut flee: Vec<usize> = Vec::new(); let mut flee: Vec<usize> = Vec::new();
for visible_tile in viewshed.visible_tiles.iter() { for visible_tile in viewshed.visible_tiles.iter() {
let idx = map.xy_idx(visible_tile.x, visible_tile.y); let idx = map.xy_idx(visible_tile.x, visible_tile.y);
@ -59,6 +63,9 @@ impl<'a> System<'a> for VisibleAI {
}, },
) )
.expect("Unable to insert intent to approach"); .expect("Unable to insert intent to approach");
chasing
.insert(entity, Chasing { target: reaction.2 })
.expect("Unable to insert intent to chase");
done = true; done = true;
} }
Reaction::Flee => { Reaction::Flee => {
@ -83,7 +90,7 @@ fn evaluate(
map: &Map, map: &Map,
factions: &ReadStorage<Faction>, factions: &ReadStorage<Faction>,
my_faction: &str, my_faction: &str,
reactions: &mut Vec<(usize, Reaction)>, reactions: &mut Vec<(usize, Reaction, Entity)>,
) { ) {
for other_entity in map.tile_content[idx].iter() { for other_entity in map.tile_content[idx].iter() {
if let Some(faction) = factions.get(*other_entity) { if let Some(faction) = factions.get(*other_entity) {
@ -94,6 +101,7 @@ fn evaluate(
&faction.name, &faction.name,
&crate::raws::RAWS.lock().unwrap(), &crate::raws::RAWS.lock().unwrap(),
), ),
*other_entity,
)); ));
} }
} }

View File

@ -324,6 +324,11 @@ pub struct MoveMode {
pub mode: Movement, pub mode: Movement,
} }
#[derive(Component, Debug, ConvertSaveload, Clone)]
pub struct Chasing {
pub target: Entity,
}
// Serialization helper code. We need to implement ConvertSaveLoad for each type that contains an // Serialization helper code. We need to implement ConvertSaveLoad for each type that contains an
// Entity. // Entity.
@ -337,5 +342,5 @@ pub struct SerializationHelper {
#[derive(Component, Serialize, Deserialize, Clone)] #[derive(Component, Serialize, Deserialize, Clone)]
pub struct DMSerializationHelper { pub struct DMSerializationHelper {
pub map: super::map::MasterDungeonMap, pub map: crate::map::MasterDungeonMap,
} }

View File

@ -26,6 +26,7 @@ pub enum HungerState {
pub enum Movement { pub enum Movement {
Static, Static,
Random, Random,
RandomWaypoint { path: Option<Vec<usize>> },
} }
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]

View File

@ -5,9 +5,6 @@ use ::specs_derive::*;
#[derive(Component, Debug, Serialize, Deserialize, Clone)] #[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Player {} pub struct Player {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Monster {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)] #[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Item {} pub struct Item {}
@ -38,17 +35,5 @@ pub struct SingleActivation {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)] #[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct BlocksVisibility {} pub struct BlocksVisibility {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Bystander {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Vendor {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Carnivore {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Herbivore {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)] #[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct MyTurn {} pub struct MyTurn {}

View File

@ -133,6 +133,9 @@ impl State {
let mut flee = ai::FleeAI {}; let mut flee = ai::FleeAI {};
flee.run_now(&self.ecs); flee.run_now(&self.ecs);
let mut chase = ai::ChaseAI {};
chase.run_now(&self.ecs);
let mut defaultmove = ai::DefaultMoveAI {}; let mut defaultmove = ai::DefaultMoveAI {};
defaultmove.run_now(&self.ecs); defaultmove.run_now(&self.ecs);
@ -218,14 +221,16 @@ impl GameState for State {
newrunstate = player_input(self, ctx); newrunstate = player_input(self, ctx);
} }
RunState::Ticking => { RunState::Ticking => {
self.run_systems(); while newrunstate == RunState::Ticking {
self.ecs.maintain(); self.run_systems();
self.ecs.maintain();
newrunstate = match *self.ecs.fetch::<RunState>() { newrunstate = match *self.ecs.fetch::<RunState>() {
RunState::AwaitingInput => RunState::AwaitingInput, RunState::AwaitingInput => RunState::AwaitingInput,
RunState::MagicMapReveal { .. } => RunState::MagicMapReveal { row: 0 }, RunState::MagicMapReveal { .. } => RunState::MagicMapReveal { row: 0 },
_ => RunState::Ticking, _ => RunState::Ticking,
}; };
}
} }
RunState::ShowInventory => { RunState::ShowInventory => {
let result = gui::show_inventory(self, ctx); let result = gui::show_inventory(self, ctx);
@ -460,8 +465,7 @@ fn main() -> ::rltk::BError {
Attributes, Attributes,
BlocksTile, BlocksTile,
BlocksVisibility, BlocksVisibility,
Bystander, Chasing,
Carnivore,
Confusion, Confusion,
Consumable, Consumable,
Door, Door,
@ -471,7 +475,6 @@ fn main() -> ::rltk::BError {
Equippable, Equippable,
Equipped, Equipped,
Faction, Faction,
Herbivore,
Hidden, Hidden,
HungerClock, HungerClock,
InBackpack, InBackpack,
@ -482,7 +485,6 @@ fn main() -> ::rltk::BError {
LootTable, LootTable,
MagicMapper, MagicMapper,
MeleeWeapon, MeleeWeapon,
Monster,
MoveMode, MoveMode,
MyTurn, MyTurn,
Name, Name,
@ -502,7 +504,6 @@ fn main() -> ::rltk::BError {
SingleActivation, SingleActivation,
Skills, Skills,
SufferDamage, SufferDamage,
Vendor,
Viewshed, Viewshed,
WantsToApproach, WantsToApproach,
WantsToDropItem, WantsToDropItem,

View File

@ -4,18 +4,19 @@ use ::rltk::{Point, Rltk, VirtualKeyCode};
use ::specs::prelude::*; use ::specs::prelude::*;
use crate::components::{ use crate::components::{
BlocksTile, BlocksVisibility, Door, EntityMoved, HungerClock, HungerState, Item, Monster, Attributes, BlocksTile, BlocksVisibility, Door, EntityMoved, Faction, HungerClock, HungerState,
Player, Pools, Position, Renderable, Vendor, Viewshed, WantsToMelee, WantsToPickupItem, Item, Player, Pools, Position, Renderable, Viewshed, WantsToMelee, WantsToPickupItem,
}; };
use crate::game_log::GameLog; use crate::game_log::GameLog;
use crate::{Bystander, Map, RunState, State, TileType}; use crate::raws::Reaction;
use crate::{Map, RunState, State, TileType};
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState { pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState {
let mut positions = ecs.write_storage::<Position>(); let mut positions = ecs.write_storage::<Position>();
let players = ecs.read_storage::<Player>(); let players = ecs.read_storage::<Player>();
let mut viewsheds = ecs.write_storage::<Viewshed>(); let mut viewsheds = ecs.write_storage::<Viewshed>();
let entities = ecs.entities(); let entities = ecs.entities();
let combat_stats = ecs.read_storage::<Pools>(); let combat_stats = ecs.read_storage::<Attributes>();
let map = ecs.fetch::<Map>(); let map = ecs.fetch::<Map>();
let mut wants_to_melee = ecs.write_storage::<WantsToMelee>(); let mut wants_to_melee = ecs.write_storage::<WantsToMelee>();
let mut entity_moved = ecs.write_storage::<EntityMoved>(); let mut entity_moved = ecs.write_storage::<EntityMoved>();
@ -23,8 +24,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState
let mut blocks_visibility = ecs.write_storage::<BlocksVisibility>(); let mut blocks_visibility = ecs.write_storage::<BlocksVisibility>();
let mut blocks_movement = ecs.write_storage::<BlocksTile>(); let mut blocks_movement = ecs.write_storage::<BlocksTile>();
let mut renderables = ecs.write_storage::<Renderable>(); let mut renderables = ecs.write_storage::<Renderable>();
let bystanders = ecs.read_storage::<Bystander>(); let factions = ecs.read_storage::<Faction>();
let vendors = ecs.read_storage::<Vendor>();
let mut result = RunState::AwaitingInput; let mut result = RunState::AwaitingInput;
let mut swap_entities: Vec<(Entity, i32, i32)> = Vec::new(); let mut swap_entities: Vec<(Entity, i32, i32)> = Vec::new();
@ -42,9 +42,20 @@ 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); let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y);
for potential_target in map.tile_content[destination_idx].iter() { for potential_target in map.tile_content[destination_idx].iter() {
let bystander = bystanders.get(*potential_target); let mut hostile = true;
let vendor = vendors.get(*potential_target); if combat_stats.get(*potential_target).is_some() {
if bystander.is_some() || vendor.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;
}
}
}
if !hostile {
// Note that we want to move the bystander // Note that we want to move the bystander
swap_entities.push((*potential_target, pos.x, pos.y)); swap_entities.push((*potential_target, pos.x, pos.y));
@ -53,34 +64,36 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState
pos.y = min(map.height - 1, max(0, pos.y + delta_y)); pos.y = min(map.height - 1, max(0, pos.y + delta_y));
entity_moved entity_moved
.insert(entity, EntityMoved {}) .insert(entity, EntityMoved {})
.expect("Unable to insert moved entity marker"); .expect("Unable to insert marker");
viewshed.dirty = true; viewshed.dirty = true;
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::Ticking; } else {
} else if combat_stats.get(*potential_target).is_some() { let target = combat_stats.get(*potential_target);
wants_to_melee if let Some(_target) = target {
.insert( wants_to_melee
entity, .insert(
WantsToMelee { entity,
target: *potential_target, WantsToMelee {
}, target: *potential_target,
) },
.expect("Add target failed"); )
.expect("Add target failed");
return RunState::Ticking; return RunState::Ticking;
}
} }
let door = doors.get_mut(*potential_target);
if let Some(door) = doors.get_mut(*potential_target) { if let Some(door) = door {
door.open = true; door.open = true;
blocks_visibility.remove(*potential_target); blocks_visibility.remove(*potential_target);
blocks_movement.remove(*potential_target); blocks_movement.remove(*potential_target);
let glyph = renderables.get_mut(*potential_target).unwrap(); let glyph = renderables.get_mut(*potential_target).unwrap();
glyph.glyph = rltk::to_cp437('/'); glyph.glyph = rltk::to_cp437('/');
viewshed.dirty = true; viewshed.dirty = true;
result = RunState::Ticking;
} }
} }
@ -95,18 +108,18 @@ 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::Ticking;
// Change levels by running onto a set of stairs match map.tiles[destination_idx] {
result = match map.tiles[destination_idx] { TileType::DownStairs => result = RunState::NextLevel,
TileType::DownStairs => RunState::NextLevel, TileType::UpStairs => result = RunState::PreviousLevel,
TileType::UpStairs => RunState::PreviousLevel, _ => {}
_ => RunState::Ticking,
} }
} }
} }
for m in swap_entities.iter() { for m in swap_entities.iter() {
if let Some(their_pos) = positions.get_mut(m.0) { let their_pos = positions.get_mut(m.0);
if let Some(their_pos) = their_pos {
their_pos.x = m.1; their_pos.x = m.1;
their_pos.y = m.2; their_pos.y = m.2;
} }
@ -184,7 +197,7 @@ fn get_item(ecs: &mut World) {
fn skip_turn(ecs: &mut World) -> RunState { fn skip_turn(ecs: &mut World) -> RunState {
let player_entity = ecs.fetch::<Entity>(); let player_entity = ecs.fetch::<Entity>();
let viewshed_components = ecs.read_storage::<Viewshed>(); let viewshed_components = ecs.read_storage::<Viewshed>();
let monsters = ecs.read_storage::<Monster>(); let factions = ecs.read_storage::<Faction>();
let worldmap_resource = ecs.fetch::<Map>(); let worldmap_resource = ecs.fetch::<Map>();
@ -193,10 +206,17 @@ fn skip_turn(ecs: &mut World) -> RunState {
for tile in viewshed.visible_tiles.iter() { for tile in viewshed.visible_tiles.iter() {
let idx = worldmap_resource.xy_idx(tile.x, tile.y); let idx = worldmap_resource.xy_idx(tile.x, tile.y);
for entity_id in worldmap_resource.tile_content[idx].iter() { for entity_id in worldmap_resource.tile_content[idx].iter() {
match monsters.get(*entity_id) { match factions.get(*entity_id) {
None => {} None => {}
Some(_) => { Some(faction) => {
can_heal = false; let reaction = crate::raws::faction_reaction(
&faction.name,
"Player",
&crate::raws::RAWS.lock().unwrap(),
);
if reaction == Reaction::Attack {
can_heal = false;
}
} }
} }
} }

View File

@ -342,6 +342,9 @@ pub fn spawn_named_mob(
"random" => eb.with(MoveMode { "random" => eb.with(MoveMode {
mode: Movement::Random, mode: Movement::Random,
}), }),
"random_waypoint" => eb.with(MoveMode {
mode: Movement::RandomWaypoint { path: None },
}),
_ => eb.with(MoveMode { _ => eb.with(MoveMode {
mode: Movement::Static, mode: Movement::Static,
}), }),

View File

@ -64,8 +64,7 @@ pub fn save_game(ecs: &mut World) {
Attributes, Attributes,
BlocksTile, BlocksTile,
BlocksVisibility, BlocksVisibility,
Bystander, Chasing,
Carnivore,
Confusion, Confusion,
Consumable, Consumable,
Door, Door,
@ -75,7 +74,6 @@ pub fn save_game(ecs: &mut World) {
Equippable, Equippable,
Equipped, Equipped,
Faction, Faction,
Herbivore,
Hidden, Hidden,
HungerClock, HungerClock,
InBackpack, InBackpack,
@ -86,7 +84,6 @@ pub fn save_game(ecs: &mut World) {
LootTable, LootTable,
MagicMapper, MagicMapper,
MeleeWeapon, MeleeWeapon,
Monster,
MoveMode, MoveMode,
MyTurn, MyTurn,
Name, Name,
@ -105,7 +102,6 @@ pub fn save_game(ecs: &mut World) {
SingleActivation, SingleActivation,
Skills, Skills,
SufferDamage, SufferDamage,
Vendor,
Viewshed, Viewshed,
WantsToApproach, WantsToApproach,
WantsToDropItem, WantsToDropItem,
@ -175,8 +171,7 @@ pub fn load_game(ecs: &mut World) {
Attributes, Attributes,
BlocksTile, BlocksTile,
BlocksVisibility, BlocksVisibility,
Bystander, Chasing,
Carnivore,
Confusion, Confusion,
Consumable, Consumable,
Door, Door,
@ -186,7 +181,6 @@ pub fn load_game(ecs: &mut World) {
Equippable, Equippable,
Equipped, Equipped,
Faction, Faction,
Herbivore,
Hidden, Hidden,
HungerClock, HungerClock,
InBackpack, InBackpack,
@ -197,7 +191,6 @@ pub fn load_game(ecs: &mut World) {
LootTable, LootTable,
MagicMapper, MagicMapper,
MeleeWeapon, MeleeWeapon,
Monster,
MoveMode, MoveMode,
MyTurn, MyTurn,
Name, Name,
@ -216,7 +209,6 @@ pub fn load_game(ecs: &mut World) {
SingleActivation, SingleActivation,
Skills, Skills,
SufferDamage, SufferDamage,
Vendor,
Viewshed, Viewshed,
WantsToApproach, WantsToApproach,
WantsToDropItem, WantsToDropItem,