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