summaryrefslogtreecommitdiff
path: root/dungeon/src
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2025-10-07 11:01:19 -0400
committerFreya Murphy <freya@freyacat.org>2025-10-08 19:37:15 -0400
commitb11f074ceba10af62b35b414ecaa51a8f13c6550 (patch)
treed12979f7eeb384f94b145ea763a97dcde353745b /dungeon/src
parentInitial commit (diff)
downloadDungeonCrawl-b11f074ceba10af62b35b414ecaa51a8f13c6550.tar.gz
DungeonCrawl-b11f074ceba10af62b35b414ecaa51a8f13c6550.tar.bz2
DungeonCrawl-b11f074ceba10af62b35b414ecaa51a8f13c6550.zip
initial baseline
Diffstat (limited to 'dungeon/src')
-rw-r--r--dungeon/src/lib.rs58
-rw-r--r--dungeon/src/map.rs189
-rw-r--r--dungeon/src/pos.rs122
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,
+ }
+ }
+}