summaryrefslogtreecommitdiff
path: root/dungeon
diff options
context:
space:
mode:
Diffstat (limited to 'dungeon')
-rw-r--r--dungeon/src/bsp.rs41
-rw-r--r--dungeon/tests/bsp_tests.rs14
2 files changed, 44 insertions, 11 deletions
diff --git a/dungeon/src/bsp.rs b/dungeon/src/bsp.rs
index f996405..9b288a2 100644
--- a/dungeon/src/bsp.rs
+++ b/dungeon/src/bsp.rs
@@ -17,7 +17,7 @@ const MAX_ROOM_SIZE: u16 = 12;
/// The `Rect` type represents a rectangle inside the map (x,y,width,height).
/// (x,y) is the top-left corner.
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Rect {
x: u16,
y: u16,
@@ -34,13 +34,20 @@ impl Rect {
fn center(&self) -> Pos {
let cx = self.x + self.w / 2;
let cy = self.y + self.h / 2;
- Pos::new(cx as u16, cy as u16).unwrap_or(const_pos!(1, 1))
+ Pos::new(cx, cy).unwrap_or(const_pos!(1, 1))
+ }
+
+ /// Returns a random point in this rectangle.
+ fn random_point<R: Rng>(&self, rng: &mut R) -> Pos {
+ let rx = rng.random_range(self.x..(self.x + self.w));
+ let ry = rng.random_range(self.y..(self.y + self.h));
+ Pos::new(rx, ry).unwrap_or(self.center())
}
}
/// The `Node` type represents a node in the BSP tree.
/// Has optional left and right children, and an optional room rectangle.
-#[derive(Debug)]
+#[derive(Debug, Clone, PartialEq, Eq)]
struct Node {
rect: Rect, // Area this node covers
left: Option<Box<Node>>, // Left child (if not leaf)
@@ -207,19 +214,24 @@ impl Node {
return right.room_center();
}
// Fallback (should not happen)
- const_pos!(1, 1)
+ panic!("No room found in subtree");
}
- /// Return a point (x,y) on the border of the room in this node.
+ /// Return a random point for a room in this subtree:
+ /// if node has a room, return a randiom point in it, else try left then right.
fn random_point_in_room<R: Rng>(&self, rng: &mut R) -> Pos {
+ // Base case: if this node has a room, return a random point in it
if let Some(room) = &self.room {
- let rx = rng.random_range(room.x..(room.x + room.w));
- let ry = rng.random_range(room.y..(room.y + room.h));
- Pos::new(rx, ry).unwrap_or(self.rect.center())
- } else {
- // No room: return center of rect
- self.rect.center()
+ return room.random_point(rng);
+ }
+ if let Some(left) = &self.left {
+ return left.random_point_in_room(rng);
}
+ if let Some(right) = &self.right {
+ return right.random_point_in_room(rng);
+ }
+ // Fallback (should not happen)
+ panic!("No room found in subtree");
}
/// Connect rooms of child nodes recursively and collect corridors to carve.
@@ -377,6 +389,13 @@ pub fn generate(seed: u64) -> (Box<[Tile; TILE_COUNT]>, Pos) {
let player_start = player_room.room_center();
// Set one tile to Stairs (exit) in a random room different from player start
+ let mut exit_room = player_room;
+ while exit_room == player_room {
+ exit_room = leaves.choose(&mut rng).unwrap_or(&leaves[0]);
+ }
+ let exit_pos = exit_room.room_center();
+ let exit_idx = exit_pos.xy().0 + exit_pos.xy().1 * MAP_SIZE;
+ tiles_box[exit_idx as usize] = Tile::Stairs;
// Return tiles and player_start
(tiles_box, player_start)
diff --git a/dungeon/tests/bsp_tests.rs b/dungeon/tests/bsp_tests.rs
index 2e3a781..6bd0153 100644
--- a/dungeon/tests/bsp_tests.rs
+++ b/dungeon/tests/bsp_tests.rs
@@ -3,6 +3,7 @@
mod tests {
use dungeon::*;
+ /// Basic integration test for BSP generation
#[test]
fn test_bsp_integration() {
let seed = 12345u64;
@@ -12,4 +13,17 @@ mod tests {
assert!(player_start.x() < map::MAP_SIZE);
assert!(player_start.y() < map::MAP_SIZE);
}
+
+ /// Test that BSP-generated floors have a valid player start
+ #[test]
+ fn test_bsp_player_start() {
+ let seed = 12345u64;
+ let (tiles, player_start) = bsp::generate(seed);
+ // Ensure player start is within bounds
+ assert!(player_start.x() < map::MAP_SIZE);
+ assert!(player_start.y() < map::MAP_SIZE);
+ // Ensure player start is an air tile
+ let idx = player_start.idx();
+ assert_eq!(tiles[idx], map::Tile::Air);
+ }
}