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 | |
| parent | Initial commit (diff) | |
| download | DungeonCrawl-b11f074ceba10af62b35b414ecaa51a8f13c6550.tar.gz DungeonCrawl-b11f074ceba10af62b35b414ecaa51a8f13c6550.tar.bz2 DungeonCrawl-b11f074ceba10af62b35b414ecaa51a8f13c6550.zip | |
initial baseline
Diffstat (limited to 'dungeon')
| -rw-r--r-- | dungeon/Cargo.lock | 157 | ||||
| -rw-r--r-- | dungeon/Cargo.toml | 10 | ||||
| -rw-r--r-- | dungeon/src/lib.rs | 58 | ||||
| -rw-r--r-- | dungeon/src/map.rs | 189 | ||||
| -rw-r--r-- | dungeon/src/pos.rs | 122 |
5 files changed, 536 insertions, 0 deletions
diff --git a/dungeon/Cargo.lock b/dungeon/Cargo.lock new file mode 100644 index 0000000..b9a6727 --- /dev/null +++ b/dungeon/Cargo.lock @@ -0,0 +1,157 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "dungeon" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.176" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/dungeon/Cargo.toml b/dungeon/Cargo.toml new file mode 100644 index 0000000..5548248 --- /dev/null +++ b/dungeon/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "dungeon" +version = "0.1.0" +edition = "2024" + +[dependencies] +rand = "0.9" + +[lints] +workspace = true 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, + } + } +} |