diff options
| author | Yusuf Elsharawy <yusufse-2004@hotmail.com> | 2025-11-10 20:05:46 +0000 |
|---|---|---|
| committer | Yusuf Elsharawy <yusufse-2004@hotmail.com> | 2025-11-10 20:10:38 +0000 |
| commit | b1c7a57e72ab7359191249d76ec84d6cae524e2a (patch) | |
| tree | c533911f87b1641d893de5ab68ed05c2558b1cba /dungeon/src | |
| parent | graphics: decouple sdl & wayland (diff) | |
| download | DungeonCrawl-b1c7a57e72ab7359191249d76ec84d6cae524e2a.tar.gz DungeonCrawl-b1c7a57e72ab7359191249d76ec84d6cae524e2a.tar.bz2 DungeonCrawl-b1c7a57e72ab7359191249d76ec84d6cae524e2a.zip | |
Refactored some code, implemented player movement
Diffstat (limited to 'dungeon/src')
| -rw-r--r-- | dungeon/src/entity.rs | 64 | ||||
| -rw-r--r-- | dungeon/src/lib.rs | 47 | ||||
| -rw-r--r-- | dungeon/src/player_input.rs | 16 | ||||
| -rw-r--r-- | dungeon/src/pos.rs | 160 |
4 files changed, 245 insertions, 42 deletions
diff --git a/dungeon/src/entity.rs b/dungeon/src/entity.rs index c84fc42..762355e 100644 --- a/dungeon/src/entity.rs +++ b/dungeon/src/entity.rs @@ -35,7 +35,7 @@ impl EntityKind { // Tiles/s pub fn move_speed(&self) -> f32 { match &self { - Self::Player => 2., + Self::Player => 5., Self::Zombie(_) => 4., _ => 0., } @@ -252,50 +252,38 @@ impl Entity { } fn movement_helper( - entity: &mut Self, - next_move: Pos, + &mut self, + mut next_move: Pos, moves: &mut Vec<Pos>, delta_time: f32, ) { - if entity.pos.y() > next_move.y() { - entity.move_by_dir(Direction::North, delta_time); - if entity.fpos.y() <= next_move.y() as f32 { - if let Some(p) = moves.pop() { - entity.pos = p; - entity.fpos = FPos::from_pos(p); - } - } - } else if entity.pos.y() < next_move.y() { - entity.move_by_dir(Direction::South, delta_time); - if entity.fpos.y() >= next_move.y() as f32 { - if let Some(p) = moves.pop() { - entity.pos = p; - entity.fpos = FPos::from_pos(p); - } - } - } else if entity.pos.x() < next_move.x() { - entity.move_by_dir(Direction::East, delta_time); - if entity.fpos.x() >= next_move.x() as f32 { - if let Some(p) = moves.pop() { - entity.pos = p; - entity.fpos = FPos::from_pos(p); - } - } - } else { - entity.move_by_dir(Direction::West, delta_time); - if entity.fpos.x() <= next_move.x() as f32 { - if let Some(p) = moves.pop() { - entity.pos = p; - entity.fpos = FPos::from_pos(p); - } + let mut move_distance = self.kind.move_speed() * delta_time; + // having this be a loop is a *little* unnecessary, + // but is technically more correct if the entity is fast enough + // to move more than one tile in a single frame + loop { + move_distance -= self + .fpos + .move_towards_manhattan(FPos::from(next_move), move_distance); + if move_distance == 0.0 { + // can't move any further + break; } - }; + // otherwise, we reached `next_move`, set position & pop from vec + self.pos = next_move; + let _ = moves.pop(); + + // there is likely more distance to travel + let Some(last) = moves.last() else { break }; + next_move = *last; + } } } /// The `Player` type represents the main player entity #[derive(Clone, Debug, PartialEq)] pub struct Player { + pub moving_to: Option<Pos>, pub entity: Entity, pub inventory: Vec<Item>, } @@ -313,7 +301,11 @@ impl Player { pub fn new(pos: Pos) -> Self { let entity = Entity::player(pos); let inventory = vec![]; - Self { entity, inventory } + Self { + entity, + inventory, + moving_to: None, + } } } impl Default for Player { diff --git a/dungeon/src/lib.rs b/dungeon/src/lib.rs index cfd2fbe..399807c 100644 --- a/dungeon/src/lib.rs +++ b/dungeon/src/lib.rs @@ -5,11 +5,13 @@ pub mod astar; pub mod bsp; pub mod entity; pub mod map; +pub mod player_input; pub mod pos; pub use bsp::*; pub use entity::*; pub use map::*; +pub use player_input::*; pub use pos::*; /// The `Dungeon` type represents the game state of the @@ -55,6 +57,51 @@ impl Dungeon { pub fn camera(&self) -> FPos { self.player.entity.fpos } + + pub fn update(&mut self, player_input: PlayerInput, delta_time: f32) { + self.act_player(player_input, delta_time); + self.act_non_players(delta_time); + } + + fn act_player(&mut self, player_input: PlayerInput, delta_time: f32) { + let mut move_distance = self.player.entity.kind.move_speed() * delta_time; + // having this be a loop is a *little* unnecessary, + // but is technically more correct if the entity is fast enough + // to move more than one tile in a single frame + // (plus, it was easier to write if i thought of this like a state machine) + loop { + match (self.player.moving_to, player_input.direction) { + (Some(pos), _) => { + move_distance -= self + .player + .entity + .fpos + .move_towards_manhattan(pos.into(), move_distance); + if move_distance == 0.0 { + // can't move any further + break; + } + // otherwise, reached `pos` + self.player.entity.pos = pos; + self.player.moving_to = None; + // continue moving + } + (None, Some(dir)) => { + // set direction & find out next position + self.player.entity.dir = dir; + self.player.moving_to = self.player.entity.pos.step(dir); + } + (None, None) => break, + } + } + // TODO: reuse a similar structure across all entities + } + + fn act_non_players(&mut self, delta_time: f32) { + for enemy in self.enemies.iter_mut() { + enemy.handle_movement(self.player.entity.pos, &mut self.floor, delta_time); + } + } } impl Default for Dungeon { fn default() -> Self { diff --git a/dungeon/src/player_input.rs b/dungeon/src/player_input.rs new file mode 100644 index 0000000..643b014 --- /dev/null +++ b/dungeon/src/player_input.rs @@ -0,0 +1,16 @@ +use crate::pos::*; + +/// Carries information on the player's inputs. +/// Allows the game to retrieve player input commands, +/// without tightly binding to a specific GUI or keybinds. +/// This way, game logic can focus on "what the inputs do" +/// as opposed to "what do the key presses mean". +#[derive(Copy, Clone, Default, Debug)] +pub struct PlayerInput { + /// The direction that the player wants to move in. + /// The creator is responsible for resolving to just one direction + /// (eg if the player is holding multiple keys at once, + /// or a joystick) + pub direction: Option<Direction>, + // other player actions are to be added later +} diff --git a/dungeon/src/pos.rs b/dungeon/src/pos.rs index e3365ef..623d914 100644 --- a/dungeon/src/pos.rs +++ b/dungeon/src/pos.rs @@ -12,7 +12,7 @@ use rand::{ use std::{ fmt::Display, - ops::{AddAssign, SubAssign}, + ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}, }; use crate::{MAP_SIZE_USIZE, map::MAP_SIZE}; @@ -499,6 +499,18 @@ impl FPos { self.1 } + /// Returns the component-wise absolute value of `FPos`. + #[must_use] + pub const fn abs(&self) -> Self { + Self(self.0.abs(), self.1.abs()) + } + + /// Returns the euclidean magnitude of `FPos`. + #[must_use] + pub fn magnitude(&self) -> f32 { + (self.0 * self.0 + self.1 * self.1).sqrt() + } + /// Steps `FPos` a given floating amount in the `Direction` `dir`. /// /// Returns `None` if the floating position wraps. @@ -534,6 +546,90 @@ impl FPos { Some(Self(x, y)) } + /// Moves from one position towards another by at most some euclidean distance. + /// Returns the distance moved. + /// + /// # Examples + /// Note: some assertions may be commented out, as they would fail due to floating-point error. + /// + /// ``` + /// use dungeon::FPos; + /// + /// let mut fpos1 = FPos::new(0.0,0.0); + /// let fpos2 = FPos::new(0.0,1.0); + /// let moved = fpos1.move_towards(fpos2, 0.6); + /// assert_eq!(moved, 0.6); + /// assert_eq!(fpos1, FPos::new(0.0, 0.6)); + /// let moved = fpos1.move_towards(fpos2, 0.6); + /// // assert_eq!(moved, 0.4); + /// assert_eq!(fpos1, FPos::new(0.0, 1.0)); + /// ``` + /// + pub fn move_towards(&mut self, goal: Self, by: f32) -> f32 { + let diff = goal - *self; + let dist = diff.magnitude(); + if by >= dist { + *self = goal; + dist + } else { + *self += diff * by / dist; + by + } + } + /// Equivalent to `move_towards` but returns a copy, discarding the distance moved. + /// + /// # Examples + /// + /// ``` + /// use dungeon::FPos; + /// + /// let fpos1 = FPos::new(0.0,0.0); + /// let fpos2 = FPos::new(0.0,1.0); + /// let fpos1 = fpos1.moved_towards(fpos2, 0.6); + /// assert_eq!(fpos1, FPos::new(0.0, 0.6)); + /// let fpos1 = fpos1.moved_towards(fpos2, 0.6); + /// assert_eq!(fpos1, FPos::new(0.0, 1.0)); + /// ``` + #[must_use] + pub fn moved_towards(mut self, goal: Self, by: f32) -> Self { + self.move_towards(goal, by); + self + } + + /// Moves from one position towards another by at most some manhattan distance. + /// Prefers vertical movement over horizontal movement. + /// Returns the distance moved. + /// + /// # Examples + /// Note: some assertions may be commented out, as they would fail due to floating-point error. + /// + /// ``` + /// use dungeon::FPos; + /// + /// let mut fpos1 = FPos::new(0.0,0.0); + /// let fpos2 = FPos::new(1.0,1.0); + /// let moved = fpos1.move_towards_manhattan(fpos2, 0.6); + /// assert_eq!(moved, 0.6); + /// assert_eq!(fpos1, FPos::new(0.0, 0.6)); + /// let moved = fpos1.move_towards_manhattan(fpos2, 0.6); + /// assert_eq!(moved, 0.6); + /// // assert_eq!(fpos1, FPos::new(0.2, 1.0)); + /// let moved = fpos1.move_towards_manhattan(fpos2, 0.6); + /// assert_eq!(moved, 0.6); + /// // assert_eq!(fpos1, FPos::new(0.8, 1.0)); + /// let moved = fpos1.move_towards_manhattan(fpos2, 0.6); + /// // assert_eq!(moved, 0.2); + /// assert_eq!(fpos1, FPos::new(1.0, 1.0)); + /// ``` + /// + pub fn move_towards_manhattan(&mut self, goal: Self, by: f32) -> f32 { + let vert_checkpoint = Self(self.0, goal.1); + let mut rem = by; + rem -= self.move_towards(vert_checkpoint, rem); + rem -= self.move_towards(goal, rem); + by - rem + } + /// Computes the absolute difference between to positions /// /// # Examples @@ -548,10 +644,8 @@ impl FPos { /// ``` /// #[must_use] - 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 abs_diff(self, other: Self) -> Self { + (self - other).abs() } /// Returns the manhattan distance between `self` and `other` @@ -566,11 +660,65 @@ impl FPos { /// assert_eq!(fpos1.manhattan(fpos2), 1.5); /// ``` #[must_use] - pub const fn manhattan(self, other: Self) -> f32 { + pub fn manhattan(self, other: Self) -> f32 { let abs_diff = Self::abs_diff(self, other); abs_diff.0 + abs_diff.1 } } + +impl AddAssign<Self> for FPos { + fn add_assign(&mut self, rhs: Self) { + self.0 += rhs.0; + self.1 += rhs.1; + } +} +impl Add<Self> for FPos { + type Output = Self; + fn add(mut self, rhs: Self) -> Self { + self += rhs; + self + } +} +impl SubAssign<Self> for FPos { + fn sub_assign(&mut self, rhs: Self) { + self.0 -= rhs.0; + self.1 -= rhs.1; + } +} +impl Sub<Self> for FPos { + type Output = Self; + fn sub(mut self, rhs: Self) -> Self { + self -= rhs; + self + } +} +impl MulAssign<f32> for FPos { + fn mul_assign(&mut self, rhs: f32) { + self.0 *= rhs; + self.1 *= rhs; + } +} +impl Mul<f32> for FPos { + type Output = Self; + fn mul(mut self, rhs: f32) -> Self { + self *= rhs; + self + } +} +impl DivAssign<f32> for FPos { + fn div_assign(&mut self, rhs: f32) { + self.0 /= rhs; + self.1 /= rhs; + } +} +impl Div<f32> for FPos { + type Output = Self; + fn div(mut self, rhs: f32) -> Self { + self /= rhs; + self + } +} + impl Default for FPos { /// Returns a default postion at the origin (0,0) /// |