Player and monster attacks follow the dungeon slayer rules
This commit is contained in:
parent
0431d872e4
commit
cad08bd300
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
22
src/game.rs
22
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);
|
||||
}
|
||||
|
||||
|
|
19
src/level.rs
19
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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue