Skip to main content

steel_worldgen/structure/mansion/
grid.rs

1use super::template::dir_from_2d;
2use super::{Direction, LegacyRandom, Random};
3
4pub(super) struct SimpleGrid {
5    pub(super) grid: Vec<Vec<i32>>,
6    pub(super) width: i32,
7    pub(super) height: i32,
8    pub(super) outside: i32,
9}
10
11impl SimpleGrid {
12    pub(super) fn new(width: i32, height: i32, outside: i32) -> Self {
13        Self {
14            grid: vec![vec![0; height as usize]; width as usize],
15            width,
16            height,
17            outside,
18        }
19    }
20
21    pub(super) fn get(&self, x: i32, y: i32) -> i32 {
22        if x >= 0 && x < self.width && y >= 0 && y < self.height {
23            self.grid[x as usize][y as usize]
24        } else {
25            self.outside
26        }
27    }
28
29    pub(super) fn set_cell(&mut self, x: i32, y: i32, value: i32) {
30        if x >= 0 && x < self.width && y >= 0 && y < self.height {
31            self.grid[x as usize][y as usize] = value;
32        }
33    }
34
35    pub(super) fn set_range(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, value: i32) {
36        for y in y0..=y1 {
37            for x in x0..=x1 {
38                self.set_cell(x, y, value);
39            }
40        }
41    }
42
43    pub(super) fn setif(&mut self, x: i32, y: i32, if_value: i32, value: i32) {
44        if self.get(x, y) == if_value {
45            self.set_cell(x, y, value);
46        }
47    }
48
49    pub(super) fn edges_to(&self, x: i32, y: i32, value: i32) -> bool {
50        self.get(x - 1, y) == value
51            || self.get(x + 1, y) == value
52            || self.get(x, y + 1) == value
53            || self.get(x, y - 1) == value
54    }
55}
56
57pub(super) fn is_house(grid: &SimpleGrid, x: i32, y: i32) -> bool {
58    let v = grid.get(x, y);
59    v == 1 || v == 2 || v == 3 || v == 4
60}
61
62pub(super) const ROOM_1X1: i32 = 65_536;
63pub(super) const ROOM_1X2: i32 = 131_072;
64pub(super) const ROOM_2X2: i32 = 262_144;
65pub(super) const ROOM_ORIGIN_FLAG: i32 = 1_048_576;
66pub(super) const ROOM_DOOR_FLAG: i32 = 2_097_152;
67pub(super) const ROOM_STAIRS_FLAG: i32 = 4_194_304;
68pub(super) const ROOM_CORRIDOR_FLAG: i32 = 8_388_608;
69pub(super) const ROOM_TYPE_MASK: i32 = 983_040;
70pub(super) const ROOM_ID_MASK: i32 = 65_535;
71
72pub(super) struct MansionGrid {
73    pub(super) base_grid: SimpleGrid,
74    pub(super) third_floor_grid: SimpleGrid,
75    pub(super) floor_rooms: [SimpleGrid; 3],
76    pub(super) entrance_x: i32,
77    pub(super) entrance_y: i32,
78}
79
80impl MansionGrid {
81    pub(super) fn new(rng: &mut LegacyRandom) -> Self {
82        let entrance_x = 7;
83        let entrance_y = 4;
84        let mut base = SimpleGrid::new(11, 11, 5);
85        base.set_range(entrance_x, entrance_y, entrance_x + 1, entrance_y + 1, 3);
86        base.set_range(
87            entrance_x - 1,
88            entrance_y,
89            entrance_x - 1,
90            entrance_y + 1,
91            2,
92        );
93        base.set_range(
94            entrance_x + 2,
95            entrance_y - 2,
96            entrance_x + 3,
97            entrance_y + 3,
98            5,
99        );
100        base.set_range(
101            entrance_x + 1,
102            entrance_y - 2,
103            entrance_x + 1,
104            entrance_y - 1,
105            1,
106        );
107        base.set_range(
108            entrance_x + 1,
109            entrance_y + 2,
110            entrance_x + 1,
111            entrance_y + 3,
112            1,
113        );
114        base.set_cell(entrance_x - 1, entrance_y - 1, 1);
115        base.set_cell(entrance_x - 1, entrance_y + 2, 1);
116        base.set_range(0, 0, 11, 1, 5);
117        base.set_range(0, 9, 11, 11, 5);
118        for (x, y, depth) in [
119            (entrance_x, entrance_y - 2, 6),
120            (entrance_x, entrance_y + 3, 6),
121            (entrance_x - 2, entrance_y - 1, 3),
122            (entrance_x - 2, entrance_y + 2, 3),
123        ] {
124            Self::recursive_corridor(&mut base, rng, x, y, Direction::West, depth);
125        }
126        while Self::clean_edges(&mut base) {}
127
128        let mut floor_rooms = [
129            SimpleGrid::new(11, 11, 5),
130            SimpleGrid::new(11, 11, 5),
131            SimpleGrid::new(11, 11, 5),
132        ];
133        Self::identify_rooms(&base, &mut floor_rooms[0], rng);
134        Self::identify_rooms(&base, &mut floor_rooms[1], rng);
135        for room in &mut floor_rooms[0..2] {
136            room.set_range(
137                entrance_x + 1,
138                entrance_y,
139                entrance_x + 1,
140                entrance_y + 1,
141                ROOM_CORRIDOR_FLAG,
142            );
143        }
144
145        let mut third = SimpleGrid::new(base.width, base.height, 5);
146        Self::setup_third_floor(&base, &mut third, &mut floor_rooms, rng);
147        Self::identify_rooms(&third, &mut floor_rooms[2], rng);
148
149        Self {
150            base_grid: base,
151            third_floor_grid: third,
152            floor_rooms,
153            entrance_x,
154            entrance_y,
155        }
156    }
157
158    pub(super) fn recursive_corridor(
159        grid: &mut SimpleGrid,
160        rng: &mut LegacyRandom,
161        x: i32,
162        y: i32,
163        heading: Direction,
164        depth: i32,
165    ) {
166        if depth <= 0 {
167            return;
168        }
169        grid.set_cell(x, y, 1);
170        let (hx, hz) = heading.offset_xz();
171        grid.setif(x + hx, y + hz, 0, 1);
172
173        for _ in 0..8 {
174            let next_dir = dir_from_2d(rng.next_i32_bounded(4));
175            if next_dir == heading.opposite() || (next_dir == Direction::East && rng.next_bool()) {
176                continue;
177            }
178            let (nx, ny) = (x + hx, y + hz);
179            let (ndx, ndz) = next_dir.offset_xz();
180            if grid.get(nx + ndx, ny + ndz) == 0 && grid.get(nx + ndx * 2, ny + ndz * 2) == 0 {
181                Self::recursive_corridor(
182                    grid,
183                    rng,
184                    x + hx + ndx,
185                    y + hz + ndz,
186                    next_dir,
187                    depth - 1,
188                );
189                break;
190            }
191        }
192
193        let cw = heading.rotate_y_clockwise();
194        let ccw = heading.rotate_y_counter_clockwise();
195        let a_cw_off = cw.offset_vec();
196        let b_ccw_off = ccw.offset_vec();
197        grid.setif(x + a_cw_off.x, y + a_cw_off.z, 0, 2);
198        grid.setif(x + b_ccw_off.x, y + b_ccw_off.z, 0, 2);
199        grid.setif(x + hx + a_cw_off.x, y + hz + a_cw_off.z, 0, 2);
200        grid.setif(x + hx + b_ccw_off.x, y + hz + b_ccw_off.z, 0, 2);
201        grid.setif(x + hx * 2, y + hz * 2, 0, 2);
202        grid.setif(x + a_cw_off.x * 2, y + a_cw_off.z * 2, 0, 2);
203        grid.setif(x + b_ccw_off.x * 2, y + b_ccw_off.z * 2, 0, 2);
204    }
205
206    pub(super) fn clean_edges(grid: &mut SimpleGrid) -> bool {
207        let mut touched = false;
208        for y in 0..grid.height {
209            for x in 0..grid.width {
210                if grid.get(x, y) != 0 {
211                    continue;
212                }
213                let direct = i32::from(is_house(grid, x + 1, y))
214                    + i32::from(is_house(grid, x - 1, y))
215                    + i32::from(is_house(grid, x, y + 1))
216                    + i32::from(is_house(grid, x, y - 1));
217                if direct >= 3 {
218                    grid.set_cell(x, y, 2);
219                    touched = true;
220                } else if direct == 2 {
221                    let diag = i32::from(is_house(grid, x + 1, y + 1))
222                        + i32::from(is_house(grid, x - 1, y + 1))
223                        + i32::from(is_house(grid, x + 1, y - 1))
224                        + i32::from(is_house(grid, x - 1, y - 1));
225                    if diag <= 1 {
226                        grid.set_cell(x, y, 2);
227                        touched = true;
228                    }
229                }
230            }
231        }
232        touched
233    }
234
235    pub(super) fn identify_rooms(
236        from: &SimpleGrid,
237        rooms: &mut SimpleGrid,
238        rng: &mut LegacyRandom,
239    ) {
240        let mut positions: Vec<(i32, i32)> = Vec::new();
241        for y in 0..from.height {
242            for x in 0..from.width {
243                if from.get(x, y) == 2 {
244                    positions.push((x, y));
245                }
246            }
247        }
248        let len = positions.len();
249        for i in (1..len).rev() {
250            let j = rng.next_i32_bounded((i + 1) as i32) as usize;
251            positions.swap(i, j);
252        }
253
254        let mut room_id = 10;
255        for &(x, y) in &positions {
256            if rooms.get(x, y) != 0 {
257                continue;
258            }
259            let (mut x0, mut x1, mut y0, mut y1) = (x, x, y, y);
260            let mut rtype = ROOM_1X1;
261
262            if rooms.get(x + 1, y) == 0
263                && rooms.get(x, y + 1) == 0
264                && rooms.get(x + 1, y + 1) == 0
265                && from.get(x + 1, y) == 2
266                && from.get(x, y + 1) == 2
267                && from.get(x + 1, y + 1) == 2
268            {
269                x1 = x + 1;
270                y1 = y + 1;
271                rtype = ROOM_2X2;
272            } else if rooms.get(x - 1, y) == 0
273                && rooms.get(x, y + 1) == 0
274                && rooms.get(x - 1, y + 1) == 0
275                && from.get(x - 1, y) == 2
276                && from.get(x, y + 1) == 2
277                && from.get(x - 1, y + 1) == 2
278            {
279                x0 = x - 1;
280                y1 = y + 1;
281                rtype = ROOM_2X2;
282            } else if rooms.get(x - 1, y) == 0
283                && rooms.get(x, y - 1) == 0
284                && rooms.get(x - 1, y - 1) == 0
285                && from.get(x - 1, y) == 2
286                && from.get(x, y - 1) == 2
287                && from.get(x - 1, y - 1) == 2
288            {
289                x0 = x - 1;
290                y0 = y - 1;
291                rtype = ROOM_2X2;
292            } else if rooms.get(x + 1, y) == 0 && from.get(x + 1, y) == 2 {
293                x1 = x + 1;
294                rtype = ROOM_1X2;
295            } else if rooms.get(x, y + 1) == 0 && from.get(x, y + 1) == 2 {
296                y1 = y + 1;
297                rtype = ROOM_1X2;
298            } else if rooms.get(x - 1, y) == 0 && from.get(x - 1, y) == 2 {
299                x0 = x - 1;
300                rtype = ROOM_1X2;
301            } else if rooms.get(x, y - 1) == 0 && from.get(x, y - 1) == 2 {
302                y0 = y - 1;
303                rtype = ROOM_1X2;
304            }
305
306            let mut door_x = if rng.next_bool() { x0 } else { x1 };
307            let mut door_y = if rng.next_bool() { y0 } else { y1 };
308            let mut door_flag = ROOM_DOOR_FLAG;
309            if !from.edges_to(door_x, door_y, 1) {
310                door_x = if door_x == x0 { x1 } else { x0 };
311                door_y = if door_y == y0 { y1 } else { y0 };
312                if !from.edges_to(door_x, door_y, 1) {
313                    door_y = if door_y == y0 { y1 } else { y0 };
314                    if !from.edges_to(door_x, door_y, 1) {
315                        door_x = if door_x == x0 { x1 } else { x0 };
316                        door_y = if door_y == y0 { y1 } else { y0 };
317                        if !from.edges_to(door_x, door_y, 1) {
318                            door_flag = 0;
319                            door_x = x0;
320                            door_y = y0;
321                        }
322                    }
323                }
324            }
325
326            for ry in y0..=y1 {
327                for rx in x0..=x1 {
328                    if rx == door_x && ry == door_y {
329                        rooms.set_cell(rx, ry, ROOM_ORIGIN_FLAG | door_flag | rtype | room_id);
330                    } else {
331                        rooms.set_cell(rx, ry, rtype | room_id);
332                    }
333                }
334            }
335            room_id += 1;
336        }
337    }
338
339    pub(super) fn setup_third_floor(
340        base: &SimpleGrid,
341        third: &mut SimpleGrid,
342        floor_rooms: &mut [SimpleGrid; 3],
343        rng: &mut LegacyRandom,
344    ) {
345        let mut potential: Vec<(i32, i32)> = Vec::new();
346        for y in 0..third.height {
347            for x in 0..third.width {
348                let data = floor_rooms[1].get(x, y);
349                if (data & ROOM_TYPE_MASK) == ROOM_1X2 && (data & ROOM_DOOR_FLAG) != 0 {
350                    potential.push((x, y));
351                }
352            }
353        }
354
355        if potential.is_empty() {
356            third.set_range(0, 0, third.width, third.height, 5);
357            return;
358        }
359
360        let &(rx, ry) = &potential[rng.next_i32_bounded(potential.len() as i32) as usize];
361        let room_data = floor_rooms[1].get(rx, ry);
362        floor_rooms[1].set_cell(rx, ry, room_data | ROOM_STAIRS_FLAG);
363
364        let room_id = room_data & ROOM_ID_MASK;
365        let room_dir = Self::get_1x2_room_direction_static(&floor_rooms[1], rx, ry, room_id);
366        let (rex, rey) = match room_dir {
367            Some(d) => {
368                let off = d.offset_vec();
369                (rx + off.x, ry + off.z)
370            }
371            None => (rx, ry),
372        };
373
374        for y in 0..third.height {
375            for x in 0..third.width {
376                if !is_house(base, x, y) {
377                    third.set_cell(x, y, 5);
378                } else if x == rx && y == ry {
379                    third.set_cell(x, y, 3);
380                } else if x == rex && y == rey {
381                    third.set_cell(x, y, 3);
382                    floor_rooms[2].set_cell(x, y, ROOM_CORRIDOR_FLAG);
383                }
384            }
385        }
386
387        let mut potential_dirs: Vec<Direction> = Vec::new();
388        for dir in Direction::HORIZONTAL {
389            let (ox, oz) = dir.offset_xz();
390            if third.get(rex + ox, rey + oz) == 0 {
391                potential_dirs.push(dir);
392            }
393        }
394
395        if potential_dirs.is_empty() {
396            third.set_range(0, 0, third.width, third.height, 5);
397            floor_rooms[1].set_cell(rx, ry, room_data);
398        } else {
399            let corridor_dir =
400                potential_dirs[rng.next_i32_bounded(potential_dirs.len() as i32) as usize];
401            let (ox, oz) = corridor_dir.offset_xz();
402            Self::recursive_corridor(third, rng, rex + ox, rey + oz, corridor_dir, 4);
403            while Self::clean_edges(third) {}
404        }
405    }
406
407    pub(super) fn is_room_id(&self, x: i32, y: i32, floor: usize, room_id: i32) -> bool {
408        (self.floor_rooms[floor].get(x, y) & ROOM_ID_MASK) == room_id
409    }
410
411    pub(super) fn get_1x2_room_direction(
412        &self,
413        x: i32,
414        y: i32,
415        floor: usize,
416        room_id: i32,
417    ) -> Option<Direction> {
418        for dir in &[
419            Direction::North,
420            Direction::East,
421            Direction::South,
422            Direction::West,
423        ] {
424            let (ox, oz) = dir.offset_xz();
425            if self.is_room_id(x + ox, y + oz, floor, room_id) {
426                return Some(*dir);
427            }
428        }
429        None
430    }
431
432    pub(super) fn get_1x2_room_direction_static(
433        floor_rooms: &SimpleGrid,
434        x: i32,
435        y: i32,
436        room_id: i32,
437    ) -> Option<Direction> {
438        for dir in &[
439            Direction::North,
440            Direction::East,
441            Direction::South,
442            Direction::West,
443        ] {
444            let (ox, oz) = dir.offset_xz();
445            if (floor_rooms.get(x + ox, y + oz) & ROOM_ID_MASK) == room_id {
446                return Some(*dir);
447            }
448        }
449        None
450    }
451}