From 715c9106dbff4524f3fdf5d23f762e5e6518e0cb Mon Sep 17 00:00:00 2001 From: troido Date: Mon, 24 Feb 2020 12:38:23 +0100 Subject: healing works now too, the first time based system --- src/components/mod.rs | 25 +++++++++++------ src/componentwrapper.rs | 3 +- src/playerstate.rs | 5 ++-- src/resources/ground.rs | 43 ++++++++++++++++++++++++++++ src/resources/mod.rs | 67 ++++++++------------------------------------ src/resources/newentities.rs | 28 ++++++++++++++++++ src/room.rs | 14 +++++---- src/systems/fight.rs | 3 +- src/systems/heal.rs | 37 ++++++++++++++++++++++++ src/systems/mod.rs | 4 ++- src/systems/trapping.rs | 4 +-- src/systems/useitem.rs | 5 ++-- src/world.rs | 9 ++++-- todo.md | 22 +++++++++++++++ 14 files changed, 187 insertions(+), 82 deletions(-) create mode 100644 src/resources/ground.rs create mode 100644 src/resources/newentities.rs create mode 100644 src/systems/heal.rs create mode 100644 todo.md diff --git a/src/components/mod.rs b/src/components/mod.rs index 330bc67..7ca0eba 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -123,16 +123,17 @@ pub struct Attacked { pub attacks: Vec } -pub fn add_attack(attacked: &mut WriteStorage , ent: Entity, attack: Attack) { - attacked - .entry(ent) - .unwrap() - .or_insert_with(Attacked::default) - .attacks - .push(attack); +impl Attacked { + pub fn add_attack(attacked: &mut WriteStorage , ent: Entity, attack: Attack) { + attacked + .entry(ent) + .unwrap() + .or_insert_with(Attacked::default) + .attacks + .push(attack); + } } - #[derive(Default, Component, Debug, Clone)] #[storage(NullStorage)] pub struct Entered; @@ -153,3 +154,11 @@ pub struct Fighter { pub attack: Attack } +#[derive(Component, Debug, Clone)] +#[storage(HashMapStorage)] +pub struct Healing { + pub delay: i64, + pub health: i64, + pub next_heal: Option +} + diff --git a/src/componentwrapper.rs b/src/componentwrapper.rs index 67f551f..7e48668 100644 --- a/src/componentwrapper.rs +++ b/src/componentwrapper.rs @@ -8,7 +8,7 @@ use crate::{ Sprite, playerstate::RoomPos, attack::Attack, - components::{Visible, Blocking, Player, Floor, Item, Inventory, Health, Serialise, RoomExit, Trap, Fighter}, + components::{Visible, Blocking, Player, Floor, Item, Inventory, Health, Serialise, RoomExit, Trap, Fighter, Healing}, parameter::{Parameter, ParameterType} }; @@ -116,6 +116,7 @@ components!( }; Trap (damage: Int) {Trap{attack: Attack::new(damage)}}; Fighter (damage: Int) {Fighter{attack: Attack::new(damage)}}; + Healing (delay: Int, health: Int) {Healing{delay, health, next_heal: None}}; ); diff --git a/src/playerstate.rs b/src/playerstate.rs index 3339dd1..4117bad 100644 --- a/src/playerstate.rs +++ b/src/playerstate.rs @@ -6,7 +6,7 @@ use crate::{ componentwrapper::{ComponentWrapper, PreEntity}, PlayerId, RoomId, - components::{Visible, Player, Inventory, Health, Fighter}, + components::{Visible, Player, Inventory, Health, Fighter, Healing}, attack::Attack, Result, aerr, @@ -116,7 +116,8 @@ impl PlayerState { capacity: self.inventory_capacity }), ComponentWrapper::Health(Health{health: self.health, maxhealth: self.maximum_health}), - ComponentWrapper::Fighter(Fighter{attack: Attack::new(5)}) + ComponentWrapper::Fighter(Fighter{attack: Attack::new(5)}), + ComponentWrapper::Healing(Healing{delay: 50, health: 1, next_heal: None}) ] } } diff --git a/src/resources/ground.rs b/src/resources/ground.rs new file mode 100644 index 0000000..b8c6b5a --- /dev/null +++ b/src/resources/ground.rs @@ -0,0 +1,43 @@ + +use std::collections::{HashMap, HashSet}; + +use specs::{ + ReadStorage, + Component, + Entity +}; + +use crate::{ + components::{Visible, Removed}, + Pos +}; + +#[derive(Default)] +pub struct Ground { + pub cells: HashMap> +} + +impl Ground { + pub fn components_on<'a, C: Component>(&self, pos: Pos, component_type: &'a ReadStorage, removals: &'a ReadStorage) -> Vec<&'a C> { + self.cells + .get(&pos) + .unwrap_or(&HashSet::new()) + .iter() + .filter(|e| !removals.contains(**e)) + .filter_map(|e| component_type.get(*e)) + .collect() + } + + pub fn by_height(&self, pos: &Pos, visibles: &ReadStorage, ignore: &Entity) -> Vec { + let mut entities: Vec = self.cells + .get(&pos).unwrap_or(&HashSet::new()) + .iter() + .cloned() + .filter(|e| e != ignore && visibles.contains(*e)) + .collect(); + entities.sort_by(|a, b| + visibles.get(*b).unwrap().height.partial_cmp(&visibles.get(*a).unwrap().height).unwrap() + ); + entities + } +} diff --git a/src/resources/mod.rs b/src/resources/mod.rs index 07669c4..2f218fe 100644 --- a/src/resources/mod.rs +++ b/src/resources/mod.rs @@ -1,18 +1,19 @@ -use std::collections::{HashMap, HashSet}; -use specs::{Entity, ReadStorage, Component}; +mod ground; +mod newentities; + +pub use ground::Ground; +pub use newentities::NewEntities; + +use std::collections::{HashMap}; +use specs::{Entity}; use crate::{ Pos, controls::Control, worldmessages::WorldMessage, - componentwrapper::PreEntity, - Encyclopedia, PlayerId, RoomId, - Result, - Template, - components::{Visible, Removed}, playerstate::RoomPos }; @@ -38,54 +39,6 @@ pub struct Spawn { pub pos: Pos } -#[derive(Default)] -pub struct Ground { - pub cells: HashMap> -} -impl Ground { - pub fn components_on<'a, C: Component>(&self, pos: Pos, component_type: &'a ReadStorage, removals: &'a ReadStorage) -> Vec<&'a C> { - self.cells - .get(&pos) - .unwrap_or(&HashSet::new()) - .iter() - .filter(|e| !removals.contains(**e)) - .filter_map(|e| component_type.get(*e)) - .collect() - } - - pub fn by_height(&self, pos: &Pos, visibles: &ReadStorage, ignore: &Entity) -> Vec { - let mut entities: Vec = self.cells - .get(&pos).unwrap_or(&HashSet::new()) - .iter() - .cloned() - .filter(|e| e != ignore && visibles.contains(*e)) - .collect(); - entities.sort_by(|a, b| - visibles.get(*b).unwrap().height.partial_cmp(&visibles.get(*a).unwrap().height).unwrap() - ); - entities - } -} - - -#[derive(Default)] -pub struct NewEntities { - pub to_build: Vec<(Pos, PreEntity)>, - pub encyclopedia: Encyclopedia -} -impl NewEntities { - pub fn new(encyclopedia: Encyclopedia) -> Self { - Self{ - to_build: Vec::new(), - encyclopedia - } - } - pub fn create(&mut self, pos: Pos, template: Template) -> Result<()> { - let components = self.encyclopedia.construct(&template)?; - self.to_build.push((pos, components)); - Ok(()) - } -} #[derive(Default)] pub struct Players { @@ -97,3 +50,7 @@ pub struct Emigration { pub emigrants: Vec<(PlayerId, RoomId, RoomPos)> } +#[derive(Default)] +pub struct TimeStamp { + pub time: i64 +} diff --git a/src/resources/newentities.rs b/src/resources/newentities.rs new file mode 100644 index 0000000..a9c4ddc --- /dev/null +++ b/src/resources/newentities.rs @@ -0,0 +1,28 @@ + +use crate::{ + Pos, + Encyclopedia, + Template, + Result, + componentwrapper::PreEntity +}; + +#[derive(Default)] +pub struct NewEntities { + pub to_build: Vec<(Pos, PreEntity)>, + pub encyclopedia: Encyclopedia +} + +impl NewEntities { + pub fn new(encyclopedia: Encyclopedia) -> Self { + Self{ + to_build: Vec::new(), + encyclopedia + } + } + pub fn create(&mut self, pos: Pos, template: Template) -> Result<()> { + let components = self.encyclopedia.construct(&template)?; + self.to_build.push((pos, components)); + Ok(()) + } +} diff --git a/src/room.rs b/src/room.rs index e19d689..3856cbd 100644 --- a/src/room.rs +++ b/src/room.rs @@ -21,7 +21,8 @@ use crate::{ NewEntities, Spawn, Players, - Emigration + Emigration, + TimeStamp }, systems::{ Move, @@ -35,7 +36,8 @@ use crate::{ Use, Attacking, Trapping, - Fight + Fight, + Heal }, components::{ Position, @@ -79,13 +81,14 @@ impl <'a, 'b>Room<'a, 'b> { let mut dispatcher = DispatcherBuilder::new() .with(RegisterNew::default(), "registernew", &[]) - .with(ControlInput, "controlinput", &[]) + .with(ControlInput, "controlinput", &["registernew"]) .with(Take, "take", &["controlinput"]) .with(Use, "use", &["controlinput"]) .with(Move, "move", &["registernew", "controlinput"]) .with(Trapping, "trapping", &["move"]) .with(Fight, "fight", &["move", "controlinput"]) - .with(Attacking, "attacking", &["use", "trapping", "fight"]) + .with(Heal, "heal", &["registernew"]) + .with(Attacking, "attacking", &["use", "trapping", "fight", "heal"]) .with(View::default(), "view", &["move", "attacking"]) .with(Migrate, "migrate", &["view"]) .with(Create, "create", &["view", "controlinput"]) @@ -135,7 +138,8 @@ impl <'a, 'b>Room<'a, 'b> { self.world.fetch::().output.clone() } - pub fn update(&mut self) { + pub fn update(&mut self, timestamp: i64) { + self.world.fetch_mut::().time = timestamp; self.dispatcher.dispatch(&self.world); self.world.maintain(); } diff --git a/src/systems/fight.rs b/src/systems/fight.rs index 4b3c672..d0342b5 100644 --- a/src/systems/fight.rs +++ b/src/systems/fight.rs @@ -13,7 +13,6 @@ use crate::components::{ Controller, Position, Attacked, - add_attack, Fighter, Health }; @@ -42,7 +41,7 @@ impl <'a> System<'a> for Fight { for direction in directions { for ent in ground.cells.get(&(position.pos + direction.to_position())).unwrap_or(&HashSet::new()) { if healths.contains(*ent) && *ent != entity { - add_attack(&mut attacked, *ent, fighter.attack.clone()); + Attacked::add_attack(&mut attacked, *ent, fighter.attack.clone()); break; } } diff --git a/src/systems/heal.rs b/src/systems/heal.rs new file mode 100644 index 0000000..458bc17 --- /dev/null +++ b/src/systems/heal.rs @@ -0,0 +1,37 @@ + +use specs::{ + WriteStorage, + Read, + System, + Join +}; + +use crate::{ + components::{Health, Healing}, + resources::TimeStamp +}; + + +pub struct Heal; +impl <'a> System<'a> for Heal { + type SystemData = ( + WriteStorage<'a, Health>, + WriteStorage<'a, Healing>, + Read<'a, TimeStamp> + ); + fn run(&mut self, (mut healths, mut healing, timestamp): Self::SystemData) { + for (health, mut heal) in (&mut healths, &mut healing).join() { + + if let Some(next_heal) = heal.next_heal { + if next_heal <= timestamp.time { + health.heal(heal.health); + heal.next_heal = None + } + } + if health.health < health.maxhealth && heal.next_heal == None { + heal.next_heal = Some(timestamp.time + heal.delay) + } + } + } +} + diff --git a/src/systems/mod.rs b/src/systems/mod.rs index d73a578..b1d0994 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -11,6 +11,7 @@ mod useitem; mod attacking; mod trapping; mod fight; +mod heal; pub use self::{ controlinput::ControlInput, @@ -24,5 +25,6 @@ pub use self::{ useitem::Use, attacking::Attacking, trapping::Trapping, - fight::Fight + fight::Fight, + heal::Heal }; diff --git a/src/systems/trapping.rs b/src/systems/trapping.rs index 5e0921e..d6902a8 100644 --- a/src/systems/trapping.rs +++ b/src/systems/trapping.rs @@ -9,7 +9,7 @@ use specs::{ }; use crate::{ - components::{Health, Attacked, add_attack, Moved, Entered, Trap, Position}, + components::{Health, Attacked, Moved, Entered, Trap, Position}, resources::Ground }; @@ -31,7 +31,7 @@ impl <'a> System<'a> for Trapping { for (entity, _entered, trap, position) in (&entities, &entereds, &traps, &positions).join() { for ent in ground.cells.get(&position.pos).unwrap(){ if ent != &entity && moves.contains(*ent) && healths.contains(*ent) { - add_attack(&mut victims, *ent, trap.attack.clone()); + Attacked::add_attack(&mut victims, *ent, trap.attack.clone()); } } } diff --git a/src/systems/useitem.rs b/src/systems/useitem.rs index c9bcfcd..4414b39 100644 --- a/src/systems/useitem.rs +++ b/src/systems/useitem.rs @@ -14,8 +14,7 @@ use crate::{ Controller, Position, Inventory, - Attacked, - add_attack + Attacked }, resources::{NewEntities}, components::item::ItemAction::{None, Build, Eat}, @@ -46,7 +45,7 @@ impl <'a> System<'a> for Use { inventory.items.remove(*rank); } Eat(health_diff) => { - add_attack(&mut attacked, ent, Attack::new(-*health_diff)); + Attacked::add_attack(&mut attacked, ent, Attack::new(-*health_diff)); inventory.items.remove(*rank); } None => {} diff --git a/src/world.rs b/src/world.rs index 9c22e0f..9f86cb8 100644 --- a/src/world.rs +++ b/src/world.rs @@ -21,7 +21,8 @@ pub struct World<'a, 'b> { default_room: RoomId, players: HashMap, rooms: HashMap>, - encyclopedia: Encyclopedia + encyclopedia: Encyclopedia, + timestamp: i64 } @@ -34,7 +35,8 @@ impl <'a, 'b>World<'a, 'b> { default_room, encyclopedia, players: HashMap::new(), - rooms: HashMap::new() + rooms: HashMap::new(), + timestamp: 0 } } @@ -92,8 +94,9 @@ impl <'a, 'b>World<'a, 'b> { pub fn update(&mut self) { self.migrate(); for room in self.rooms.values_mut() { - room.update(); + room.update(self.timestamp); } + self.timestamp += 1; } fn migrate(&mut self) { diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..610b059 --- /dev/null +++ b/todo.md @@ -0,0 +1,22 @@ + +# TODO + +- delays for moving/fighting +- handle player death +- log world events to player +- interactions +- spawners +- growing/timers +- room unloading +- relative room locations +- ai mobs +- healing +- equipment +- autofight/autoretaliate +- wounds +- loot +- shortcuts for defining items +- improve error handling +- don't crash immediately on invalid map files +- safely write files + -- cgit