diff options
Diffstat (limited to 'dungeon')
| -rw-r--r-- | dungeon/src/entity.rs | 18 | ||||
| -rw-r--r-- | dungeon/src/map.rs | 97 | ||||
| -rw-r--r-- | dungeon/src/pos.rs | 62 |
3 files changed, 110 insertions, 67 deletions
diff --git a/dungeon/src/entity.rs b/dungeon/src/entity.rs index badb43c..931947c 100644 --- a/dungeon/src/entity.rs +++ b/dungeon/src/entity.rs @@ -1,5 +1,7 @@ //! The `entity` module contains structures of all entities including players and enimies. +use rand::Rng; + use crate::{Direction, FPos, Floor, Pos, astar, const_pos}; /// `PLAYER_FULL_HEALTH` is the starting health of the player entity @@ -70,12 +72,13 @@ impl EnemyMoveState { None } - pub fn new_roam(starting_pos: Pos, floor: &Floor) -> Self { - let mut rand_dir = Direction::get_random_dir(); - let mut rand_tiles = rand::random_range(MIN_ROAM_DIST..=MAX_ROAM_DIST); + pub fn new_roam(starting_pos: Pos, floor: &mut Floor) -> Self { let mut loop_index = 0; loop { - if let Some(p) = starting_pos.step_by(rand_dir, rand_tiles) { + let dir = Direction::random(floor.rand()); + let dist = floor.rand().random_range(MIN_ROAM_DIST..=MIN_ROAM_DIST); + + if let Some(p) = starting_pos.step_by(dir, dist) { if !floor.get(p).is_wall() { if let Some(data) = astar::astar(starting_pos, p, floor) { let mut path = data.0; @@ -85,9 +88,6 @@ impl EnemyMoveState { } } - rand_dir = Direction::get_random_dir(); - rand_tiles = rand::random_range(MIN_ROAM_DIST..=MAX_ROAM_DIST); - // Safety check loop_index += 1; if loop_index >= 100 { @@ -186,7 +186,7 @@ impl Entity { } } - pub fn handle_movement(&mut self, player_pos: Pos, floor: &Floor, delta_time: f32) { + pub fn handle_movement(&mut self, player_pos: Pos, floor: &mut Floor, delta_time: f32) { match &self.kind { EntityKind::Zombie(move_state) => { self.zombie_movement(move_state.clone(), player_pos, delta_time, floor); @@ -201,7 +201,7 @@ impl Entity { move_state: EnemyMoveState, player_pos: Pos, delta_time: f32, - floor: &Floor, + floor: &mut Floor, ) { // Check if player is in range if !matches!(move_state, EnemyMoveState::Attack { .. }) diff --git a/dungeon/src/map.rs b/dungeon/src/map.rs index c204cac..8d9a391 100644 --- a/dungeon/src/map.rs +++ b/dungeon/src/map.rs @@ -7,6 +7,7 @@ use strum_macros::EnumIter; use std::{ cell::RefCell, + fmt::{Display, Write}, hash::{DefaultHasher, Hash, Hasher}, }; @@ -40,12 +41,14 @@ impl Tile { } /// Returns if the tile is a wall - pub fn is_wall(self) -> bool { - self == Self::Wall + #[must_use] + pub const fn is_wall(self) -> bool { + matches!(self, Self::Wall) } /// Returns if the tile is walkable - pub fn is_walkable(self) -> bool { + #[must_use] + pub const fn is_walkable(self) -> bool { matches!(self, Self::Air) } } @@ -54,6 +57,16 @@ impl Default for Tile { Self::Air } } +impl Display for Tile { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let char = match self { + Self::Wall => '#', + Self::Air => '.', + Self::Stairs => '>', + }; + f.write_char(char) + } +} /// The `Floor` type represents the current playing /// grid of the dungeon. It contains the tiles of the @@ -194,55 +207,22 @@ impl Floor { *hash } - /// Display the floor as a string for debugging - /// - /// # Examples - /// ```no_run - /// use dungeon::Floor; - /// let floor = Floor::generate(); - /// println!("{}", floor.display()); - /// ``` - #[must_use] - pub fn display(&self) -> String { - let mut output = String::new(); - for pos in Pos::values() { - // If it's the player start, show 'P' - if self.player_start == pos { - output.push('P'); - continue; - } - // Otherwise, show the tile character - let tile = self.get(pos); - let char = match tile { - Tile::Wall => '#', - Tile::Air => '.', - Tile::Stairs => '>', - }; - output.push(char); - // Newline at the end of each row - if pos.xy().0 == MAP_SIZE - 1 { - output.push('\n'); - } - } - output - } - /// Returns a random open (no wall) position pub fn random_pos(&mut self) -> Pos { loop { - let x = self.rng.random_range(0..MAP_SIZE); - let y = self.rng.random_range(0..MAP_SIZE); - let Some(pos) = Pos::new(x, y) else { - continue; - }; - if self.get(pos) != Tile::Air { + let pos = self.rand().random(); + if !self.get(pos).is_walkable() { continue; } break pos; } } -} + /// Returns the random number gen for the `Floor` + pub fn rand(&mut self) -> &mut impl Rng { + &mut self.rng + } +} impl Default for Floor { /// Returns a floor with a single set of walls on the map border fn default() -> Self { @@ -259,6 +239,34 @@ impl Default for Floor { Self::new(tiles, player_start, seed) } } +impl Display for Floor { + /// Display the floor as a string for debugging + /// + /// # Examples + /// ```no_run + /// use dungeon::Floor; + /// let floor = Floor::generate(); + /// println!("{floor}"); + /// ``` + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for pos in Pos::values() { + // If it's the player start, show 'P' + if self.player_start == pos { + f.write_char('P')?; + continue; + } + // Otherwise, show the tile character + let tile = self.get(pos); + write!(f, "{tile}")?; + // Newline at the end of each row + if pos.xy().0 == MAP_SIZE - 1 { + writeln!(f)?; + } + } + + Ok(()) + } +} /// Tests #[cfg(test)] @@ -269,8 +277,7 @@ mod tests { #[test] fn test_floor_display() { let floor = Floor::generate(); - let display = floor.display(); // Print the display for visual inspection - println!("{display}"); + println!("{floor}"); } } diff --git a/dungeon/src/pos.rs b/dungeon/src/pos.rs index f6c5eb4..0cad387 100644 --- a/dungeon/src/pos.rs +++ b/dungeon/src/pos.rs @@ -53,8 +53,15 @@ impl Direction { Self::iter() } - pub fn get_random_dir() -> Self { - rand::random() + /// Returns a random direction provided with a rng + #[must_use] + pub fn random<R: Rng + ?Sized>(rng: &mut R) -> Self { + match rng.random_range(0..4) { + 0 => Self::North, + 1 => Self::South, + 2 => Self::East, + _ => Self::West, + } } } impl Display for Direction { @@ -67,16 +74,9 @@ impl Display for Direction { } } } - impl Distribution<Direction> for StandardUniform { fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Direction { - // match rng.gen_range(0, 3) { // rand 0.5, 0.6, 0.7 - match rng.random_range(0..=3) { - 0 => Direction::North, - 1 => Direction::East, - 2 => Direction::South, - _ => Direction::West, - } + Direction::random(rng) } } @@ -314,7 +314,18 @@ impl Pos { } /// Returns the manhattan distance between `self` and `other` - pub fn manhattan(self, other: Self) -> u16 { + /// + /// # Examples + /// + /// ``` + /// use dungeon::Pos; + /// + /// let pos1 = Pos::new(3, 6).unwrap(); + /// let pos2 = Pos::new(5, 1).unwrap(); + /// assert_eq!(pos1.manhattan(pos2), 7); + /// ``` + #[must_use] + pub const fn manhattan(self, other: Self) -> u16 { let abs_diff = Self::abs_diff(self, other); abs_diff.0 + abs_diff.1 } @@ -352,6 +363,14 @@ impl Pos { pub fn values() -> impl Iterator<Item = Self> { (0..MAP_SIZE).flat_map(|y| (0..MAP_SIZE).filter_map(move |x| Self::new(x, y))) } + + /// Returns a random position provided with a rng + #[must_use] + pub fn random<R: Rng + ?Sized>(rng: &mut R) -> Self { + let x = rng.random_range(0..MAP_SIZE); + let y = rng.random_range(0..MAP_SIZE); + Self(x, y) + } } impl Default for Pos { /// Returns a default postion at the origin (0,0) @@ -375,6 +394,11 @@ impl TryFrom<usize> for Pos { Self::from_idx(value).ok_or(()) } } +impl Distribution<Pos> for StandardUniform { + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Pos { + Pos::random(rng) + } +} /// The `FPos` type represents a floating 2D (temp) position. /// @@ -512,13 +536,25 @@ impl FPos { /// ``` /// #[must_use] - pub fn abs_diff(self, other: Self) -> Self { + pub const fn abs_diff(self, other: Self) -> Self { let x = (self.0 - other.0).abs(); let y = (self.1 - other.1).abs(); Self(x, y) } - pub fn manhattan(self, other: Self) -> f32 { + /// Returns the manhattan distance between `self` and `other` + /// + /// # Examples + /// + /// ``` + /// use dungeon::FPos; + /// + /// let fpos1 = FPos::new(1.0, 0.0); + /// let fpos2 = FPos::new(1.2, 1.3); + /// assert_eq!(fpos1.manhattan(fpos2), 1.5); + /// ``` + #[must_use] + pub const fn manhattan(self, other: Self) -> f32 { let abs_diff = Self::abs_diff(self, other); abs_diff.0 + abs_diff.1 } |