summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortroido <troido@protonmail.com>2020-03-02 12:02:54 +0100
committertroido <troido@protonmail.com>2020-03-02 12:02:54 +0100
commit27c0795fb70739ce5609a0f424d80491d4a8c5a1 (patch)
tree1a6ab802edb717a42ca67a8d997cf960d7f4f5ed
parentd246537a28a7a71dfb2487d31d6fac3ccab5053d (diff)
added monster ai
-rw-r--r--content/maps/room.json3
-rw-r--r--src/components/mod.rs18
-rw-r--r--src/componentwrapper.rs6
-rw-r--r--src/controls.rs2
-rw-r--r--src/defaultencyclopedia.rs14
-rw-r--r--src/playerstate.rs2
-rw-r--r--src/pos.rs18
-rw-r--r--src/resources/ground.rs1
-rw-r--r--src/room.rs14
-rw-r--r--src/systems/controlai.rs81
-rw-r--r--src/systems/controlinput.rs1
-rw-r--r--src/systems/fight.rs12
-rw-r--r--src/systems/mod.rs4
-rw-r--r--src/systems/updatecooldowns.rs8
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())
diff --git a/src/pos.rs b/src/pos.rs
index b6e54ad..d95c6f7 100644
--- a/src/pos.rs
+++ b/src/pos.rs
@@ -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 {