diff options
| author | troido <troido@protonmail.com> | 2020-03-02 12:02:54 +0100 |
|---|---|---|
| committer | troido <troido@protonmail.com> | 2020-03-02 12:02:54 +0100 |
| commit | 27c0795fb70739ce5609a0f424d80491d4a8c5a1 (patch) | |
| tree | 1a6ab802edb717a42ca67a8d997cf960d7f4f5ed | |
| parent | d246537a28a7a71dfb2487d31d6fac3ccab5053d (diff) | |
added monster ai
| -rw-r--r-- | content/maps/room.json | 3 | ||||
| -rw-r--r-- | src/components/mod.rs | 18 | ||||
| -rw-r--r-- | src/componentwrapper.rs | 6 | ||||
| -rw-r--r-- | src/controls.rs | 2 | ||||
| -rw-r--r-- | src/defaultencyclopedia.rs | 14 | ||||
| -rw-r--r-- | src/playerstate.rs | 2 | ||||
| -rw-r--r-- | src/pos.rs | 18 | ||||
| -rw-r--r-- | src/resources/ground.rs | 1 | ||||
| -rw-r--r-- | src/room.rs | 14 | ||||
| -rw-r--r-- | src/systems/controlai.rs | 81 | ||||
| -rw-r--r-- | src/systems/controlinput.rs | 1 | ||||
| -rw-r--r-- | src/systems/fight.rs | 12 | ||||
| -rw-r--r-- | src/systems/mod.rs | 4 | ||||
| -rw-r--r-- | src/systems/updatecooldowns.rs | 8 |
14 files changed, 165 insertions, 19 deletions
diff --git a/content/maps/room.json b/content/maps/room.json index 1aa72af..1e12b4b 100644 --- a/content/maps/room.json +++ b/content/maps/room.json @@ -6,7 +6,7 @@ " XXXXXXXXXXXX~~~XXXXXXXXXXXXXXXXXXXXXX", " ,,,,,,,,,,,,~~~,,,,,,,,,,,,,,,,,,,,,X", " ,,,,,,,,,,,,,~~~,,,,,,,,,,,,,,,,,,,,,X", - " ,,,,,,,,,,,,,~~~~,,,,,,,,,,,,,,,,,,,,X", + " ,,,,,,,,,,r,,~~~~,,,,,,,,,,,,,,,,,,,,X", " bbbb..,,,,,,,,,,,~~~,,,,,,,,,,,,,,,,,,,,X", " ,,.,,,,,,,,,,,~~~,,,,,,,,,,,,,,,,,,,,X", " ,,,.,,,,,,,,,,,~~~,,,,,,,,,,,,,,,,,,,,X", @@ -43,6 +43,7 @@ "%": {"type": "portal", "kwargs": {"destination": "broom", "dest_pos": "northentry"}}, "^": ["grass", "spiketrap"], "d": ["grass", "dummy"], + "r": ["grass", "rat"], " ": [] } } diff --git a/src/components/mod.rs b/src/components/mod.rs index f7e483b..2759144 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -147,7 +147,8 @@ pub struct Trap { #[storage(HashMapStorage)] pub struct Fighter { pub attack: AttackMessage, - pub cooldown: i64 + pub cooldown: i64, + pub range: i64 } #[derive(Component, Debug, Clone)] @@ -179,4 +180,19 @@ pub struct Autofight { pub target: Option<Entity> } +#[derive(Component, Debug, Clone, Default)] +#[storage(HashMapStorage)] +pub struct MonsterAI { + pub move_chance: f64, + pub homesickness: f64, + pub view_distance: i64 +} + +#[derive(Component, Debug, Clone, Default)] +#[storage(HashMapStorage)] +pub struct Home { + pub home: Pos +} + + diff --git a/src/componentwrapper.rs b/src/componentwrapper.rs index 54c0daa..29bf39d 100644 --- a/src/componentwrapper.rs +++ b/src/componentwrapper.rs @@ -23,7 +23,8 @@ use crate::{ Healing, Volatile, AttackMessage, - Autofight + Autofight, + MonsterAI }, parameter::{Parameter, ParameterType} }; @@ -132,10 +133,11 @@ components!( } }; Trap (damage: Int) {Trap{attack: AttackMessage::new(damage)}}; - Fighter (damage: Int, cooldown: Int) {Fighter{attack: AttackMessage::new(damage), cooldown}}; + Fighter (damage: Int, cooldown: Int) {Fighter{attack: AttackMessage::new(damage), cooldown, range: 1}}; Healing (delay: Int, health: Int) {Healing{delay, health, next_heal: None}}; Volatile (delay: Int) {Volatile{delay, end_time: None}}; Autofight () {Autofight::default()}; + MonsterAI (move_chance: Float, homesickness: Float, view_distance: Int) {MonsterAI{move_chance, homesickness, view_distance}}; ); diff --git a/src/controls.rs b/src/controls.rs index d317317..d3dec68 100644 --- a/src/controls.rs +++ b/src/controls.rs @@ -4,7 +4,7 @@ use serde_json::Value; use specs::Entity; use crate::{PlayerId, Pos}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Direction { North, South, diff --git a/src/defaultencyclopedia.rs b/src/defaultencyclopedia.rs index c7e947e..0234c54 100644 --- a/src/defaultencyclopedia.rs +++ b/src/defaultencyclopedia.rs @@ -142,6 +142,20 @@ pub fn default_encyclopedia() -> Encyclopedia { "sprite": "wound", "height": 0.25, "components": [["Volatile", {"delay": ["int", 4]}]] + }, + "rat": { + "sprite": "rat", + "height": 1, + "components": [ + ["MonsterAI", { + "view_distance": ["int", 3], + "move_chance": ["float", 0.08], + "homesickness": ["float", 0.1] + }], + ["Health", {"health": ["int", 8], "maxhealth": ["int", 8]}], + ["Fighter", {"damage": ["int", 2], "cooldown": ["int", 6]}], + ["Movable", {"cooldown": ["int", 3]}] + ] } })).unwrap() } diff --git a/src/playerstate.rs b/src/playerstate.rs index baabacd..629d36f 100644 --- a/src/playerstate.rs +++ b/src/playerstate.rs @@ -125,7 +125,7 @@ impl PlayerState { capacity: self.inventory_capacity }), ComponentWrapper::Health(Health{health: self.health, maxhealth: self.maximum_health}), - ComponentWrapper::Fighter(Fighter{attack: AttackMessage::new(5), cooldown: 8}), + ComponentWrapper::Fighter(Fighter{attack: AttackMessage::new(5), cooldown: 8, range: 1}), ComponentWrapper::Healing(Healing{delay: 50, health: 1, next_heal: None}), ComponentWrapper::Movable(Movable{cooldown: 2}), ComponentWrapper::Autofight(Autofight::default()) @@ -1,6 +1,6 @@ -use std::ops::Add; +use std::ops::{Add, Sub}; use serde_json::Value; use serde::{Serialize, Serializer, ser::SerializeTuple}; use crate::util::clamp; @@ -31,6 +31,11 @@ impl Pos { y: val.get(1)?.as_i64()? }) } + + pub fn distance_to(&self, other: Pos) -> i64 { + let d = other - *self; + d.x.abs() + d.y.abs() + } } @@ -57,3 +62,14 @@ impl Add<Pos> for Pos { } } +impl Sub<Pos> for Pos { + type Output = Pos; + + fn sub(self, other: Pos) -> Pos { + Pos { + x: self.x - other.x, + y: self.y - other.y + } + } +} + diff --git a/src/resources/ground.rs b/src/resources/ground.rs index b8c6b5a..a5f4c00 100644 --- a/src/resources/ground.rs +++ b/src/resources/ground.rs @@ -40,4 +40,5 @@ impl Ground { ); entities } + } diff --git a/src/room.rs b/src/room.rs index 81b8cd0..03ee9db 100644 --- a/src/room.rs +++ b/src/room.rs @@ -39,7 +39,8 @@ use crate::{ Fight, Heal, Volate, - UpdateCooldowns + UpdateCooldowns, + ControlAI }, components::{ Position, @@ -87,16 +88,17 @@ impl <'a, 'b>Room<'a, 'b> { .with(RegisterNew::default(), "registernew", &[]) .with(UpdateCooldowns, "cool_down", &["registernew"]) .with(ControlInput, "controlinput", &["cool_down"]) - .with(Take, "take", &["controlinput"]) - .with(Use, "use", &["controlinput"]) - .with(Move, "move", &["registernew", "controlinput"]) + .with(ControlAI, "controlai", &["cool_down"]) + .with(Take, "take", &["controlinput", "controlai"]) + .with(Use, "use", &["controlinput", "controlai"]) + .with(Move, "move", &["controlinput", "controlai"]) .with(Trapping, "trapping", &["move"]) - .with(Fight, "fight", &["move", "controlinput"]) + .with(Fight, "fight", &["move"]) .with(Heal, "heal", &["registernew"]) .with(Attacking, "attacking", &["use", "trapping", "fight", "heal"]) .with(View::default(), "view", &["move", "attacking", "volate"]) .with(Migrate, "migrate", &["view"]) - .with(Create, "create", &["view", "controlinput"]) + .with(Create, "create", &["view"]) .with(Remove, "remove", &["view", "move"]) .build(); diff --git a/src/systems/controlai.rs b/src/systems/controlai.rs new file mode 100644 index 0000000..9d49b41 --- /dev/null +++ b/src/systems/controlai.rs @@ -0,0 +1,81 @@ + + +use rand::Rng; + +use specs::{ + ReadStorage, + WriteStorage, + Entities, + System, + Join +}; + +use crate::{ + components::{Controller, ControlCooldown, Fighter, MonsterAI, Home, Health, Position}, + controls::{Control, Direction::{North, South, East, West}} +}; + + +pub struct ControlAI; +impl <'a> System<'a> for ControlAI { + type SystemData = ( + Entities<'a>, + WriteStorage<'a, Controller>, + ReadStorage<'a, ControlCooldown>, + ReadStorage<'a, MonsterAI>, + ReadStorage<'a, Fighter>, + ReadStorage<'a, Home>, + ReadStorage<'a, Health>, + ReadStorage<'a, Position> + ); + fn run(&mut self, (entities, mut controllers, cooldowns, ais, fighters, homes, healths, positions): Self::SystemData) { + + for (entity, ai, position, ()) in (&entities, &ais, &positions, !&cooldowns).join() { + if let Some(fighter) = fighters.get(entity) { + let mut closest_distance = ai.view_distance + 1; + let mut closest = None; + let mut closest_position = None; + for (target, target_position, _) in (&entities, &positions, &healths).join() { + if target == entity { + continue; + } + let distance = position.pos.distance_to(target_position.pos); + if distance < closest_distance { + closest_distance = distance; + closest = Some(target); + closest_position = Some(target_position); + } + } + if let Some(target) = closest { + if closest_distance <= fighter.range { + controllers.insert(entity, Controller{control: Control::AttackTarget(target)}).unwrap(); + } else { + let p = position.pos; + let t = closest_position.unwrap().pos; + let mut directions = Vec::new(); + if t.x > p.x {directions.push(East);} + else if t.x < p.x {directions.push(West);} + if t.y > p.y {directions.push(South);} + else if t.y < p.y {directions.push(North);} + if !directions.is_empty() { + let direction = directions[rand::thread_rng().gen_range(0, directions.len())]; + controllers.insert(entity, Controller{control: Control::Move(direction)}).unwrap(); + } + } + return; + } + } + if rand::thread_rng().gen_range(0.0, 1.0) < ai.move_chance { + let direction = [North, South, East, West][rand::thread_rng().gen_range(0, 4)]; + controllers.insert(entity, Controller{control: Control::Move(direction)}).unwrap(); +// home = roomData.getComponent(obj, Home) +// if home is not None and home.home.inRoom() and random.random() < (ai.homesickness * pathfinding.distanceBetween(obj, home.home)): +// direction = pathfinding.stepTo(obj, home.home) +// else: +// direction = random.choice(["north", "south", "east", "west"]) +// movable.direction = direction + } + } + } +} + diff --git a/src/systems/controlinput.rs b/src/systems/controlinput.rs index 6d5b4ea..1a582e4 100644 --- a/src/systems/controlinput.rs +++ b/src/systems/controlinput.rs @@ -26,7 +26,6 @@ impl <'a> System<'a> for ControlInput { WriteStorage<'a, Autofight> ); fn run(&mut self, (entities, mut input, mut controllers, players, cooldowns, mut autofighters): Self::SystemData) { - controllers.clear(); for (player, entity, ()) in (&players, &entities, !&cooldowns).join() { if let Some(control) = input.actions.remove(&player.id){ diff --git a/src/systems/fight.rs b/src/systems/fight.rs index 182dba6..703ea4a 100644 --- a/src/systems/fight.rs +++ b/src/systems/fight.rs @@ -52,7 +52,17 @@ impl <'a> System<'a> for Fight { } } } - Control::AttackTarget(t) => {target = Some(*t);} + Control::AttackTarget(t) => { + if *t == entity { // don't knock yourself out + if let Some(autofighter) = autofighters.get_mut(entity){ + autofighter.target = None; + } + } else if let Some(target_position) = positions.get(*t){ + if position.pos.distance_to(target_position.pos) <= fighter.range { + target = Some(*t); + } + } + } _ => {} } if let Some(ent) = target { diff --git a/src/systems/mod.rs b/src/systems/mod.rs index 24ed3df..221ffc4 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -14,6 +14,7 @@ mod fight; mod heal; mod volate; mod updatecooldowns; +mod controlai; pub use self::{ controlinput::ControlInput, @@ -30,5 +31,6 @@ pub use self::{ fight::Fight, heal::Heal, volate::Volate, - updatecooldowns::UpdateCooldowns + updatecooldowns::UpdateCooldowns, + controlai::ControlAI }; diff --git a/src/systems/updatecooldowns.rs b/src/systems/updatecooldowns.rs index 73ca770..705561d 100644 --- a/src/systems/updatecooldowns.rs +++ b/src/systems/updatecooldowns.rs @@ -6,16 +6,18 @@ use specs::{ Join }; -use crate::components::ControlCooldown; +use crate::components::{Controller, ControlCooldown}; pub struct UpdateCooldowns; impl <'a> System<'a> for UpdateCooldowns { type SystemData = ( Entities<'a>, - WriteStorage<'a, ControlCooldown> + WriteStorage<'a, ControlCooldown>, + WriteStorage<'a, Controller> ); - fn run(&mut self, (entities, mut cooldowns): Self::SystemData) { + fn run(&mut self, (entities, mut cooldowns, mut controllers): Self::SystemData) { + controllers.clear(); let mut to_remove = Vec::new(); for (entity, cooldown) in (&entities, &mut cooldowns).join() { if cooldown.amount > 0 { |
