terrain_forge/algorithms/
rooms.rs

1use crate::{Algorithm, Grid, Rng, Tile};
2
3#[derive(Debug, Clone)]
4pub struct SimpleRoomsConfig {
5    pub min_room_size: usize,
6    pub max_room_size: usize,
7    pub max_rooms: usize,
8    pub min_spacing: usize,
9}
10
11impl Default for SimpleRoomsConfig {
12    fn default() -> Self {
13        Self { min_room_size: 4, max_room_size: 10, max_rooms: 10, min_spacing: 1 }
14    }
15}
16
17pub struct SimpleRooms {
18    config: SimpleRoomsConfig,
19}
20
21impl SimpleRooms {
22    pub fn new(config: SimpleRoomsConfig) -> Self { Self { config } }
23}
24
25impl Default for SimpleRooms {
26    fn default() -> Self { Self::new(SimpleRoomsConfig::default()) }
27}
28
29struct Room { x: usize, y: usize, w: usize, h: usize }
30
31impl Room {
32    fn intersects(&self, other: &Room, spacing: usize) -> bool {
33        let s = spacing as i32;
34        !((self.x as i32 + self.w as i32 + s) < other.x as i32
35            || (other.x as i32 + other.w as i32 + s) < self.x as i32
36            || (self.y as i32 + self.h as i32 + s) < other.y as i32
37            || (other.y as i32 + other.h as i32 + s) < self.y as i32)
38    }
39    fn center(&self) -> (usize, usize) { (self.x + self.w / 2, self.y + self.h / 2) }
40}
41
42impl Algorithm<Tile> for SimpleRooms {
43    fn generate(&self, grid: &mut Grid<Tile>, seed: u64) {
44        let mut rng = Rng::new(seed);
45        let mut rooms: Vec<Room> = Vec::new();
46        let cfg = &self.config;
47
48        for _ in 0..cfg.max_rooms * 3 {
49            if rooms.len() >= cfg.max_rooms { break; }
50
51            let w = rng.range_usize(cfg.min_room_size, cfg.max_room_size + 1);
52            let h = rng.range_usize(cfg.min_room_size, cfg.max_room_size + 1);
53            if w + 2 >= grid.width() || h + 2 >= grid.height() { continue; }
54
55            let x = rng.range_usize(1, grid.width() - w - 1);
56            let y = rng.range_usize(1, grid.height() - h - 1);
57            let room = Room { x, y, w, h };
58
59            if rooms.iter().any(|r| r.intersects(&room, cfg.min_spacing)) { continue; }
60
61            grid.fill_rect(x as i32, y as i32, w, h, Tile::Floor);
62
63            if let Some(prev) = rooms.last() {
64                let (cx, cy) = room.center();
65                let (px, py) = prev.center();
66                if rng.chance(0.5) {
67                    carve_h(grid, px, cx, py);
68                    carve_v(grid, py, cy, cx);
69                } else {
70                    carve_v(grid, py, cy, px);
71                    carve_h(grid, px, cx, cy);
72                }
73            }
74            rooms.push(room);
75        }
76    }
77
78    fn name(&self) -> &'static str { "SimpleRooms" }
79}
80
81fn carve_h(grid: &mut Grid<Tile>, x1: usize, x2: usize, y: usize) {
82    for x in x1.min(x2)..=x1.max(x2) { grid.set(x as i32, y as i32, Tile::Floor); }
83}
84
85fn carve_v(grid: &mut Grid<Tile>, y1: usize, y2: usize, x: usize) {
86    for y in y1.min(y2)..=y1.max(y2) { grid.set(x as i32, y as i32, Tile::Floor); }
87}