ChaseAI{}, FleeAI{}, removal of monster/bystander tags
This commit is contained in:
parent
91607fb9ce
commit
198486df1d
13 changed files with 234 additions and 187 deletions
|
|
@ -9,7 +9,7 @@
|
|||
},
|
||||
{
|
||||
"id": "neutral",
|
||||
"responses": { "default": "ignore" }
|
||||
"responses": { "default": "ignore", "hostile": "flee", "mindless": "flee", "carnivore": "flee" }
|
||||
},
|
||||
{
|
||||
"id": "hostile",
|
||||
|
|
@ -21,6 +21,6 @@
|
|||
},
|
||||
{
|
||||
"id": "carnivore",
|
||||
"responses": { "default": "ignore", "herbivores": "attack", "player": "attack" }
|
||||
"responses": { "default": "ignore", "herbivores": "attack", "player": "attack", "neutral": "attack" }
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
use crate::{Bystander, EntityMoved, Map, Position, TakingTurn, Viewshed};
|
||||
use specs::prelude::*;
|
||||
|
||||
pub struct BystanderAI {}
|
||||
|
||||
impl<'a> System<'a> for BystanderAI {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
WriteExpect<'a, Map>,
|
||||
Entities<'a>,
|
||||
WriteStorage<'a, Viewshed>,
|
||||
ReadStorage<'a, Bystander>,
|
||||
WriteStorage<'a, Position>,
|
||||
WriteStorage<'a, EntityMoved>,
|
||||
WriteExpect<'a, rltk::RandomNumberGenerator>,
|
||||
ReadStorage<'a, TakingTurn>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
let (mut map, entities, mut viewshed, bystander, mut position, mut entity_moved, mut rng, turns) = data;
|
||||
|
||||
for (entity, mut viewshed, _bystander, mut pos, _turn) in
|
||||
(&entities, &mut viewshed, &bystander, &mut position, &turns).join()
|
||||
{
|
||||
if try_move_randomly(&mut pos, &mut rng, &mut map, &mut viewshed) {
|
||||
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_move_randomly(
|
||||
pos: &mut Position,
|
||||
rng: &mut rltk::RandomNumberGenerator,
|
||||
map: &mut Map,
|
||||
viewshed: &mut Viewshed,
|
||||
) -> bool {
|
||||
// Try to move randomly
|
||||
let mut x = pos.x;
|
||||
let mut y = pos.y;
|
||||
let move_roll = rng.roll_dice(1, 8);
|
||||
match move_roll {
|
||||
1 => x -= 1,
|
||||
2 => x += 1,
|
||||
3 => y -= 1,
|
||||
4 => y += 1,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
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] {
|
||||
let idx = map.xy_idx(pos.x, pos.y);
|
||||
map.blocked[idx] = false;
|
||||
pos.x = x;
|
||||
pos.y = y;
|
||||
map.blocked[dest_idx] = true;
|
||||
viewshed.dirty = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
85
src/ai/chase_ai_system.rs
Normal file
85
src/ai/chase_ai_system.rs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
use crate::{Chasing, EntityMoved, Map, Position, TakingTurn, Telepath, Viewshed};
|
||||
use rltk::prelude::*;
|
||||
use specs::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// If the target is beyond this distance, they're no longer being detected,
|
||||
// so stop following them. This is essentially a combined value of the sound
|
||||
// the target might be making, noise, light, etc., anything they could do to
|
||||
// be detected. As those constituent systems are developed, this value should
|
||||
// be changed to being a result of some calculations between them.
|
||||
const MAX_CHASE_DISTANCE: usize = 15;
|
||||
pub struct ChaseAI {}
|
||||
|
||||
impl<'a> System<'a> for ChaseAI {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
WriteStorage<'a, TakingTurn>,
|
||||
WriteStorage<'a, Chasing>,
|
||||
WriteStorage<'a, Position>,
|
||||
WriteExpect<'a, Map>,
|
||||
WriteStorage<'a, Viewshed>,
|
||||
WriteStorage<'a, Telepath>,
|
||||
WriteStorage<'a, EntityMoved>,
|
||||
Entities<'a>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
let (mut turns, mut chasing, mut positions, mut map, mut viewsheds, mut telepaths, mut entity_moved, entities) =
|
||||
data;
|
||||
let mut targets: HashMap<Entity, (i32, i32)> = HashMap::new();
|
||||
let mut end_chase: Vec<Entity> = Vec::new();
|
||||
// For every chasing entity with a turn, look for a valid target position,
|
||||
// and if found, store that position in a temporary HashMap. This gets around
|
||||
// needing to read Position twice - that would cause borrowchecker issues.
|
||||
// If there's no valid target found, remove the chasing component.
|
||||
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();
|
||||
// Iterate over everyone who is *still* chasing, and path to the target
|
||||
// stored in the HashMap. If successful, follow the path. If not, remove
|
||||
// the chasing component.
|
||||
let mut turn_done: Vec<Entity> = Vec::new();
|
||||
for (entity, _turn, mut pos, _chase, mut viewshed) in
|
||||
(&entities, &turns, &mut positions, &chasing, &mut viewsheds).join()
|
||||
{
|
||||
turn_done.push(entity);
|
||||
let target_pos = targets[&entity];
|
||||
let path = a_star_search(
|
||||
map.xy_idx(pos.x, pos.y) as i32,
|
||||
map.xy_idx(target_pos.0, target_pos.1) as i32,
|
||||
&mut *map,
|
||||
);
|
||||
if path.success && path.steps.len() > 1 && path.steps.len() < MAX_CHASE_DISTANCE {
|
||||
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("Failed to insert EntityMoved");
|
||||
idx = map.xy_idx(pos.x, pos.y);
|
||||
map.blocked[idx] = true;
|
||||
viewshed.dirty = true;
|
||||
if let Some(is_telepath) = telepaths.get_mut(entity) {
|
||||
is_telepath.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,10 +20,23 @@ impl<'a> System<'a> for EnergySystem {
|
|||
WriteExpect<'a, RunState>,
|
||||
ReadExpect<'a, Entity>,
|
||||
ReadStorage<'a, Name>,
|
||||
ReadExpect<'a, Point>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
let (clock, mut energies, burdens, positions, mut turns, entities, mut rng, mut runstate, player, names) = data;
|
||||
let (
|
||||
clock,
|
||||
mut energies,
|
||||
burdens,
|
||||
positions,
|
||||
mut turns,
|
||||
entities,
|
||||
mut rng,
|
||||
mut runstate,
|
||||
player,
|
||||
names,
|
||||
player_pos,
|
||||
) = data;
|
||||
// If not ticking, do nothing.
|
||||
if *runstate != RunState::Ticking {
|
||||
return;
|
||||
|
|
@ -44,7 +57,7 @@ impl<'a> System<'a> for EnergySystem {
|
|||
}
|
||||
}
|
||||
// EVERYTHING ELSE
|
||||
for (entity, energy, _pos) in (&entities, &mut energies, &positions).join() {
|
||||
for (entity, energy, pos) in (&entities, &mut energies, &positions).join() {
|
||||
let burden_modifier = if let Some(burden) = burdens.get(entity) {
|
||||
match burden.level {
|
||||
BurdenLevel::Burdened => 0.75,
|
||||
|
|
@ -76,17 +89,25 @@ impl<'a> System<'a> for EnergySystem {
|
|||
// has enough energy, they take a turn and decrement their energy
|
||||
// by TURN_COST. If the current entity is the player, await input.
|
||||
if energy.current >= TURN_COST {
|
||||
turns.insert(entity, TakingTurn {}).expect("Unable to insert turn.");
|
||||
energy.current -= TURN_COST;
|
||||
if LOG_TICKS {
|
||||
let name = if let Some(name) = names.get(entity) { &name.name } else { "Unknown entity" };
|
||||
console::log(format!(
|
||||
"ENERGY SYSTEM: {} granted a turn. [leftover energy: {}].",
|
||||
name, energy.current
|
||||
));
|
||||
}
|
||||
let mut my_turn = true;
|
||||
if entity == *player {
|
||||
*runstate = RunState::AwaitingInput;
|
||||
} else {
|
||||
let distance = rltk::DistanceAlg::Pythagoras.distance2d(*player_pos, Point::new(pos.x, pos.y));
|
||||
if distance > 20.0 {
|
||||
my_turn = false;
|
||||
}
|
||||
}
|
||||
if my_turn {
|
||||
turns.insert(entity, TakingTurn {}).expect("Unable to insert turn.");
|
||||
energy.current -= TURN_COST;
|
||||
if LOG_TICKS {
|
||||
let name = if let Some(name) = names.get(entity) { &name.name } else { "Unknown entity" };
|
||||
console::log(format!(
|
||||
"ENERGY SYSTEM: {} granted a turn. [leftover energy: {}].",
|
||||
name, energy.current
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
59
src/ai/flee_ai_system.rs
Normal file
59
src/ai/flee_ai_system.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use crate::{EntityMoved, Map, Position, TakingTurn, Telepath, Viewshed, WantsToFlee};
|
||||
use rltk::prelude::*;
|
||||
use specs::prelude::*;
|
||||
|
||||
pub struct FleeAI {}
|
||||
|
||||
impl<'a> System<'a> for FleeAI {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
WriteStorage<'a, TakingTurn>,
|
||||
WriteStorage<'a, WantsToFlee>,
|
||||
WriteStorage<'a, Position>,
|
||||
WriteExpect<'a, Map>,
|
||||
WriteStorage<'a, Viewshed>,
|
||||
WriteStorage<'a, Telepath>,
|
||||
WriteStorage<'a, EntityMoved>,
|
||||
Entities<'a>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
let (
|
||||
mut turns,
|
||||
mut wants_to_flee,
|
||||
mut positions,
|
||||
mut map,
|
||||
mut viewsheds,
|
||||
mut telepaths,
|
||||
mut entity_moved,
|
||||
entities,
|
||||
) = data;
|
||||
let mut turn_done: Vec<Entity> = Vec::new();
|
||||
for (entity, _turn, mut pos, fleeing, mut viewshed) in
|
||||
(&entities, &turns, &mut positions, &wants_to_flee, &mut viewsheds).join()
|
||||
{
|
||||
turn_done.push(entity);
|
||||
let my_idx = map.xy_idx(pos.x, pos.y);
|
||||
map.populate_blocked();
|
||||
let flee_map = DijkstraMap::new(map.width as usize, map.height as usize, &fleeing.indices, &*map, 100.0);
|
||||
let flee_target = DijkstraMap::find_highest_exit(&flee_map, my_idx, &*map);
|
||||
if let Some(flee_target) = flee_target {
|
||||
if !map.blocked[flee_target as usize] {
|
||||
map.blocked[my_idx] = false;
|
||||
map.blocked[flee_target as usize] = true;
|
||||
viewshed.dirty = true;
|
||||
if let Some(is_telepath) = telepaths.get_mut(entity) {
|
||||
is_telepath.dirty = true;
|
||||
}
|
||||
pos.x = flee_target as i32 % map.width;
|
||||
pos.y = flee_target as i32 / map.width;
|
||||
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert EntityMoved");
|
||||
}
|
||||
}
|
||||
}
|
||||
wants_to_flee.clear();
|
||||
for done in turn_done.iter() {
|
||||
turns.remove(*done);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,13 +8,13 @@ mod regen_system;
|
|||
pub use regen_system::RegenSystem;
|
||||
mod encumbrance_system;
|
||||
pub use encumbrance_system::{EncumbranceSystem, CARRY_CAPACITY_PER_STRENGTH};
|
||||
mod bystander_ai_system;
|
||||
pub use bystander_ai_system::BystanderAI;
|
||||
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;
|
||||
mod approach_ai_system;
|
||||
pub use approach_ai_system::ApproachAI;
|
||||
mod chase_ai_system;
|
||||
pub use chase_ai_system::ChaseAI;
|
||||
mod flee_ai_system;
|
||||
pub use flee_ai_system::FleeAI;
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
use super::bystander_ai_system::try_move_randomly;
|
||||
use crate::{EntityMoved, Map, Monster, Position, TakingTurn, Viewshed, WantsToMelee};
|
||||
use rltk::Point;
|
||||
use specs::prelude::*;
|
||||
|
||||
pub struct MonsterAI {}
|
||||
|
||||
impl<'a> System<'a> for MonsterAI {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
WriteExpect<'a, Map>,
|
||||
WriteExpect<'a, rltk::RandomNumberGenerator>,
|
||||
ReadExpect<'a, Point>,
|
||||
ReadExpect<'a, Entity>,
|
||||
Entities<'a>,
|
||||
WriteStorage<'a, Viewshed>,
|
||||
ReadStorage<'a, Monster>,
|
||||
WriteStorage<'a, Position>,
|
||||
WriteStorage<'a, WantsToMelee>,
|
||||
WriteStorage<'a, EntityMoved>,
|
||||
ReadStorage<'a, TakingTurn>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
let (
|
||||
mut map,
|
||||
mut rng,
|
||||
player_pos,
|
||||
player_entity,
|
||||
entities,
|
||||
mut viewshed,
|
||||
monster,
|
||||
mut position,
|
||||
mut wants_to_melee,
|
||||
mut entity_moved,
|
||||
turns,
|
||||
) = data;
|
||||
|
||||
for (entity, mut viewshed, _monster, mut pos, _turn) in
|
||||
(&entities, &mut viewshed, &monster, &mut position, &turns).join()
|
||||
{
|
||||
let distance = rltk::DistanceAlg::Pythagoras.distance2d(Point::new(pos.x, pos.y), *player_pos);
|
||||
if distance < 1.5 {
|
||||
wants_to_melee
|
||||
.insert(entity, WantsToMelee { target: *player_entity })
|
||||
.expect("Unable to insert attack.");
|
||||
} else if viewshed.visible_tiles.contains(&*player_pos) {
|
||||
// If the player is visible, but the path is obstructed, this will currently search
|
||||
// the entire map (i.e. Will do a huge ASTAR to find an alternate route), and the
|
||||
// mob will follow that path until it leaves vision, then lose sight of the player
|
||||
// and stop.
|
||||
let path = rltk::a_star_search(map.xy_idx(pos.x, pos.y), map.xy_idx(player_pos.x, player_pos.y), &*map);
|
||||
if path.success && path.steps.len() > 1 {
|
||||
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;
|
||||
idx = map.xy_idx(pos.x, pos.y);
|
||||
map.blocked[idx] = true;
|
||||
viewshed.dirty = true;
|
||||
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker");
|
||||
}
|
||||
} else {
|
||||
if try_move_randomly(&mut pos, &mut rng, &mut map, &mut viewshed) {
|
||||
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
raws::Reaction, Faction, Map, Mind, Position, TakingTurn, Telepath, Viewshed, WantsToApproach, WantsToFlee,
|
||||
raws::Reaction, Chasing, Faction, Map, Mind, Position, TakingTurn, Telepath, Viewshed, WantsToApproach, WantsToFlee,
|
||||
};
|
||||
use specs::prelude::*;
|
||||
use std::collections::HashSet;
|
||||
|
|
@ -20,6 +20,7 @@ impl<'a> System<'a> for VisibleAI {
|
|||
ReadStorage<'a, Viewshed>,
|
||||
ReadStorage<'a, Telepath>,
|
||||
ReadStorage<'a, Mind>,
|
||||
WriteStorage<'a, Chasing>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
|
|
@ -35,6 +36,7 @@ impl<'a> System<'a> for VisibleAI {
|
|||
viewsheds,
|
||||
telepaths,
|
||||
minds,
|
||||
mut chasing,
|
||||
) = data;
|
||||
|
||||
for (entity, _turn, faction, pos, viewshed) in (&entities, &turns, &factions, &positions, &viewsheds).join() {
|
||||
|
|
@ -42,7 +44,7 @@ impl<'a> System<'a> for VisibleAI {
|
|||
continue;
|
||||
}
|
||||
let this_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 idxs: HashSet<usize> = HashSet::new();
|
||||
for visible_tile in viewshed.visible_tiles.iter() {
|
||||
|
|
@ -70,6 +72,7 @@ impl<'a> System<'a> for VisibleAI {
|
|||
wants_to_approach
|
||||
.insert(entity, WantsToApproach { idx: reaction.0 as i32 })
|
||||
.expect("Error inserting WantsToApproach");
|
||||
chasing.insert(entity, Chasing { target: reaction.2 }).expect("Unable to insert Chasing");
|
||||
done = true;
|
||||
}
|
||||
Reaction::Flee => {
|
||||
|
|
@ -90,7 +93,7 @@ fn evaluate(
|
|||
map: &Map,
|
||||
factions: &ReadStorage<Faction>,
|
||||
this_faction: &str,
|
||||
reactions: &mut Vec<(usize, Reaction)>,
|
||||
reactions: &mut Vec<(usize, Reaction, Entity)>,
|
||||
minds: Option<&ReadStorage<Mind>>,
|
||||
) {
|
||||
for other_entity in map.tile_content[idx].iter() {
|
||||
|
|
@ -105,6 +108,7 @@ fn evaluate(
|
|||
reactions.push((
|
||||
idx,
|
||||
crate::raws::faction_reaction(this_faction, &faction.name, &crate::raws::RAWS.lock().unwrap()),
|
||||
*other_entity,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,9 +54,6 @@ pub struct Faction {
|
|||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Prop {}
|
||||
|
||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Monster {}
|
||||
|
||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct LootTable {
|
||||
pub table: String,
|
||||
|
|
@ -75,9 +72,6 @@ pub struct Clock {}
|
|||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct TakingTurn {}
|
||||
|
||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Bystander {}
|
||||
|
||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Quips {
|
||||
pub available: Vec<String>,
|
||||
|
|
@ -383,6 +377,11 @@ pub struct WantsToFlee {
|
|||
pub indices: Vec<usize>, // Dijkstra
|
||||
}
|
||||
|
||||
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||
pub struct Chasing {
|
||||
pub target: Entity,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Consumable {}
|
||||
|
||||
|
|
|
|||
27
src/main.rs
27
src/main.rs
|
|
@ -96,11 +96,6 @@ impl State {
|
|||
let mut encumbrance_system = ai::EncumbranceSystem {};
|
||||
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 approach_ai = ai::ApproachAI {};
|
||||
let mut mob = ai::MonsterAI {};
|
||||
let mut bystanders = ai::BystanderAI {};
|
||||
let mut trigger_system = trigger_system::TriggerSystem {};
|
||||
let mut melee_system = MeleeCombatSystem {};
|
||||
let mut damage_system = DamageSystem {};
|
||||
|
|
@ -119,11 +114,7 @@ impl State {
|
|||
energy.run_now(&self.ecs);
|
||||
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);
|
||||
approach_ai.run_now(&self.ecs);
|
||||
mob.run_now(&self.ecs);
|
||||
bystanders.run_now(&self.ecs);
|
||||
self.run_ai();
|
||||
trigger_system.run_now(&self.ecs);
|
||||
inventory_system.run_now(&self.ecs);
|
||||
item_use_system.run_now(&self.ecs);
|
||||
|
|
@ -138,6 +129,19 @@ impl State {
|
|||
self.ecs.maintain();
|
||||
}
|
||||
|
||||
fn run_ai(&mut self) {
|
||||
let mut adjacent_ai = ai::AdjacentAI {};
|
||||
let mut visible_ai = ai::VisibleAI {};
|
||||
let mut approach_ai = ai::ApproachAI {};
|
||||
let mut flee_ai = ai::FleeAI {};
|
||||
let mut chase_ai = ai::ChaseAI {};
|
||||
adjacent_ai.run_now(&self.ecs);
|
||||
visible_ai.run_now(&self.ecs);
|
||||
approach_ai.run_now(&self.ecs);
|
||||
flee_ai.run_now(&self.ecs);
|
||||
chase_ai.run_now(&self.ecs);
|
||||
}
|
||||
|
||||
fn run_map_index(&mut self) {
|
||||
let mut mapindex = MapIndexingSystem {};
|
||||
mapindex.run_now(&self.ecs);
|
||||
|
|
@ -523,10 +527,9 @@ fn main() -> rltk::BError {
|
|||
gs.ecs.register::<Burden>();
|
||||
gs.ecs.register::<Prop>();
|
||||
gs.ecs.register::<Player>();
|
||||
gs.ecs.register::<Chasing>();
|
||||
gs.ecs.register::<Faction>();
|
||||
gs.ecs.register::<Clock>();
|
||||
gs.ecs.register::<Monster>();
|
||||
gs.ecs.register::<Bystander>();
|
||||
gs.ecs.register::<Quips>();
|
||||
gs.ecs.register::<Mind>();
|
||||
gs.ecs.register::<Viewshed>();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use super::{
|
||||
gamelog, gui::get_item_display_name, Attributes, BlocksTile, BlocksVisibility, Bystander, Door, EntityMoved,
|
||||
Hidden, HungerClock, HungerState, Item, Map, Monster, Name, ParticleBuilder, Player, Pools, Position, Renderable,
|
||||
gamelog, gui::get_item_display_name, raws::Reaction, Attributes, BlocksTile, BlocksVisibility, Door, EntityMoved,
|
||||
Faction, Hidden, HungerClock, HungerState, Item, Map, Name, ParticleBuilder, Player, Pools, Position, Renderable,
|
||||
RunState, State, SufferDamage, Telepath, TileType, Viewshed, WantsToMelee, WantsToPickupItem,
|
||||
};
|
||||
use rltk::{Point, RandomNumberGenerator, Rltk, VirtualKeyCode};
|
||||
|
|
@ -275,7 +275,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool {
|
|||
let mut viewsheds = ecs.write_storage::<Viewshed>();
|
||||
let mut telepaths = ecs.write_storage::<Telepath>();
|
||||
let mut entity_moved = ecs.write_storage::<EntityMoved>();
|
||||
let friendlies = ecs.read_storage::<Bystander>();
|
||||
let factions = ecs.read_storage::<Faction>();
|
||||
let pools = ecs.read_storage::<Pools>();
|
||||
let map = ecs.fetch::<Map>();
|
||||
|
||||
|
|
@ -296,8 +296,17 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool {
|
|||
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 friendly = friendlies.get(*potential_target);
|
||||
if friendly.is_some() {
|
||||
let mut hostile = true;
|
||||
if pools.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !hostile {
|
||||
swap_entities.push((*potential_target, 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));
|
||||
|
|
@ -523,23 +532,27 @@ pub fn try_previous_level(ecs: &mut World) -> bool {
|
|||
fn skip_turn(ecs: &mut World) -> bool {
|
||||
let player_entity = ecs.fetch::<Entity>();
|
||||
let mut viewsheds = ecs.write_storage::<Viewshed>();
|
||||
let monsters = ecs.read_storage::<Monster>();
|
||||
let worldmap_resource = ecs.fetch::<Map>();
|
||||
let hunger_clocks = ecs.read_storage::<HungerClock>();
|
||||
|
||||
// Default to being able to heal by waiting.
|
||||
let mut can_heal = true;
|
||||
let factions = ecs.read_storage::<Faction>();
|
||||
|
||||
// Check viewshed for monsters nearby. If we can see a monster, we can't heal.
|
||||
let viewshed = viewsheds.get_mut(*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() {
|
||||
let mob = monsters.get(*entity_id);
|
||||
match mob {
|
||||
let faction = factions.get(*entity_id);
|
||||
match faction {
|
||||
None => {}
|
||||
Some(_) => {
|
||||
can_heal = false;
|
||||
Some(faction) => {
|
||||
let reaction =
|
||||
crate::raws::faction_reaction(&faction.name, "player", &crate::raws::RAWS.lock().unwrap());
|
||||
if reaction == Reaction::Attack {
|
||||
can_heal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -295,8 +295,6 @@ pub fn spawn_named_mob(
|
|||
for flag in flags.iter() {
|
||||
match flag.as_str() {
|
||||
"BLOCKS_TILE" => eb = eb.with(BlocksTile {}),
|
||||
"BYSTANDER" => eb = eb.with(Bystander {}),
|
||||
"MONSTER" => eb = eb.with(Monster {}),
|
||||
"MINDLESS" => {
|
||||
eb = eb.with(Faction { name: "mindless".to_string() });
|
||||
has_faction = true;
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ pub fn save_game(ecs: &mut World) {
|
|||
BlocksTile,
|
||||
BlocksVisibility,
|
||||
Burden,
|
||||
Bystander,
|
||||
Chasing,
|
||||
Clock,
|
||||
Confusion,
|
||||
Consumable,
|
||||
|
|
@ -83,7 +83,6 @@ pub fn save_game(ecs: &mut World) {
|
|||
MagicMapper,
|
||||
MeleeWeapon,
|
||||
Mind,
|
||||
Monster,
|
||||
MultiAttack,
|
||||
NaturalAttacks,
|
||||
Name,
|
||||
|
|
@ -174,7 +173,7 @@ pub fn load_game(ecs: &mut World) {
|
|||
BlocksTile,
|
||||
BlocksVisibility,
|
||||
Burden,
|
||||
Bystander,
|
||||
Chasing,
|
||||
Clock,
|
||||
Confusion,
|
||||
Consumable,
|
||||
|
|
@ -201,7 +200,6 @@ pub fn load_game(ecs: &mut World) {
|
|||
MagicMapper,
|
||||
MeleeWeapon,
|
||||
Mind,
|
||||
Monster,
|
||||
MultiAttack,
|
||||
NaturalAttacks,
|
||||
Name,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue