diff --git a/src/game.rs b/src/game.rs index b134a1c..ce07c88 100644 --- a/src/game.rs +++ b/src/game.rs @@ -179,11 +179,11 @@ impl Game { } } /// updates the player's current level. This will remove collected artifacts and dead monsters. - pub fn update_level(&mut self) { + pub fn update_level(&mut self, ticks: u128) { let player_pos = &self.player.get_immutable_position(); let player_level = player_pos.get_level(); let level = &mut self.levels[player_level]; - level.update(); + level.update(ticks, &mut self.player); } } diff --git a/src/level.rs b/src/level.rs index 7c7accc..a83caf6 100644 --- a/src/level.rs +++ b/src/level.rs @@ -9,6 +9,8 @@ use crate::monster::Monster; use crate::player::Player; use crate::position::HasPosition; use crate::position::Position; +use rand::Rng; +use rand::rngs::ThreadRng; pub const LEVEL_WIDTH: usize = 50; pub const LEVEL_HEIGHT: usize = 25; @@ -34,6 +36,7 @@ pub struct Level { pub(crate) start: (usize, usize), /// the position of the end in the level (either stair down or end point) pub(crate) end: (usize, usize), + pub(crate) rng: ThreadRng, } impl Level { @@ -108,7 +111,7 @@ impl Level { } } } - pub fn update(&mut self) { + pub fn update(&mut self, ticks: u128, player: &mut Player) { for (index, a) in &mut self.artifacts.iter().enumerate() { if a.was_collected() { self.artifacts.remove(index); @@ -122,17 +125,43 @@ impl Level { } } for index in 0..self.monsters.len() { - if self.can_monster_move(self.monsters[index].as_ref(), 0, 1) { - self.monsters[index].get_position().change(0, 1); - } else if self.can_monster_move(self.monsters[index].as_ref(), 0, -1) { - self.monsters[index].get_position().change(0, -1); + if ticks % self.monsters[index].get_ticks_between_steps() != 0 { + continue; + } + loop { + // calculate the direction the monster will try to walk + let (dx, dy) = match self.rng.gen_range(0..5) { + 1 => { (1, 0) } + 2 => { (-1, 0) } + 3 => { (0, 1) } + 4 => { (0, -1) } + _ => { (0, 0) } + }; + 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); + } + break; + } } } } + pub fn can_monster_move(&self, agent: &dyn Monster, dx: i16, dy: i16) -> bool { let agent_pos = agent.get_immutable_position(); let new_x: usize = (agent_pos.get_x() as i16 + dx) as usize; let new_y: usize = (agent_pos.get_y() as i16 + dy) as usize; + + if new_x < 0 || new_y < 0 || new_x >= LEVEL_WIDTH || new_y >= LEVEL_HEIGHT { + return false; + } + + for index in 0..self.monsters.len() { + let pos = self.monsters[index].get_immutable_position(); + if pos.get_x() == new_x && pos.get_y() == new_y { return false; } + } self.structure[new_x][new_y] != StructureElement::Wall } pub fn can_player_move(&self, agent: &Player, dx: i16, dy: i16) -> bool { @@ -157,6 +186,7 @@ impl Level { artifacts: Vec::with_capacity(10), start: (0, 0), end: (0, 0), + rng: rand::thread_rng(), } } #[cfg(test)] diff --git a/src/level_generator.rs b/src/level_generator.rs index ddf5454..a894d89 100644 --- a/src/level_generator.rs +++ b/src/level_generator.rs @@ -344,6 +344,7 @@ impl LevelGenerator { artifacts, start: (start_x, start_y), end: (end_x, end_y), + rng: rand::thread_rng(), } } } diff --git a/src/main.rs b/src/main.rs index 8e74c54..71d0a95 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ use std::io::Result; use std::io::stdout; +use std::time::Instant; use crossterm::{ event::{self, KeyCode, KeyEventKind}, @@ -30,6 +31,9 @@ mod level_generator; mod artifacts; mod monster; +/// length of a game frame in ms +pub const FRAME_LENGTH: u64 = 100; + // fn main() -> Result<()> { let mut game = Game::new(Player::new(realname().as_str(), 10)); @@ -39,6 +43,8 @@ fn main() -> Result<()> { let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?; terminal.clear()?; + let start_time = Instant::now(); + let mut ticks = 0; loop { terminal.draw(|frame| { let mut area = frame.size(); @@ -57,7 +63,7 @@ fn main() -> Result<()> { .block(block) .wrap(Wrap { trim: true }); frame.render_widget(paragraph, area); - return + return; } if area.width > 80 { @@ -96,7 +102,7 @@ fn main() -> Result<()> { stats_area, ); })?; - if event::poll(std::time::Duration::from_millis(100))? { + if event::poll(std::time::Duration::from_millis(FRAME_LENGTH))? { if let event::Event::Key(key) = event::read()? { if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') { break; @@ -117,11 +123,13 @@ fn main() -> Result<()> { } } } - game.update_level(); + game.update_level(ticks); if game.get_game_state() != GameState::RUNNING { break; } + ticks += 1; } + let playtime = start_time.elapsed(); loop { let _ = terminal.draw(|frame| { let mut area = frame.size(); @@ -151,6 +159,7 @@ fn main() -> Result<()> { }; text += format!("\nYou gained {} experience.", game.get_player().get_experience()).as_str(); text += format!("\nYou collected {} gold.", game.get_player().get_gold()).as_str(); + text += format!("\nYou played {} seconds.", playtime.as_secs()).as_str(); let paragraph = Paragraph::new(text).block(block).wrap(Wrap { trim: true }); frame.render_widget(paragraph, area); }); diff --git a/src/monster.rs b/src/monster.rs index 2b2fc7f..7ab2120 100644 --- a/src/monster.rs +++ b/src/monster.rs @@ -8,8 +8,7 @@ pub trait Monster: HasPosition { fn decrease_life(&mut self, by: usize); // fn get_immutable_position(&self) -> &Position; fn get_experience_gain(&self) -> usize; - // #[cfg(test)] - // fn get_position(&mut self) -> &mut Position; + fn get_ticks_between_steps(&self) -> u128; #[cfg(test)] fn get_life(&self) -> usize; } @@ -23,6 +22,8 @@ macro_rules! default_monster { fn decrease_life(&mut self, by: usize) { self.life = self.life.saturating_sub(by); } + fn get_ticks_between_steps(&self) -> u128 { self.ticks_between_steps } + #[cfg(test)] fn get_life(&self) -> usize { self.life } } @@ -43,6 +44,7 @@ pub struct Rat { symbol: String, color: Color, experience_gain: usize, + ticks_between_steps: u128, } impl Rat { @@ -53,6 +55,7 @@ impl Rat { symbol: String::from("R"), color: Color::Black, experience_gain: 5, + ticks_between_steps: 5, } } } @@ -64,6 +67,7 @@ pub struct Orc { symbol: String, color: Color, experience_gain: usize, + ticks_between_steps: u128, } impl Orc { @@ -74,6 +78,7 @@ impl Orc { symbol: String::from("O"), color: Color::DarkGray, experience_gain: 10, + ticks_between_steps: 10, } } }