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",
|
"id": "neutral",
|
||||||
"responses": { "default": "ignore" }
|
"responses": { "default": "ignore", "hostile": "flee", "mindless": "flee", "carnivore": "flee" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "hostile",
|
"id": "hostile",
|
||||||
|
|
@ -21,6 +21,6 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "carnivore",
|
"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>,
|
WriteExpect<'a, RunState>,
|
||||||
ReadExpect<'a, Entity>,
|
ReadExpect<'a, Entity>,
|
||||||
ReadStorage<'a, Name>,
|
ReadStorage<'a, Name>,
|
||||||
|
ReadExpect<'a, Point>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
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 not ticking, do nothing.
|
||||||
if *runstate != RunState::Ticking {
|
if *runstate != RunState::Ticking {
|
||||||
return;
|
return;
|
||||||
|
|
@ -44,7 +57,7 @@ impl<'a> System<'a> for EnergySystem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// EVERYTHING ELSE
|
// 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) {
|
let burden_modifier = if let Some(burden) = burdens.get(entity) {
|
||||||
match burden.level {
|
match burden.level {
|
||||||
BurdenLevel::Burdened => 0.75,
|
BurdenLevel::Burdened => 0.75,
|
||||||
|
|
@ -76,6 +89,16 @@ impl<'a> System<'a> for EnergySystem {
|
||||||
// has enough energy, they take a turn and decrement their energy
|
// has enough energy, they take a turn and decrement their energy
|
||||||
// by TURN_COST. If the current entity is the player, await input.
|
// by TURN_COST. If the current entity is the player, await input.
|
||||||
if energy.current >= TURN_COST {
|
if energy.current >= TURN_COST {
|
||||||
|
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.");
|
turns.insert(entity, TakingTurn {}).expect("Unable to insert turn.");
|
||||||
energy.current -= TURN_COST;
|
energy.current -= TURN_COST;
|
||||||
if LOG_TICKS {
|
if LOG_TICKS {
|
||||||
|
|
@ -85,8 +108,6 @@ impl<'a> System<'a> for EnergySystem {
|
||||||
name, energy.current
|
name, energy.current
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if entity == *player {
|
|
||||||
*runstate = RunState::AwaitingInput;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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;
|
pub use regen_system::RegenSystem;
|
||||||
mod encumbrance_system;
|
mod encumbrance_system;
|
||||||
pub use encumbrance_system::{EncumbranceSystem, CARRY_CAPACITY_PER_STRENGTH};
|
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;
|
mod adjacent_ai_system;
|
||||||
pub use adjacent_ai_system::AdjacentAI;
|
pub use adjacent_ai_system::AdjacentAI;
|
||||||
mod visible_ai_system;
|
mod visible_ai_system;
|
||||||
pub use visible_ai_system::VisibleAI;
|
pub use visible_ai_system::VisibleAI;
|
||||||
mod approach_ai_system;
|
mod approach_ai_system;
|
||||||
pub use approach_ai_system::ApproachAI;
|
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::{
|
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 specs::prelude::*;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
@ -20,6 +20,7 @@ impl<'a> System<'a> for VisibleAI {
|
||||||
ReadStorage<'a, Viewshed>,
|
ReadStorage<'a, Viewshed>,
|
||||||
ReadStorage<'a, Telepath>,
|
ReadStorage<'a, Telepath>,
|
||||||
ReadStorage<'a, Mind>,
|
ReadStorage<'a, Mind>,
|
||||||
|
WriteStorage<'a, Chasing>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
|
|
@ -35,6 +36,7 @@ impl<'a> System<'a> for VisibleAI {
|
||||||
viewsheds,
|
viewsheds,
|
||||||
telepaths,
|
telepaths,
|
||||||
minds,
|
minds,
|
||||||
|
mut chasing,
|
||||||
) = data;
|
) = data;
|
||||||
|
|
||||||
for (entity, _turn, faction, pos, viewshed) in (&entities, &turns, &factions, &positions, &viewsheds).join() {
|
for (entity, _turn, faction, pos, viewshed) in (&entities, &turns, &factions, &positions, &viewsheds).join() {
|
||||||
|
|
@ -42,7 +44,7 @@ impl<'a> System<'a> for VisibleAI {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let this_idx = map.xy_idx(pos.x, pos.y);
|
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 flee: Vec<usize> = Vec::new();
|
||||||
let mut idxs: HashSet<usize> = HashSet::new();
|
let mut idxs: HashSet<usize> = HashSet::new();
|
||||||
for visible_tile in viewshed.visible_tiles.iter() {
|
for visible_tile in viewshed.visible_tiles.iter() {
|
||||||
|
|
@ -70,6 +72,7 @@ impl<'a> System<'a> for VisibleAI {
|
||||||
wants_to_approach
|
wants_to_approach
|
||||||
.insert(entity, WantsToApproach { idx: reaction.0 as i32 })
|
.insert(entity, WantsToApproach { idx: reaction.0 as i32 })
|
||||||
.expect("Error inserting WantsToApproach");
|
.expect("Error inserting WantsToApproach");
|
||||||
|
chasing.insert(entity, Chasing { target: reaction.2 }).expect("Unable to insert Chasing");
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
Reaction::Flee => {
|
Reaction::Flee => {
|
||||||
|
|
@ -90,7 +93,7 @@ fn evaluate(
|
||||||
map: &Map,
|
map: &Map,
|
||||||
factions: &ReadStorage<Faction>,
|
factions: &ReadStorage<Faction>,
|
||||||
this_faction: &str,
|
this_faction: &str,
|
||||||
reactions: &mut Vec<(usize, Reaction)>,
|
reactions: &mut Vec<(usize, Reaction, Entity)>,
|
||||||
minds: Option<&ReadStorage<Mind>>,
|
minds: Option<&ReadStorage<Mind>>,
|
||||||
) {
|
) {
|
||||||
for other_entity in map.tile_content[idx].iter() {
|
for other_entity in map.tile_content[idx].iter() {
|
||||||
|
|
@ -105,6 +108,7 @@ fn evaluate(
|
||||||
reactions.push((
|
reactions.push((
|
||||||
idx,
|
idx,
|
||||||
crate::raws::faction_reaction(this_faction, &faction.name, &crate::raws::RAWS.lock().unwrap()),
|
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)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Prop {}
|
pub struct Prop {}
|
||||||
|
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
|
||||||
pub struct Monster {}
|
|
||||||
|
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct LootTable {
|
pub struct LootTable {
|
||||||
pub table: String,
|
pub table: String,
|
||||||
|
|
@ -75,9 +72,6 @@ pub struct Clock {}
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct TakingTurn {}
|
pub struct TakingTurn {}
|
||||||
|
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
|
||||||
pub struct Bystander {}
|
|
||||||
|
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Quips {
|
pub struct Quips {
|
||||||
pub available: Vec<String>,
|
pub available: Vec<String>,
|
||||||
|
|
@ -383,6 +377,11 @@ pub struct WantsToFlee {
|
||||||
pub indices: Vec<usize>, // Dijkstra
|
pub indices: Vec<usize>, // Dijkstra
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||||
|
pub struct Chasing {
|
||||||
|
pub target: Entity,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Consumable {}
|
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 encumbrance_system = ai::EncumbranceSystem {};
|
||||||
let mut turn_status_system = ai::TurnStatusSystem {};
|
let mut turn_status_system = ai::TurnStatusSystem {};
|
||||||
let mut quip_system = ai::QuipSystem {};
|
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 trigger_system = trigger_system::TriggerSystem {};
|
||||||
let mut melee_system = MeleeCombatSystem {};
|
let mut melee_system = MeleeCombatSystem {};
|
||||||
let mut damage_system = DamageSystem {};
|
let mut damage_system = DamageSystem {};
|
||||||
|
|
@ -119,11 +114,7 @@ impl State {
|
||||||
energy.run_now(&self.ecs);
|
energy.run_now(&self.ecs);
|
||||||
turn_status_system.run_now(&self.ecs);
|
turn_status_system.run_now(&self.ecs);
|
||||||
quip_system.run_now(&self.ecs);
|
quip_system.run_now(&self.ecs);
|
||||||
adjacent_ai.run_now(&self.ecs);
|
self.run_ai();
|
||||||
visible_ai.run_now(&self.ecs);
|
|
||||||
approach_ai.run_now(&self.ecs);
|
|
||||||
mob.run_now(&self.ecs);
|
|
||||||
bystanders.run_now(&self.ecs);
|
|
||||||
trigger_system.run_now(&self.ecs);
|
trigger_system.run_now(&self.ecs);
|
||||||
inventory_system.run_now(&self.ecs);
|
inventory_system.run_now(&self.ecs);
|
||||||
item_use_system.run_now(&self.ecs);
|
item_use_system.run_now(&self.ecs);
|
||||||
|
|
@ -138,6 +129,19 @@ impl State {
|
||||||
self.ecs.maintain();
|
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) {
|
fn run_map_index(&mut self) {
|
||||||
let mut mapindex = MapIndexingSystem {};
|
let mut mapindex = MapIndexingSystem {};
|
||||||
mapindex.run_now(&self.ecs);
|
mapindex.run_now(&self.ecs);
|
||||||
|
|
@ -523,10 +527,9 @@ fn main() -> rltk::BError {
|
||||||
gs.ecs.register::<Burden>();
|
gs.ecs.register::<Burden>();
|
||||||
gs.ecs.register::<Prop>();
|
gs.ecs.register::<Prop>();
|
||||||
gs.ecs.register::<Player>();
|
gs.ecs.register::<Player>();
|
||||||
|
gs.ecs.register::<Chasing>();
|
||||||
gs.ecs.register::<Faction>();
|
gs.ecs.register::<Faction>();
|
||||||
gs.ecs.register::<Clock>();
|
gs.ecs.register::<Clock>();
|
||||||
gs.ecs.register::<Monster>();
|
|
||||||
gs.ecs.register::<Bystander>();
|
|
||||||
gs.ecs.register::<Quips>();
|
gs.ecs.register::<Quips>();
|
||||||
gs.ecs.register::<Mind>();
|
gs.ecs.register::<Mind>();
|
||||||
gs.ecs.register::<Viewshed>();
|
gs.ecs.register::<Viewshed>();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use super::{
|
use super::{
|
||||||
gamelog, gui::get_item_display_name, Attributes, BlocksTile, BlocksVisibility, Bystander, Door, EntityMoved,
|
gamelog, gui::get_item_display_name, raws::Reaction, Attributes, BlocksTile, BlocksVisibility, Door, EntityMoved,
|
||||||
Hidden, HungerClock, HungerState, Item, Map, Monster, Name, ParticleBuilder, Player, Pools, Position, Renderable,
|
Faction, Hidden, HungerClock, HungerState, Item, Map, Name, ParticleBuilder, Player, Pools, Position, Renderable,
|
||||||
RunState, State, SufferDamage, Telepath, TileType, Viewshed, WantsToMelee, WantsToPickupItem,
|
RunState, State, SufferDamage, Telepath, TileType, Viewshed, WantsToMelee, WantsToPickupItem,
|
||||||
};
|
};
|
||||||
use rltk::{Point, RandomNumberGenerator, Rltk, VirtualKeyCode};
|
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 viewsheds = ecs.write_storage::<Viewshed>();
|
||||||
let mut telepaths = ecs.write_storage::<Telepath>();
|
let mut telepaths = ecs.write_storage::<Telepath>();
|
||||||
let mut entity_moved = ecs.write_storage::<EntityMoved>();
|
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 pools = ecs.read_storage::<Pools>();
|
||||||
let map = ecs.fetch::<Map>();
|
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);
|
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 friendly = friendlies.get(*potential_target);
|
let mut hostile = true;
|
||||||
if friendly.is_some() {
|
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));
|
swap_entities.push((*potential_target, pos.x, pos.y));
|
||||||
pos.x = min(map.width - 1, max(0, pos.x + delta_x));
|
pos.x = min(map.width - 1, max(0, pos.x + delta_x));
|
||||||
pos.y = min(map.height - 1, max(0, pos.y + delta_y));
|
pos.y = min(map.height - 1, max(0, pos.y + delta_y));
|
||||||
|
|
@ -523,27 +532,31 @@ pub fn try_previous_level(ecs: &mut World) -> bool {
|
||||||
fn skip_turn(ecs: &mut World) -> bool {
|
fn skip_turn(ecs: &mut World) -> bool {
|
||||||
let player_entity = ecs.fetch::<Entity>();
|
let player_entity = ecs.fetch::<Entity>();
|
||||||
let mut viewsheds = ecs.write_storage::<Viewshed>();
|
let mut viewsheds = ecs.write_storage::<Viewshed>();
|
||||||
let monsters = ecs.read_storage::<Monster>();
|
|
||||||
let worldmap_resource = ecs.fetch::<Map>();
|
let worldmap_resource = ecs.fetch::<Map>();
|
||||||
let hunger_clocks = ecs.read_storage::<HungerClock>();
|
let hunger_clocks = ecs.read_storage::<HungerClock>();
|
||||||
|
|
||||||
// Default to being able to heal by waiting.
|
// Default to being able to heal by waiting.
|
||||||
let mut can_heal = true;
|
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.
|
// Check viewshed for monsters nearby. If we can see a monster, we can't heal.
|
||||||
let viewshed = viewsheds.get_mut(*player_entity).unwrap();
|
let viewshed = viewsheds.get_mut(*player_entity).unwrap();
|
||||||
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() {
|
||||||
let mob = monsters.get(*entity_id);
|
let faction = factions.get(*entity_id);
|
||||||
match mob {
|
match faction {
|
||||||
None => {}
|
None => {}
|
||||||
Some(_) => {
|
Some(faction) => {
|
||||||
|
let reaction =
|
||||||
|
crate::raws::faction_reaction(&faction.name, "player", &crate::raws::RAWS.lock().unwrap());
|
||||||
|
if reaction == Reaction::Attack {
|
||||||
can_heal = false;
|
can_heal = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Dirty viewshed (so we search for hidden tiles whenever we wait)
|
// Dirty viewshed (so we search for hidden tiles whenever we wait)
|
||||||
viewshed.dirty = true;
|
viewshed.dirty = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -295,8 +295,6 @@ pub fn spawn_named_mob(
|
||||||
for flag in flags.iter() {
|
for flag in flags.iter() {
|
||||||
match flag.as_str() {
|
match flag.as_str() {
|
||||||
"BLOCKS_TILE" => eb = eb.with(BlocksTile {}),
|
"BLOCKS_TILE" => eb = eb.with(BlocksTile {}),
|
||||||
"BYSTANDER" => eb = eb.with(Bystander {}),
|
|
||||||
"MONSTER" => eb = eb.with(Monster {}),
|
|
||||||
"MINDLESS" => {
|
"MINDLESS" => {
|
||||||
eb = eb.with(Faction { name: "mindless".to_string() });
|
eb = eb.with(Faction { name: "mindless".to_string() });
|
||||||
has_faction = true;
|
has_faction = true;
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ pub fn save_game(ecs: &mut World) {
|
||||||
BlocksTile,
|
BlocksTile,
|
||||||
BlocksVisibility,
|
BlocksVisibility,
|
||||||
Burden,
|
Burden,
|
||||||
Bystander,
|
Chasing,
|
||||||
Clock,
|
Clock,
|
||||||
Confusion,
|
Confusion,
|
||||||
Consumable,
|
Consumable,
|
||||||
|
|
@ -83,7 +83,6 @@ pub fn save_game(ecs: &mut World) {
|
||||||
MagicMapper,
|
MagicMapper,
|
||||||
MeleeWeapon,
|
MeleeWeapon,
|
||||||
Mind,
|
Mind,
|
||||||
Monster,
|
|
||||||
MultiAttack,
|
MultiAttack,
|
||||||
NaturalAttacks,
|
NaturalAttacks,
|
||||||
Name,
|
Name,
|
||||||
|
|
@ -174,7 +173,7 @@ pub fn load_game(ecs: &mut World) {
|
||||||
BlocksTile,
|
BlocksTile,
|
||||||
BlocksVisibility,
|
BlocksVisibility,
|
||||||
Burden,
|
Burden,
|
||||||
Bystander,
|
Chasing,
|
||||||
Clock,
|
Clock,
|
||||||
Confusion,
|
Confusion,
|
||||||
Consumable,
|
Consumable,
|
||||||
|
|
@ -201,7 +200,6 @@ pub fn load_game(ecs: &mut World) {
|
||||||
MagicMapper,
|
MagicMapper,
|
||||||
MeleeWeapon,
|
MeleeWeapon,
|
||||||
Mind,
|
Mind,
|
||||||
Monster,
|
|
||||||
MultiAttack,
|
MultiAttack,
|
||||||
NaturalAttacks,
|
NaturalAttacks,
|
||||||
Name,
|
Name,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue