diff options
| -rw-r--r-- | dungeon/src/entity.rs | 132 | ||||
| -rw-r--r-- | dungeon/src/lib.rs | 12 | ||||
| -rw-r--r-- | dungeon/src/map.rs | 26 | ||||
| -rw-r--r-- | graphics/src/render.rs | 2 |
4 files changed, 141 insertions, 31 deletions
diff --git a/dungeon/src/entity.rs b/dungeon/src/entity.rs index 3a8f131..22d66e0 100644 --- a/dungeon/src/entity.rs +++ b/dungeon/src/entity.rs @@ -1,6 +1,9 @@ //! The `entity` module contains structures of all entities including players and enimies. -use std::mem::take; +use std::{ + mem::take, + time::{Duration, Instant}, +}; use rand::Rng; @@ -12,9 +15,6 @@ use crate::{ rng::DungeonRng, }; -/// `PLAYER_FULL_HEALTH` is the starting health of the player entity -pub const PLAYER_FULL_HEALTH: u32 = 10; - /// `PLAYER_INVENTORY_SIZE` is the maximum size of the inventory pub const PLAYER_INVENTORY_SIZE: u16 = 5; @@ -25,9 +25,6 @@ pub const PLAYER_INVENTORY_SIZE_USIZE: usize = PLAYER_INVENTORY_SIZE as usize; pub const MIN_ROAM_DIST: u16 = 1; pub const MAX_ROAM_DIST: u16 = 4; -/// 'ZOMBIE_HEALTH' is the starting health of a zombie enemy -pub const ZOMBIE_HEALTH: u32 = 5; - /// 'ENEMY_VISION_RADIUS' is the range used to determine if the player is seen by an enemy pub const ENEMY_VISION_RADIUS: u16 = 10; @@ -37,9 +34,67 @@ pub const IDLE_WAIT: f32 = 1.; /// The `Item` type represents any item an entity may be using #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum Item { - Potion { heal: u32 }, - Weapon { atack: u32 }, - Armor { defense: u32 }, + /// Heals health when used + HeartFragment, + /// Heals health when used + HealthPotion, + /// Increases speed temporarily + SpeedPotion, + /// Blows up a portion of the map + Bomb, + /// Blows up a larget portion of the map + LargeBomb, +} +impl Item { + pub fn consume(self, dungeon: &mut Dungeon) { + match self { + Self::HeartFragment => { + dungeon.player.entity.heal(1); + } + Self::HealthPotion => { + dungeon.player.entity.heal(3); + } + Self::SpeedPotion => { + dungeon.player.potion_timer = + Instant::now().checked_add(Duration::from_secs(5)); + dungeon.player.entity.speed = dungeon.player.entity.speed.inc(); + } + Self::Bomb => { + dungeon.floor.explode(dungeon.player.entity.pos, 1); + } + Self::LargeBomb => { + dungeon.floor.explode(dungeon.player.entity.pos, 2); + } + } + } +} + +/// Different speed entities can move +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum EntitySpeed { + Stopped = 0, + Slow = 2, + Normal = 3, + Fast = 4, +} +impl EntitySpeed { + /// Returns speed for this current time slice + #[must_use] + pub const fn per_frame(self, delta_time: f32) -> f32 { + (self as u8) as f32 * delta_time + } + + /// Increment to the next speed + #[must_use] + pub const fn inc(self) -> Self { + match self { + Self::Stopped => Self::Stopped, + Self::Slow => Self::Normal, + Self::Normal => Self::Fast, + Self::Fast => Self::Fast, + } + } } /// The `EntityKind` represents what kind of entity this is. @@ -54,11 +109,20 @@ pub enum EntityKind { } impl EntityKind { /// Returns the move speed value for this type of entity in tiles/s - pub const fn move_speed(&self) -> f32 { + pub const fn initial_speed(&self) -> EntitySpeed { match &self { - Self::Player => 3.5, - Self::Zombie(_) => 2.0, - _ => 0.0, + Self::Player => EntitySpeed::Normal, + Self::Zombie(_) => EntitySpeed::Slow, + _ => EntitySpeed::Stopped, + } + } + + /// Returns the known max/initial health + pub const fn initial_health(&self) -> u32 { + match self { + Self::Player => 10, + Self::Zombie(_) => 5, + _ => 0, } } } @@ -150,7 +214,9 @@ pub struct Entity { /// Which kind this entity is (along with entity kind specific data) pub kind: EntityKind, /// The amount of health this entity has (None if this Entity does not use health) - pub health: Option<u32>, + pub health: u32, + /// The current movement speed + pub speed: EntitySpeed, /// The fixed grid position we are moving to pub moving_to: Option<Pos>, } @@ -165,23 +231,20 @@ impl Entity { /// let pos = Pos::new(0, 0).unwrap(); /// let dir = Direction::North; /// let kind = EntityKind::Player; - /// let health = Some(10); - /// let entity = Entity::new(pos, dir, kind, health); + /// let entity = Entity::new(pos, dir, kind); /// ``` #[must_use] - pub const fn new( - pos: Pos, - dir: Direction, - kind: EntityKind, - health: Option<u32>, - ) -> Self { + pub const fn new(pos: Pos, dir: Direction, kind: EntityKind) -> Self { let fpos = FPos::from_pos(pos); + let health = kind.initial_health(); + let speed = kind.initial_speed(); Self { pos, fpos, dir, kind, health, + speed, moving_to: None, } } @@ -200,8 +263,7 @@ impl Entity { pub const fn player(pos: Pos) -> Self { let dir = Direction::East; let kind = EntityKind::Player; - let health = Some(PLAYER_FULL_HEALTH); - Self::new(pos, dir, kind, health) + Self::new(pos, dir, kind) } /// Creates the Zombie version of the `Entity` @@ -218,8 +280,7 @@ impl Entity { pub const fn zombie(pos: Pos) -> Self { let dir = Direction::East; let kind = EntityKind::Zombie(EnemyMoveState::idle()); - let health = Some(ZOMBIE_HEALTH); - Self::new(pos, dir, kind, health) + Self::new(pos, dir, kind) } /// Returns the position in front of the entity @@ -234,6 +295,12 @@ impl Entity { self.moving_to = None; } + /// Heals an entity a set amount of HP + pub fn heal(&mut self, amt: u32) { + let max = self.kind.initial_health(); + self.health = (self.health + amt).min(max); + } + /// Returns a reference to this entities current AI pub const fn get_ai(&self) -> Option<&EnemyMoveState> { match &self.kind { @@ -248,6 +315,8 @@ impl Entity { pub struct Player { pub entity: Entity, pub inventory: Vec<Item>, + // How long until we reset potion effects? + pub potion_timer: Option<Instant>, } impl Player { /// Instantiates the game player at a given `Pos` @@ -263,7 +332,12 @@ impl Player { pub const fn new(pos: Pos) -> Self { let entity = Entity::player(pos); let inventory = vec![]; - Self { entity, inventory } + let potion_timer = None; + Self { + entity, + inventory, + potion_timer, + } } } impl Default for Player { @@ -395,7 +469,7 @@ impl Updater<'_> { let current = entity.fpos; // we are far from the goal, so we need to move towards it - let to_move = entity.kind.move_speed() * self.delta_time; + let to_move = entity.speed.per_frame(self.delta_time); entity.fpos.move_towards_manhattan(fdest, to_move); if fdest.abs_diff(entity.fpos).magnitude() <= 0.1 { diff --git a/dungeon/src/lib.rs b/dungeon/src/lib.rs index 4e48b68..13ab397 100644 --- a/dungeon/src/lib.rs +++ b/dungeon/src/lib.rs @@ -10,6 +10,8 @@ pub mod player_input; pub mod pos; pub mod rng; +use std::time::Instant; + use rand::{Rng, SeedableRng, TryRngCore, rngs::OsRng}; use crate::{ @@ -119,6 +121,7 @@ impl Dungeon { UpdateResult::MessageUpdated(changed) } else { self.update_entities(player_input, delta_time); + self.update_player(); if self.floor.get(self.player.entity.pos) == Tile::Stairs { // we are moving to a new floor @@ -132,6 +135,15 @@ impl Dungeon { } } + fn update_player(&mut self) { + if let Some(timer) = self.player.potion_timer + && Instant::now() > timer + { + self.player.potion_timer = None; + self.player.entity.speed = self.player.entity.kind.initial_speed(); + } + } + fn spawn_enimies(&mut self) { // TODO: better entity spawning let zombie = Entity::zombie(self.floor.random_walkable_pos(&mut self.level_rng)); diff --git a/dungeon/src/map.rs b/dungeon/src/map.rs index 0d05b73..7403e31 100644 --- a/dungeon/src/map.rs +++ b/dungeon/src/map.rs @@ -53,7 +53,11 @@ impl Tile { matches!(self, Self::Room | Self::Hallway | Self::Stairs) } - // Index by u16 + /// Returns if the tile is blast resistant + #[must_use] + pub const fn blast_resistant(self) -> bool { + matches!(self, Self::Stairs) + } } impl Display for Tile { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -164,6 +168,26 @@ impl Floor { break pos; } } + + /// Blows up a set number of tiles with a given radius + pub fn explode(&mut self, center_pos: Pos, radius: i16) { + let tiles_mut = self.tiles_mut(); + for x_off in -radius..=radius { + for y_off in -radius..=radius { + let Some(x) = center_pos.x().checked_add_signed(x_off) else { + continue; + }; + let Some(y) = center_pos.y().checked_add_signed(y_off) else { + continue; + }; + let Some(pos) = Pos::new(x, y) else { continue }; + if pos.is_border() || tiles_mut[pos.idx()].blast_resistant() { + continue; + } + tiles_mut[pos.idx()] = Tile::Room; + } + } + } } impl Display for Floor { /// Display the floor as a string for debugging diff --git a/graphics/src/render.rs b/graphics/src/render.rs index 02bc61c..208dafb 100644 --- a/graphics/src/render.rs +++ b/graphics/src/render.rs @@ -665,7 +665,7 @@ impl Renderer { where R: RaylibDraw, { - let health = player.entity.health.unwrap_or(0); + let health = player.entity.health; let damage = 0; // TODO: calc damage const TEXT_WIDTH: u16 = FONT_SIZE * 3 + UI_PADDING; |