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