diff options
| author | Freya Murphy <freya@freyacat.org> | 2025-10-07 11:01:19 -0400 |
|---|---|---|
| committer | Freya Murphy <freya@freyacat.org> | 2025-10-08 19:37:15 -0400 |
| commit | b11f074ceba10af62b35b414ecaa51a8f13c6550 (patch) | |
| tree | d12979f7eeb384f94b145ea763a97dcde353745b /dungeon/src | |
| parent | Initial commit (diff) | |
| download | DungeonCrawl-b11f074ceba10af62b35b414ecaa51a8f13c6550.tar.gz DungeonCrawl-b11f074ceba10af62b35b414ecaa51a8f13c6550.tar.bz2 DungeonCrawl-b11f074ceba10af62b35b414ecaa51a8f13c6550.zip | |
initial baseline
Diffstat (limited to 'dungeon/src')
| -rw-r--r-- | dungeon/src/lib.rs | 58 | ||||
| -rw-r--r-- | dungeon/src/map.rs | 189 | ||||
| -rw-r--r-- | dungeon/src/pos.rs | 122 |
3 files changed, 369 insertions, 0 deletions
diff --git a/dungeon/src/lib.rs b/dungeon/src/lib.rs new file mode 100644 index 0000000..66faec3 --- /dev/null +++ b/dungeon/src/lib.rs @@ -0,0 +1,58 @@ +//! The `dungon` crate contains the core functionality for +//! interacting with a `Dungeon` and its components. + +mod map; +mod pos; + +pub use map::*; +pub use pos::*; + +/// The `Dungeon` type represents the game state of the +/// dungeon crawler. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Dungeon { + player: Entity, + floor: Floor, +} +impl Dungeon { + /// Creates a new `Dungeon`. + /// + /// # Examples + /// + /// ```no_run + /// use dungeon::Dungeon; + /// + /// let dungeon = Dungeon::new(); + /// ``` + #[must_use] + pub fn new() -> Self { + let floor = Floor::generate(); + let player = Entity::player(floor.player_start()); + + Self { player, floor } + } + + /// Creates a new `Dungeon` with a provided seed. + /// + /// # Examples + /// + /// ```no_run + /// use dungeon::Dungeon; + /// + /// let seed = 234690523482u64; + /// let dungeon = Dungeon::new_seeded(seed); + /// ``` + #[must_use] + pub fn new_seeded(seed: u64) -> Self { + let floor = Floor::generate_seeded(seed); + let player = Entity::player(floor.player_start()); + + Self { player, floor } + } +} + +impl Default for Dungeon { + fn default() -> Self { + Self::new() + } +} diff --git a/dungeon/src/map.rs b/dungeon/src/map.rs new file mode 100644 index 0000000..8c4328c --- /dev/null +++ b/dungeon/src/map.rs @@ -0,0 +1,189 @@ +//! The `map` module contains structures of the dungeon game map +//! including the current `Floor`, map `Tile`, and `Entity`. + +use crate::pos::{Direction, Pos}; + +/// `MAP_SIZE` is the size of the size of the dungeon grid. +pub const MAP_SIZE: u16 = 100; + +/// The number of tiles in the dungeon grid +pub const TILE_COUNT: usize = MAP_SIZE as usize * MAP_SIZE as usize; + +/// The `EntityKind` represents what kind of entity this is. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum EntityKind { + Player, +} + +/// The `Entity` kind represents the main player, or any other +/// ai autonomous character that can move freely across the +/// dungeon. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct Entity { + pos: Pos, + dir: Direction, + kind: EntityKind, +} +impl Entity { + /// Creates a new `Entity` at a given `Pos`, `Direction`, and `EntityKind`. + /// + /// # Examples + /// + /// ``` + /// use dungeon::{Pos, Direction, Entity, EntityKind}; + /// + /// let pos = Pos::new(0, 0).unwrap(); + /// let dir = Direction::North; + /// let kind = EntityKind::Player; + /// let entity = Entity::new(pos, dir, kind); + /// ``` + #[must_use] + pub const fn new(pos: Pos, dir: Direction, kind: EntityKind) -> Self { + Self { pos, dir, kind } + } + + /// Creates the Player version of the `Entity` + /// + /// # Examples + /// + /// ``` + /// use dungeon::{Pos, Entity}; + /// + /// let pos = Pos::new(0, 0).unwrap(); + /// let player = Entity::player(pos); + /// ``` + #[must_use] + pub const fn player(pos: Pos) -> Self { + let dir = Direction::East; + let kind = EntityKind::Player; + Self::new(pos, dir, kind) + } + + /// Returns the `Pos` of the entity + #[must_use] + pub const fn pos(&self) -> Pos { + self.pos + } + + /// Returns a mutable referense to the `Pos` of the entity + #[must_use] + pub const fn pos_mut(&mut self) -> &mut Pos { + &mut self.pos + } + + /// Returns the `Direction` of the entity + #[must_use] + pub const fn dir(&self) -> Direction { + self.dir + } + + /// Returns a mutable referense to the `Direction` of the entity + #[must_use] + pub const fn dir_mut(&mut self) -> &mut Direction { + &mut self.dir + } + + /// Returns the `EntityKind` of this entity + #[must_use] + pub const fn kind(&self) -> EntityKind { + self.kind + } +} + +/// The `Tile` enum represents what is (or is not) at +/// any given spot in the dungeon grid. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum Tile { + /// `Wall` represents an impassible wall + Wall, + /// `Air` represents empty walkable space + Air, +} + +/// 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, Hash)] +pub struct Floor { + /// The dungeon grid + tiles: Box<[Tile; TILE_COUNT]>, + /// The position the player starts at + player_start: Pos, + /// The seed used when generating the dungeon grid + seed: u64, +} +impl Floor { + /// Generates a dungeon `Floor` using wave function collapse. + /// + /// # Examples + /// + /// ```no_run + /// use dungeon::Floor; + /// + /// let floor = Floor::generate(); + /// ``` + #[must_use] + pub fn generate() -> Self { + let seed = rand::random(); + Self::generate_seeded(seed) + } + + /// Genreates a dungeon `Floor` using wave function collapse provided with a seed. + /// + /// The provided seed is used for randomness in the wave function + /// collapse algorithm. + /// + /// # Examples + /// + /// ```no_run + /// use dungeon::Floor; + /// + /// /// here is our very seedy seed + /// let seed = 2893249402u64; + /// let floor = Floor::generate_seeded(seed); + /// ``` + #[must_use] + pub fn generate_seeded(_seed: u64) -> Self { + unimplemented!() + } + + /// Returns the start position of the player + #[must_use] + pub const fn player_start(&self) -> Pos { + self.player_start + } + + /// Returns the seed used to generate the map + #[must_use] + pub const fn seed(&self) -> u64 { + self.seed + } + + /// Returns a `Tile` on the dungeon grid at `Pos`. + #[must_use] + pub const fn get(&self, pos: Pos) -> Tile { + let idx = pos.idx(); + self.tiles[idx] + } + + /// Returns a multable reference to a `Tile` on the dungeon grid at `Pos`. + #[must_use] + pub const fn get_mut(&mut self, pos: Pos) -> &mut Tile { + let idx = pos.idx(); + &mut self.tiles[idx] + } + + /// Returns a reference to all tiles in the `Floor`. + /// The size of this lise will always be `TILE_COUNT` long. + #[must_use] + pub const fn tiles(&self) -> &[Tile] { + &*self.tiles + } + + /// Returns a mutable reference to all tiles in the `Floor`. + /// The size of this lise will always be `TILE_COUNT` long. + #[must_use] + pub const fn tiles_mut(&mut self) -> &mut [Tile] { + &mut *self.tiles + } +} diff --git a/dungeon/src/pos.rs b/dungeon/src/pos.rs new file mode 100644 index 0000000..bfd994f --- /dev/null +++ b/dungeon/src/pos.rs @@ -0,0 +1,122 @@ +//! The `pos` module contains structures for representation an +//! entity or objects position and facing direction inside the +//! dungeon grid. + +use crate::map::MAP_SIZE; + +/// The `Direction` type represents a direction an entity +/// or any position object is facing inside the dungeon map. +/// Since the dungeon lives on a grid, there are only four +/// possible directions. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum Direction { + North, + South, + East, + West, +} + +/// The `Pos` type represents a 2D position inside the dungeon grid. +/// +/// The max size for the dungeon map is set by the `MAP_SIZE` constant +/// and therefore the x and y positions can be between 0 and `MAP_SIZE - 1`. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Pos(u16, u16); +impl Pos { + /// Creates a new position from a given x and y position. + /// + /// Returns `None` if the position goes out of the map. + /// + /// # Examples + /// + /// ``` + /// use dungeon::Pos; + /// + /// let pos = Pos::new(0,0); + /// assert!(pos.is_some()); + /// ``` + /// + /// ``` + /// use dungeon::{Pos, MAP_SIZE}; + /// + /// let pos = Pos::new(MAP_SIZE, MAP_SIZE); + /// assert!(pos.is_none()) + /// ``` + #[must_use] + pub const fn new(x: u16, y: u16) -> Option<Self> { + if x >= MAP_SIZE || y >= MAP_SIZE { + None + } else { + Some(Self(x, y)) + } + } + + /// Returns the x and y positions of `Pos`. + /// + /// # Examples + /// + /// ``` + /// use dungeon::Pos; + /// + /// let pos = Pos::new(5,7).unwrap(); + /// let (x,y) = pos.xy(); + /// assert_eq!(x, 5); + /// assert_eq!(y, 7); + /// ``` + #[must_use] + pub const fn xy(self) -> (u16, u16) { + (self.0, self.1) + } + + /// Converts the x and y positions into an index of a continous list. + /// + /// # Examples + /// + /// ``` + /// use dungeon::{Pos, MAP_SIZE}; + /// + /// let pos = Pos::new(1,2).unwrap(); + /// let idx = pos.idx(); + /// assert_eq!(idx, 1 + 2 * MAP_SIZE as usize); + /// ``` + #[must_use] + pub const fn idx(self) -> usize { + let (x, y) = self.xy(); + let idx = x + y * MAP_SIZE; + idx as usize + } + + /// Steps `Pos` one space in the `Direction` `dir`. + /// + /// Returns `None` if the position goes out of the map. + /// + /// # Examples + /// + /// ``` + /// use dungeon::{Direction, Pos}; + /// + /// let pos = Pos::new(0, 1).unwrap(); + /// let new_pos = pos.step(Direction::North); + /// assert_eq!(new_pos, Pos::new(0, 0)); + /// ``` + /// + /// ``` + /// use dungeon::{Direction, Pos}; + /// + /// let pos = Pos::new(0, 1).unwrap(); + /// let new_pos = pos.step(Direction::West); + /// assert!(new_pos.is_none()); + /// ``` + #[must_use] + pub const fn step(self, dir: Direction) -> Option<Self> { + use Direction as D; + let (x, y) = self.xy(); + match dir { + D::North if y > 0 => Self::new(x, y - 1), + D::South => Self::new(x, y + 1), + D::East => Self::new(x + 1, y), + D::West if x > 0 => Self::new(x - 1, y), + _ => None, + } + } +} |