improve combat system
This commit is contained in:
parent
efc4cdd363
commit
cefb0cc5f2
28
src/game.rs
28
src/game.rs
|
@ -49,8 +49,10 @@ impl Game {
|
|||
fn player_reached_goal(&mut self) -> bool {
|
||||
match self.next_element(0, 0) {
|
||||
None => {}
|
||||
Some(a) => if a == StructureElement::End {
|
||||
return true
|
||||
Some(a) => {
|
||||
if a == StructureElement::End {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
false
|
||||
|
@ -153,6 +155,7 @@ impl Game {
|
|||
.discover(&Position::new(player_level, new_x, new_y));
|
||||
(-dx, -dy)
|
||||
}
|
||||
|
||||
pub fn player_fights_monster(&mut self) -> bool {
|
||||
let player_pos = &self.player.get_immutable_position();
|
||||
let player_level = player_pos.get_level();
|
||||
|
@ -162,12 +165,21 @@ impl Game {
|
|||
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());
|
||||
|
||||
let monster_dmg = m.damage() as i16;
|
||||
let player_dmg = self.player.damage();
|
||||
self.player.change_life(-monster_dmg);
|
||||
m.decrease_life(player_dmg);
|
||||
|
||||
// inform player
|
||||
self.messages.insert(
|
||||
0,
|
||||
format!("{} hits you for {} damage.", m.get_name(), monster_dmg).to_string(),
|
||||
);
|
||||
self.messages.insert(
|
||||
0,
|
||||
format!("you hit {} for {} damage.", m.get_name(), player_dmg).to_string(),
|
||||
);
|
||||
// monster died, player gains experience
|
||||
if m.is_dead() {
|
||||
self.player.gain_experience(m.get_experience_gain());
|
||||
|
|
28
src/level.rs
28
src/level.rs
|
@ -48,11 +48,7 @@ impl Level {
|
|||
&mut self,
|
||||
x: i16,
|
||||
y: i16,
|
||||
) -> (
|
||||
Option<StructureElement>,
|
||||
PossibleMonster,
|
||||
PossibleArtifact
|
||||
) {
|
||||
) -> (Option<StructureElement>, PossibleMonster, PossibleArtifact) {
|
||||
if x < 0 || y < 0 {
|
||||
return (None, None, None);
|
||||
}
|
||||
|
@ -151,15 +147,29 @@ impl Level {
|
|||
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);
|
||||
// TODO handle fight between monster and player
|
||||
let monster_dmg = self.monsters[index].damage() as i16;
|
||||
let player_dmg = player.damage();
|
||||
self.monsters[index].decrease_life(player_dmg);
|
||||
player.change_life(-monster_dmg);
|
||||
|
||||
messages.insert(
|
||||
0,
|
||||
format!("{} hits you.", self.monsters[index].get_name()).to_string(),
|
||||
format!(
|
||||
"{} hits you for {} damage.",
|
||||
self.monsters[index].get_name(),
|
||||
monster_dmg
|
||||
)
|
||||
.to_string(),
|
||||
);
|
||||
messages.insert(
|
||||
0,
|
||||
format!("you hit {}.", self.monsters[index].get_name()).to_string(),
|
||||
format!(
|
||||
"you hit {} for {} damage.",
|
||||
self.monsters[index].get_name(),
|
||||
player_dmg
|
||||
)
|
||||
.to_string(),
|
||||
);
|
||||
// if the attack did not kill the opponent, back down
|
||||
if !player.is_dead() {
|
||||
|
|
|
@ -11,7 +11,7 @@ use rand::Rng;
|
|||
|
||||
use crate::artifacts::{Artifact, Chest, Potion};
|
||||
use crate::level::{Level, StructureElement};
|
||||
use crate::monster::{Monster, Orc, Rat};
|
||||
use crate::monster::{Monster, Orc, Rat, Snake};
|
||||
use crate::position::Position;
|
||||
|
||||
const ROOMS_VERTICAL: usize = 7;
|
||||
|
@ -301,11 +301,16 @@ impl LevelGenerator {
|
|||
let t_y = top + room.offset_y + rng.gen_range(0..room.height);
|
||||
// TODO randomize enemies here
|
||||
match rng.gen_range(1..=100) {
|
||||
1..=50 => {
|
||||
1..=30 => {
|
||||
enemies.push(Box::new(Orc::new_with_position(Position::new(
|
||||
self.level, t_x, t_y,
|
||||
))));
|
||||
}
|
||||
31..=60 => {
|
||||
enemies.push(Box::new(Snake::new_with_position(Position::new(
|
||||
self.level, t_x, t_y,
|
||||
))));
|
||||
}
|
||||
_ => {
|
||||
enemies.push(Box::new(Rat::new_with_position(Position::new(
|
||||
self.level, t_x, t_y,
|
||||
|
|
|
@ -35,7 +35,7 @@ pub const FRAME_LENGTH: u64 = 100;
|
|||
|
||||
//
|
||||
fn main() -> Result<()> {
|
||||
let mut game = Game::new(Player::new(realname().as_str(), 10));
|
||||
let mut game = Game::new(Player::new(realname().as_str(), 30));
|
||||
|
||||
stdout().execute(EnterAlternateScreen)?;
|
||||
enable_raw_mode()?;
|
||||
|
@ -108,7 +108,7 @@ fn main() -> Result<()> {
|
|||
game.get_player().get_max_life(),
|
||||
game.get_player().get_experience(),
|
||||
game.get_player().get_gold(),
|
||||
game.get_player().get_immutable_position().get_level()
|
||||
game.get_player().get_immutable_position().get_level()+1
|
||||
))
|
||||
.block(block)
|
||||
.wrap(Wrap { trim: true }),
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use std::ops::RangeInclusive;
|
||||
|
||||
use rand::Rng;
|
||||
use ratatui::prelude::Color;
|
||||
|
||||
use crate::position::{HasPosition, Position};
|
||||
|
@ -12,10 +15,21 @@ pub trait Monster: HasPosition {
|
|||
fn get_ticks_between_steps(&self) -> u128;
|
||||
#[cfg(test)]
|
||||
fn get_life(&self) -> usize;
|
||||
fn damage(&self) -> usize;
|
||||
}
|
||||
|
||||
macro_rules! default_monster {
|
||||
($($t:ty),+ $(,)?) => ($(
|
||||
macro_rules! create_monster {
|
||||
($($t:ident),+ $(,)?) => ($(
|
||||
pub struct $t {
|
||||
name: String,
|
||||
life: usize,
|
||||
position: Position,
|
||||
symbol: String,
|
||||
color: Color,
|
||||
experience_gain: usize,
|
||||
ticks_between_steps: u128,
|
||||
damage_range: RangeInclusive<usize>,
|
||||
}
|
||||
impl Monster for $t {
|
||||
fn get_name(&self) -> &str { &self.name }
|
||||
fn is_dead(&self) -> bool { self.life <= 0 }
|
||||
|
@ -25,6 +39,7 @@ macro_rules! default_monster {
|
|||
self.life = self.life.saturating_sub(by);
|
||||
}
|
||||
fn get_ticks_between_steps(&self) -> u128 { self.ticks_between_steps }
|
||||
fn damage(&self) -> usize { rand::thread_rng().gen_range(self.damage_range.clone()) }
|
||||
|
||||
#[cfg(test)]
|
||||
fn get_life(&self) -> usize { self.life }
|
||||
|
@ -40,16 +55,6 @@ macro_rules! default_monster {
|
|||
)+)
|
||||
}
|
||||
|
||||
pub struct Rat {
|
||||
name: String,
|
||||
life: usize,
|
||||
position: Position,
|
||||
symbol: String,
|
||||
color: Color,
|
||||
experience_gain: usize,
|
||||
ticks_between_steps: u128,
|
||||
}
|
||||
|
||||
impl Rat {
|
||||
pub fn new_with_position(position: Position) -> Self {
|
||||
Self {
|
||||
|
@ -60,20 +65,11 @@ impl Rat {
|
|||
color: Color::Black,
|
||||
experience_gain: 5,
|
||||
ticks_between_steps: 5,
|
||||
damage_range: 1..=2,
|
||||
}
|
||||
}
|
||||
}
|
||||
default_monster!(Rat);
|
||||
|
||||
pub struct Orc {
|
||||
name: String,
|
||||
life: usize,
|
||||
position: Position,
|
||||
symbol: String,
|
||||
color: Color,
|
||||
experience_gain: usize,
|
||||
ticks_between_steps: u128,
|
||||
}
|
||||
create_monster!(Rat);
|
||||
|
||||
impl Orc {
|
||||
pub fn new_with_position(position: Position) -> Self {
|
||||
|
@ -85,11 +81,27 @@ impl Orc {
|
|||
color: Color::DarkGray,
|
||||
experience_gain: 10,
|
||||
ticks_between_steps: 10,
|
||||
damage_range: 2..=3,
|
||||
}
|
||||
}
|
||||
}
|
||||
create_monster!(Orc);
|
||||
|
||||
default_monster!(Orc);
|
||||
impl Snake {
|
||||
pub fn new_with_position(position: Position) -> Self {
|
||||
Self {
|
||||
name: "snake".to_string(),
|
||||
life: 3,
|
||||
position,
|
||||
symbol: String::from("S"),
|
||||
color: Color::DarkGray,
|
||||
experience_gain: 10,
|
||||
ticks_between_steps: 20,
|
||||
damage_range: 1..=4,
|
||||
}
|
||||
}
|
||||
}
|
||||
create_monster!(Snake);
|
||||
|
||||
#[test]
|
||||
fn monsters_can_move() {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use rand::Rng;
|
||||
use std::cmp::{max, min};
|
||||
|
||||
use crate::position::{HasPosition, Position};
|
||||
|
@ -60,6 +61,10 @@ impl Player {
|
|||
pub fn get_experience(&self) -> usize {
|
||||
self.experience
|
||||
}
|
||||
|
||||
pub fn damage(&self) -> usize {
|
||||
rand::thread_rng().gen_range(1..4)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasPosition for Player {
|
||||
|
|
Loading…
Reference in New Issue