diff --git a/src/game.rs b/src/game.rs index 3020bfe..c436d52 100644 --- a/src/game.rs +++ b/src/game.rs @@ -29,6 +29,12 @@ pub const LEVEL_SIZE: (usize, usize) = (80, 24); pub struct DungeonLevel { /// The tiles at every position in the level. tiles: [[DungeonTile; LEVEL_SIZE.0]; LEVEL_SIZE.1], + + /// The location of each of the up-staircases. + upstairs: Vec<(i32, i32)>, + + /// The location of each of the down-staircases. + downstairs: Vec<(i32, i32)>, } /// The smallest measurable independent location in the dungeon, @@ -38,14 +44,31 @@ pub enum DungeonTile { Floor, Wall, Hallway, + Upstair, + Downstair, } impl DungeonLevel { /// Creates a new level in a branch that has the given /// configuration. pub fn new(_cfg: &BranchConfig) -> Self { + // Self { + // tiles: rooms::generate_level(100, &mut rand::thread_rng()), + // } + rooms::generate_level(100, &mut rand::thread_rng(), 1, 1) + } + + /// Creates a new level with the given set of tiles, upstairs, and + /// downstairs. + pub fn from_raw_parts( + tiles: [[DungeonTile; LEVEL_SIZE.0]; LEVEL_SIZE.1], + upstairs: Vec<(i32, i32)>, + downstairs: Vec<(i32, i32)>, + ) -> Self { Self { - tiles: rooms::generate_level(100, &mut rand::thread_rng()), + tiles, + upstairs, + downstairs, } } @@ -100,6 +123,8 @@ impl DungeonLevel { } } DungeonTile::Hallway => '#', + DungeonTile::Upstair => '<', + DungeonTile::Downstair => '>', } } @@ -108,6 +133,16 @@ impl DungeonLevel { pub fn tile(&self, x: i32, y: i32) -> &DungeonTile { &self.tiles[y as usize][x as usize] } + + /// Gets the list of up-stairs. + pub fn upstairs(&self) -> &[(i32, i32)] { + &self.upstairs + } + + /// Gets the list of down-stairs. + pub fn downstairs(&self) -> &[(i32, i32)] { + &self.downstairs + } } impl Display for DungeonLevel { diff --git a/src/main.rs b/src/main.rs index 9b35a8a..6a6292d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,12 +22,13 @@ fn main() { let cfg = BranchConfig; let level = DungeonLevel::new(&cfg); + let spawn_pos = level.upstairs()[0]; world.insert(level); world .create_entity() - .with(Position { x: 5, y: 6 }) + .with(Position { x: spawn_pos.0, y: spawn_pos.1 }) .with(CharRender { glyph: '@' }) .with(Player) .with(Mobile { @@ -49,16 +50,14 @@ fn main() { loop { dispatcher.dispatch(&world); - let players = world.read_storage::(); - let turns = world.read_storage::(); - - if (&players, &turns).join().any(|(_plr, turn)| turn.next == 0) { - drop(players); - drop(turns); + if ( + &world.read_storage::(), + &world.read_storage::(), + ) + .join() + .any(|(_plr, turn)| turn.next == 0) + { player_turn(&mut world, &mut window); - } else { - drop(players); - drop(turns); } } } diff --git a/src/rooms.rs b/src/rooms.rs index 2abf466..49569e4 100644 --- a/src/rooms.rs +++ b/src/rooms.rs @@ -20,7 +20,7 @@ use pathfinding::directed::astar::astar; use rand::Rng; use crate::{ - game::{DungeonTile, LEVEL_SIZE}, + game::{DungeonLevel, DungeonTile, LEVEL_SIZE}, util::NiceFloat, }; @@ -48,7 +48,13 @@ const HALLWAY_RANDOMNESS: f64 = 0.6; /// Generates a grid of the given size containing rooms connected by /// passages. -pub fn generate(n_rooms: usize, size: (usize, usize), rng: &mut impl Rng) -> Grid { +pub fn generate( + n_rooms: usize, + size: (usize, usize), + rng: &mut impl Rng, + upstairs: usize, + downstairs: usize, +) -> (Grid, Vec<(i32, i32)>, Vec<(i32, i32)>) { let mut grid = Grid::init(size.1, size.0, DungeonTile::Wall); let rooms = RoomBounds::generate(n_rooms, size, rng); @@ -58,21 +64,24 @@ pub fn generate(n_rooms: usize, size: (usize, usize), rng: &mut impl Rng) -> Gri } } - cut_hallways(&mut grid, &rooms, rng); + add_hallways(&mut grid, &rooms, rng); + let (upstairs, downstairs) = add_stairs(&mut grid, upstairs, downstairs, rng); - grid + (grid, upstairs, downstairs) } /// Generates a grid of the statically-known level size. pub fn generate_level( n_rooms: usize, rng: &mut impl Rng, -) -> [[DungeonTile; LEVEL_SIZE.0]; LEVEL_SIZE.1] { + upstairs: usize, + downstairs: usize, +) -> DungeonLevel { // FIXME: This function is atrocious. We do an allocation here // when we theoretically doesn't need to (we get a heap-allocated // Grid back, when we know statically that it's LEVEL_SIZE so we // could allocate it on the stack)... - let grid = generate(n_rooms, LEVEL_SIZE, rng); + let (grid, upstairs, downstairs) = generate(n_rooms, LEVEL_SIZE, rng, upstairs, downstairs); // ...and then we use a pointless default of DungeonTile::Floor // here then copy in the real data from `grid`. @@ -84,7 +93,7 @@ pub fn generate_level( *slot = value; } - data + DungeonLevel::from_raw_parts(data, upstairs, downstairs) } /// The bounding box of a room. @@ -171,7 +180,7 @@ impl RoomBounds { } /// Adds a set of hallways connecting the given rooms to a dungeon. -fn cut_hallways(grid: &mut Grid, rooms: &[RoomBounds], rng: &mut impl Rng) { +fn add_hallways(grid: &mut Grid, rooms: &[RoomBounds], rng: &mut impl Rng) { // How hard we try to avoid traveling through stone at a pair of // coordinates. let mut stone_weights = Grid::new(grid.rows(), grid.cols()); @@ -239,3 +248,41 @@ fn cut_hallways(grid: &mut Grid, rooms: &[RoomBounds], rng: &mut im } } } + +/// Adds staircases leading upwards and downwards to the level. +fn add_stairs( + grid: &mut Grid, + n_upstairs: usize, + n_downstairs: usize, + rng: &mut impl Rng, +) -> (Vec<(i32, i32)>, Vec<(i32, i32)>) { + let (mut upstairs, mut downstairs) = ( + Vec::with_capacity(n_upstairs), + Vec::with_capacity(n_downstairs), + ); + + for _ in 0..n_upstairs { + let (x, y) = empty_square(grid, rng); + upstairs.push((x, y)); + grid[y as usize][x as usize] = DungeonTile::Upstair; + } + + for _ in 0..n_downstairs { + let (x, y) = empty_square(grid, rng); + downstairs.push((x, y)); + grid[y as usize][x as usize] = DungeonTile::Downstair; + } + + (upstairs, downstairs) +} + +/// Finds an unoccupied (floor) square of the level. +fn empty_square(grid: &Grid, rng: &mut impl Rng) -> (i32, i32) { + loop { + let (x, y) = (rng.gen_range(0..grid.cols()), rng.gen_range(0..grid.rows())); + + if grid[y][x] == DungeonTile::Floor { + break (x as _, y as _); + } + } +}