From cad08bd3002906ad462a25806c49a0288597fc7f Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Sat, 6 Jan 2024 13:02:31 +0100 Subject: [PATCH] Player and monster attacks follow the dungeon slayer rules --- src/artifacts.rs | 2 +- src/dungeon_slayer.rs | 19 ++++++++++++++----- src/game.rs | 22 ++++++++++++++++------ src/level.rs | 19 ++++++++++++++----- src/monster.rs | 9 ++++++--- src/player.rs | 13 ++++++++----- 6 files changed, 59 insertions(+), 25 deletions(-) diff --git a/src/artifacts.rs b/src/artifacts.rs index 16b1510..b26d961 100644 --- a/src/artifacts.rs +++ b/src/artifacts.rs @@ -72,7 +72,7 @@ impl Artifact for Potion { // only consume potion of the player can gain at least one health point if !player.is_healthy() { let old = player.get_life(); - player.change_life(self.health.try_into().unwrap()); + player.increase_life(self.health.try_into().unwrap()); let new = player.get_life(); messages.insert(0, format!("picked up potion and gained {} hp.", new - old).to_string()); self.health = 0; diff --git a/src/dungeon_slayer.rs b/src/dungeon_slayer.rs index 5168515..7a364bf 100644 --- a/src/dungeon_slayer.rs +++ b/src/dungeon_slayer.rs @@ -1,16 +1,22 @@ use rand::Rng; -pub fn do_challenge(stat: u8) -> bool { +pub fn do_challenge(stat: u8) -> (bool, u8) { if stat <= 20 { let mut rng = rand::thread_rng(); let dice_roll = rng.gen_range(1..21); return match dice_roll { - 20 => { false } - 1 => { true } - _ => dice_roll <= stat + 20 => { (false, 0) } + 1 => { (true, dice_roll) } + _ => { + if dice_roll <= stat { + (true, stat - dice_roll) + } else { + (false, 0) + } + } }; } - true + (false, 0) } pub struct PlayerStats { @@ -82,6 +88,9 @@ impl PlayerStats { pub fn get_defense(&self) -> u8 { self.body + self.toughness // TODO + self.armor } + pub fn get_hit(&self) -> u8 { + self.body + self.strength // TODO + self.weapon + } } pub struct MonsterStats { diff --git a/src/game.rs b/src/game.rs index c8e12bf..bfdf85a 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,3 +1,5 @@ +use crate::dungeon_slayer::do_challenge; +#[cfg(test)] use crate::dungeon_slayer::PlayerStats; use crate::level::{Level, StructureElement}; use crate::level_generator::LevelGenerator; @@ -157,11 +159,19 @@ impl Game { match m { None => {} Some(m) => { - // TODO fight the monster - self.player.change_life(-1); - m.decrease_life(1); - self.messages.insert(0, format!("{} hits you.", m.get_name()).to_string()); - self.messages.insert(0, format!("you hit {}.", m.get_name()).to_string()); + // Player attacks monster + let attack = do_challenge(self.player.player_stats.get_hit()); + if attack.0 { + let mut damage = attack.1; + let defense = do_challenge(m.get_defense()); + if defense.0 { + damage = damage.saturating_sub(defense.1); + } + self.messages.insert(0, format!("attack on {} successful with {} damage.", m.get_name(), damage).to_string()); + m.decrease_life(damage as usize); + } else { + self.messages.insert(0, format!("attack on {} failed.", m.get_name()).to_string()); + } // monster died, player gains experience if m.is_dead() { self.player.gain_experience(m.get_experience_gain()); @@ -245,7 +255,7 @@ fn game_has_mutable_player() { let p = Player::new("foo", player_stats); let mut g = Game::new(p); assert_eq!(g.get_player().get_name(), "foo"); - g.get_mutable_player().change_life(-1); + g.get_mutable_player().decrease_life(1); assert_eq!(g.get_player().get_life(), 20); } diff --git a/src/level.rs b/src/level.rs index 4c80f77..80c80f6 100644 --- a/src/level.rs +++ b/src/level.rs @@ -6,6 +6,7 @@ use rand::rngs::ThreadRng; #[cfg(test)] use crate::artifacts::{Chest, Potion}; use crate::artifacts::Artifact; +use crate::dungeon_slayer::do_challenge; #[cfg(test)] use crate::monster::{Orc, Rat}; use crate::monster::Monster; @@ -138,11 +139,19 @@ impl Level { if self.can_monster_move(self.monsters[index].as_ref(), dx, dy) { let (new_x, new_y) = self.monsters[index].get_position().change(dx, dy); if player.get_immutable_position().get_x() == new_x && player.get_immutable_position().get_y() == new_y { - self.monsters[index].decrease_life(1); - player.change_life(-1); - messages.insert(0, format!("{} hits you.", self.monsters[index].get_name()).to_string()); - messages.insert(0, format!("you hit {}.", self.monsters[index].get_name()).to_string()); - // if the attack did not kill the opponent, back down + // TODO monster attacks + let attack = do_challenge(self.monsters[index].get_hit()); + if attack.0 { + let mut damage = attack.1; + let defense = do_challenge(player.player_stats.get_defense()); + if defense.0 { + damage = damage.saturating_sub(defense.1); + } + messages.insert(0, format!("attack from {} successful with {} damage.", self.monsters[index].get_name(), damage).to_string()); + player.decrease_life(damage); + } else { + messages.insert(0, format!("attack from {} failed.", self.monsters[index].get_name()).to_string()); + } if !player.is_dead() { self.monsters[index].get_position().change(-dx, -dy); } diff --git a/src/monster.rs b/src/monster.rs index 8d2483b..108fe0b 100644 --- a/src/monster.rs +++ b/src/monster.rs @@ -11,6 +11,8 @@ pub trait Monster: HasPosition { // fn get_immutable_position(&self) -> &Position; fn get_experience_gain(&self) -> usize; fn get_ticks_between_steps(&self) -> u128; + fn get_defense(&self) -> u8; + fn get_hit(&self) -> u8; #[cfg(test)] fn get_life(&self) -> usize; } @@ -26,7 +28,8 @@ macro_rules! default_monster { self.life = self.life.saturating_sub(by); } fn get_ticks_between_steps(&self) -> u128 { self.ticks_between_steps } - + fn get_defense(&self) -> u8 { self.monster_stats.defense } + fn get_hit(&self) -> u8 { self.monster_stats.hit } #[cfg(test)] fn get_life(&self) -> usize { self.life } } @@ -127,7 +130,7 @@ impl Orc { color: Color::DarkGray, experience_gain: 10, ticks_between_steps: 10, - monster_stats + monster_stats, } } } @@ -162,6 +165,6 @@ fn monsters_can_die() { #[test] fn test_rat_values() { - let mut m = Rat::new_with_position(Position::new(0, 0, 0)); + let m = Rat::new_with_position(Position::new(0, 0, 0)); assert_eq!(m.monster_stats.max_life, 3); } \ No newline at end of file diff --git a/src/player.rs b/src/player.rs index 0d700e4..89cb9e6 100644 --- a/src/player.rs +++ b/src/player.rs @@ -26,8 +26,11 @@ impl Player { pub fn get_name(&self) -> String { return self.name.clone(); } - pub fn change_life(&mut self, by: i16) { - self.life = max(0, min(self.get_max_life(), self.life + by)); + pub fn decrease_life(&mut self, by: u8) { + self.life = max(0, self.life.saturating_sub(by as i16)); + } + pub fn increase_life(&mut self, by: u8) { + self.life = min(self.life.saturating_add(by as i16), self.get_max_life()); } pub fn get_life(&self) -> i16 { self.life @@ -125,11 +128,11 @@ fn test_change_life() { }, }; assert_eq!(p.get_life(), 5); - p.change_life(-2); + p.decrease_life(2); assert_eq!(p.get_life(), 3); - p.change_life(10); + p.increase_life(10); assert_eq!(p.get_life(), 10); - p.change_life(-12); + p.decrease_life(12); assert_eq!(p.get_life(), 0); }