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