diff options
| author | Ryan Symons <47405201+rsymons22@users.noreply.github.com> | 2025-11-04 22:24:23 -0500 |
|---|---|---|
| committer | Ryan Symons <47405201+rsymons22@users.noreply.github.com> | 2025-11-04 22:24:23 -0500 |
| commit | 574f3c694dcce927ac87dfb79276775f0904bdc8 (patch) | |
| tree | bebe583ec5194c4a5a3187c04b79d0c84215e1b8 | |
| parent | Add SDL feature flag (fixes wayland issues) (diff) | |
| download | DungeonCrawl-574f3c694dcce927ac87dfb79276775f0904bdc8.tar.gz DungeonCrawl-574f3c694dcce927ac87dfb79276775f0904bdc8.tar.bz2 DungeonCrawl-574f3c694dcce927ac87dfb79276775f0904bdc8.zip | |
Added enemy code structure and test movement
| -rw-r--r-- | dungeon/src/enemy.rs | 106 | ||||
| -rw-r--r-- | dungeon/src/entity.rs | 7 | ||||
| -rw-r--r-- | dungeon/src/lib.rs | 16 | ||||
| -rw-r--r-- | dungeon/src/map.rs | 2 | ||||
| -rw-r--r-- | game/src/main.rs | 8 | ||||
| -rw-r--r-- | graphics/src/render.rs | 3 |
6 files changed, 139 insertions, 3 deletions
diff --git a/dungeon/src/enemy.rs b/dungeon/src/enemy.rs new file mode 100644 index 0000000..8c818f5 --- /dev/null +++ b/dungeon/src/enemy.rs @@ -0,0 +1,106 @@ +use crate::{Direction, Entity, FPos, Pos}; + +pub const ZOMBIE_HEALTH: u32 = 50; + +#[derive(Clone, Debug, PartialEq)] +pub enum EnemyMoveSpeed { + Slow, + Medium, + Fast, +} + +/// Every (value) seconds, an enemy with a (speed) can move one tile +pub const SLOW_SPEED_SEC_PER_TILE: f32 = 1.; +pub const MEDIUM_SPEED_SEC_PER_TILE: f32 = 0.5; +pub const FAST_SPEED_SEC_PER_TILE: f32 = 0.25; + +#[derive(Clone, Debug, PartialEq)] +pub enum EnemyAttackType { + Melee, + Ranged, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum EnemyType { + Zombie, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Enemy { + pub entity: Entity, + pub enemy_type: EnemyType, + pub move_speed: EnemyMoveSpeed, + pub attack_type: EnemyAttackType, + time_acc: f32, +} + +impl Enemy { + pub fn new(enemy_type: EnemyType, pos: Pos) -> Self { + match enemy_type { + EnemyType::Zombie => Self::zombie(pos), + } + } + + fn _new( + pos: Pos, + health: u32, + enemy_type: EnemyType, + move_speed: EnemyMoveSpeed, + attack_type: EnemyAttackType, + ) -> Self { + let entity = Entity::enemy(pos, health); + Self { + entity, + enemy_type, + move_speed, + attack_type, + time_acc: 0., + } + } + + fn zombie(pos: Pos) -> Self { + Self::_new( + pos, + ZOMBIE_HEALTH, + EnemyType::Zombie, + EnemyMoveSpeed::Slow, + EnemyAttackType::Melee, + ) + } + + fn is_there(&self) -> bool { + self.time_acc + >= match &self.move_speed { + EnemyMoveSpeed::Slow => SLOW_SPEED_SEC_PER_TILE, + EnemyMoveSpeed::Medium => MEDIUM_SPEED_SEC_PER_TILE, + EnemyMoveSpeed::Fast => FAST_SPEED_SEC_PER_TILE, + } + } + + /// + /// Rough concept for enemy "AI": + /// + /// State: Roam/Patrol + /// Moves around in a small area (2x2, 3x3, 4x4) randomly choosing direction and how much to move. + /// State: Attack + /// Sees player in facing direction (only in box/rectangle in dir) + /// (ex. facing north, if player is anywhere above monster and theres no walls in the way, attack) + /// Do A* pathing to player until enemy dead or player dead or floor change + /// + /// Current implementation below is just a test + /// + pub fn update(&mut self, player_pos: Pos, time_since_last_frame: f32) { + if self.entity.pos == player_pos { + return; + } + + self.time_acc += time_since_last_frame; + if self.is_there() { + let temp_pos = self.entity.pos.step(Direction::West).unwrap(); + self.entity.pos = temp_pos; + self.entity.fpos = FPos::from_pos(temp_pos); + + self.time_acc = 0.; + } + } +} diff --git a/dungeon/src/entity.rs b/dungeon/src/entity.rs index 02184a9..5f0425f 100644 --- a/dungeon/src/entity.rs +++ b/dungeon/src/entity.rs @@ -21,6 +21,7 @@ pub enum Item { pub enum EntityKind { /// The main player Player, + Enemy, /// An item (not in an inventory) on the floor of the dungeon Item(Item), } @@ -89,6 +90,12 @@ impl Entity { let health = Some(PLAYER_FULL_HEALTH); Self::new(pos, dir, kind, health) } + + pub const fn enemy(pos: Pos, health: u32) -> Self { + let dir = Direction::East; + let kind = EntityKind::Enemy; + Self::new(pos, dir, kind, Some(health)) + } } /// The `Player` type represents the main player entity diff --git a/dungeon/src/lib.rs b/dungeon/src/lib.rs index 39d7c60..8119119 100644 --- a/dungeon/src/lib.rs +++ b/dungeon/src/lib.rs @@ -2,6 +2,7 @@ //! interacting with a `Dungeon` and its components. mod bsp; +mod enemy; mod entity; mod map; mod pos; @@ -11,12 +12,15 @@ pub use entity::*; pub use map::*; pub use pos::*; +use crate::enemy::{Enemy, EnemyType}; + /// The `Dungeon` type represents the game state of the /// dungeon crawler. #[derive(Clone, Debug, PartialEq)] pub struct Dungeon { pub floor: Floor, pub player: Player, + pub enemies: Vec<Enemy>, } impl Dungeon { /// Creates a new `Dungeon`. @@ -65,6 +69,16 @@ impl From<Floor> for Dungeon { // TODO: initalize rest of game state - Self { floor, player } + // TODO: How will we randomize enemy type/number per floor? + let enemies = vec![Enemy::new( + EnemyType::Zombie, + Pos::new(player.entity.pos.x() + 4, player.entity.pos.y()).unwrap(), + )]; + + Self { + floor, + player, + enemies, + } } } diff --git a/dungeon/src/map.rs b/dungeon/src/map.rs index f4a8729..212dd00 100644 --- a/dungeon/src/map.rs +++ b/dungeon/src/map.rs @@ -52,7 +52,7 @@ impl Default for Tile { /// The `Floor` type represents the current playing /// grid of the dungeon. It contains the tiles of the /// grid, and the starting position of the player. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq)] pub struct Floor { /// The dungeon grid tiles: Box<[Tile; TILE_COUNT]>, diff --git a/game/src/main.rs b/game/src/main.rs index 22203c3..7e6c8bb 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -5,11 +5,17 @@ fn main() -> Result<()> { // Load the window let mut window = Window::new(720, 480, "game")?; // Initial game state - let dungeon = Dungeon::new(); + let mut dungeon = Dungeon::new(); // Main game loop while window.is_open() { // TODO update game state + + // Enemy Tick + for enemy in dungeon.enemies.iter_mut() { + enemy.update(dungeon.player.entity.pos, window.delta_time()); + } + // Draw a single frame window.renderer().draw_frame(&dungeon); } diff --git a/graphics/src/render.rs b/graphics/src/render.rs index 499301c..4166a7a 100644 --- a/graphics/src/render.rs +++ b/graphics/src/render.rs @@ -411,6 +411,9 @@ where /// Draws the entities on the map fn draw_entities(&mut self, dungeon: &Dungeon) { self.draw_entity(&dungeon.player.entity); + for enemy in &dungeon.enemies { + self.draw_entity(&enemy.entity); + } } /// Draws an entity |