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}