1use super::{Direction, IVec3, LegacyRandom, Random, Rotation};
2use super::{
3 grid::{
4 MansionGrid, ROOM_1X1, ROOM_1X2, ROOM_2X2, ROOM_CORRIDOR_FLAG, ROOM_DOOR_FLAG,
5 ROOM_ID_MASK, ROOM_ORIGIN_FLAG, ROOM_STAIRS_FLAG, ROOM_TYPE_MASK, is_house,
6 },
7 roof::create_roof,
8 rooms::{add_room_1x1, add_room_1x2, add_room_2x2, add_room_2x2_secret},
9 template::{MansionTemplatePiece, Mirror, above, add_piece, compose_rotation, relative},
10 walls::{place_entrance, traverse_outer_walls, traverse_wall_piece},
11};
12
13#[expect(
14 clippy::match_same_arms,
15 reason = "table kept one-per-case to match vanilla's FirstFloorRoomCollection / SecondFloor / ThirdFloor dispatch"
16)]
17pub(super) fn get_room_name(
18 rng: &mut LegacyRandom,
19 floor: usize,
20 kind: &str,
21 is_stairs: bool,
22) -> String {
23 match (floor, kind) {
24 (0, "1x1") => format!("1x1_a{}", rng.next_i32_bounded(5) + 1),
25 (0, "1x1s") => format!("1x1_as{}", rng.next_i32_bounded(4) + 1),
26 (0, "1x2side") => format!("1x2_a{}", rng.next_i32_bounded(9) + 1),
27 (0, "1x2front") => format!("1x2_b{}", rng.next_i32_bounded(5) + 1),
28 (0, "1x2secret") => format!("1x2_s{}", rng.next_i32_bounded(2) + 1),
29 (0, "2x2") => format!("2x2_a{}", rng.next_i32_bounded(4) + 1),
30 (0, "2x2secret") => "2x2_s1".to_string(),
31 (_, "1x1") => format!("1x1_b{}", rng.next_i32_bounded(5) + 1),
32 (_, "1x1s") => format!("1x1_as{}", rng.next_i32_bounded(4) + 1),
33 (_, "1x2side") => {
34 if is_stairs {
35 "1x2_c_stairs".to_string()
36 } else {
37 format!("1x2_c{}", rng.next_i32_bounded(4) + 1)
38 }
39 }
40 (_, "1x2front") => {
41 if is_stairs {
42 "1x2_d_stairs".to_string()
43 } else {
44 format!("1x2_d{}", rng.next_i32_bounded(5) + 1)
45 }
46 }
47 (_, "1x2secret") => format!("1x2_se{}", rng.next_i32_bounded(1) + 1),
48 (_, "2x2") => format!("2x2_b{}", rng.next_i32_bounded(5) + 1),
49 (_, "2x2secret") => "2x2_s1".to_string(),
50 _ => "corridor_floor".to_string(),
51 }
52}
53
54pub(super) struct PlacementData {
55 pub(super) position: IVec3,
56 pub(super) rotation: Rotation,
57 pub(super) wall_type: &'static str,
58}
59
60#[expect(
61 clippy::too_many_lines,
62 reason = "mirrors vanilla's MansionPiecePlacer traversal order"
63)]
64pub(super) fn generate_mansion_pieces(
65 origin: IVec3,
66 rotation: Rotation,
67 rng: &mut LegacyRandom,
68) -> Vec<MansionTemplatePiece> {
69 let mansion = MansionGrid::new(rng);
70 let start_x = mansion.entrance_x + 1;
71 let start_y = mansion.entrance_y + 1;
72 let end_x = mansion.entrance_x + 1;
73 let end_y = mansion.entrance_y;
74
75 let mut pieces: Vec<MansionTemplatePiece> = Vec::new();
76
77 let mut data = PlacementData {
78 position: origin,
79 rotation,
80 wall_type: "wall_flat",
81 };
82 place_entrance(&mut pieces, &mut data);
83
84 let mut second = PlacementData {
85 position: above(data.position, 8),
86 rotation: data.rotation,
87 wall_type: "wall_window",
88 };
89
90 traverse_outer_walls(
91 &mut pieces,
92 &mut data,
93 &mansion.base_grid,
94 Direction::South,
95 start_x,
96 start_y,
97 end_x,
98 end_y,
99 );
100 traverse_outer_walls(
101 &mut pieces,
102 &mut second,
103 &mansion.base_grid,
104 Direction::South,
105 start_x,
106 start_y,
107 end_x,
108 end_y,
109 );
110
111 let mut third_data = PlacementData {
112 position: above(data.position, 19),
113 rotation: data.rotation,
114 wall_type: "wall_window",
115 };
116
117 let mut done = false;
118 for y in 0..mansion.third_floor_grid.height {
119 if done {
120 break;
121 }
122 for x in (0..mansion.third_floor_grid.width).rev() {
123 if done {
124 break;
125 }
126 if is_house(&mansion.third_floor_grid, x, y) {
127 third_data.position = relative(
128 third_data.position,
129 rotation,
130 Direction::South,
131 8 + (y - start_y) * 8,
132 );
133 third_data.position = relative(
134 third_data.position,
135 rotation,
136 Direction::East,
137 (x - start_x) * 8,
138 );
139 traverse_wall_piece(&mut pieces, &mut third_data);
140 traverse_outer_walls(
141 &mut pieces,
142 &mut third_data,
143 &mansion.third_floor_grid,
144 Direction::South,
145 x,
146 y,
147 x,
148 y,
149 );
150 done = true;
151 }
152 }
153 }
154
155 create_roof(
156 &mut pieces,
157 above(origin, 16),
158 rotation,
159 &mansion.base_grid,
160 Some(&mansion.third_floor_grid),
161 start_x,
162 start_y,
163 );
164 create_roof(
165 &mut pieces,
166 above(origin, 27),
167 rotation,
168 &mansion.third_floor_grid,
169 None,
170 start_x,
171 start_y,
172 );
173
174 for floor_num in 0..3_usize {
175 let floor_origin = above(
176 origin,
177 8 * floor_num as i32 + if floor_num == 2 { 3 } else { 0 },
178 );
179 let rooms = &mansion.floor_rooms[floor_num];
180 let grid = if floor_num == 2 {
181 &mansion.third_floor_grid
182 } else {
183 &mansion.base_grid
184 };
185 let south_piece = if floor_num == 0 {
186 "carpet_south_1"
187 } else {
188 "carpet_south_2"
189 };
190 let west_piece = if floor_num == 0 {
191 "carpet_west_1"
192 } else {
193 "carpet_west_2"
194 };
195
196 for y in 0..grid.height {
197 for x in 0..grid.width {
198 if grid.get(x, y) == 1 {
199 let mut pos = relative(
200 floor_origin,
201 rotation,
202 Direction::South,
203 8 + (y - start_y) * 8,
204 );
205 pos = relative(pos, rotation, Direction::East, (x - start_x) * 8);
206 add_piece(&mut pieces, "corridor_floor", pos, rotation, Mirror::None);
207
208 if grid.get(x, y - 1) == 1 || (rooms.get(x, y - 1) & ROOM_CORRIDOR_FLAG) != 0 {
209 let p = above(
210 relative(
211 relative(pos, rotation, Direction::East, 1),
212 rotation,
213 Direction::South,
214 0,
215 ),
216 1,
217 );
218 add_piece(&mut pieces, "carpet_north", p, rotation, Mirror::None);
219 }
220 if grid.get(x + 1, y) == 1 || (rooms.get(x + 1, y) & ROOM_CORRIDOR_FLAG) != 0 {
221 let p = above(
222 relative(
223 relative(pos, rotation, Direction::South, 1),
224 rotation,
225 Direction::East,
226 5,
227 ),
228 1,
229 );
230 add_piece(&mut pieces, "carpet_east", p, rotation, Mirror::None);
231 }
232 if grid.get(x, y + 1) == 1 || (rooms.get(x, y + 1) & ROOM_CORRIDOR_FLAG) != 0 {
233 let p = relative(
234 relative(pos, rotation, Direction::South, 5),
235 rotation,
236 Direction::West,
237 1,
238 );
239 add_piece(&mut pieces, south_piece, p, rotation, Mirror::None);
240 }
241 if grid.get(x - 1, y) == 1 || (rooms.get(x - 1, y) & ROOM_CORRIDOR_FLAG) != 0 {
242 let p = relative(
243 relative(pos, rotation, Direction::West, 1),
244 rotation,
245 Direction::North,
246 1,
247 );
248 add_piece(&mut pieces, west_piece, p, rotation, Mirror::None);
249 }
250 }
251 }
252 }
253
254 let wall_piece = if floor_num == 0 {
255 "indoors_wall_1"
256 } else {
257 "indoors_wall_2"
258 };
259 let door_piece = if floor_num == 0 {
260 "indoors_door_1"
261 } else {
262 "indoors_door_2"
263 };
264
265 for y in 0..grid.height {
266 for x in 0..grid.width {
267 let is_third_start = floor_num == 2 && grid.get(x, y) == 3;
268 if grid.get(x, y) != 2 && !is_third_start {
269 continue;
270 }
271 let room_data = rooms.get(x, y);
272 let room_type = room_data & ROOM_TYPE_MASK;
273 let room_id = room_data & ROOM_ID_MASK;
274 let is_corridor_start = is_third_start && (room_data & ROOM_CORRIDOR_FLAG) != 0;
275
276 let mut door_dirs: Vec<Direction> = Vec::new();
277 if (room_data & ROOM_DOOR_FLAG) != 0 {
278 for dir in Direction::HORIZONTAL {
279 let (ox, oz) = dir.offset_xz();
280 if grid.get(x + ox, y + oz) == 1 {
281 door_dirs.push(dir);
282 }
283 }
284 }
285
286 let door_dir: Option<Direction> = if !door_dirs.is_empty() {
287 Some(door_dirs[rng.next_i32_bounded(door_dirs.len() as i32) as usize])
288 } else if (room_data & ROOM_ORIGIN_FLAG) != 0 {
289 Some(Direction::Up)
290 } else {
291 None
292 };
293
294 let mut room_pos = relative(
295 floor_origin,
296 rotation,
297 Direction::South,
298 8 + (y - start_y) * 8,
299 );
300 room_pos = relative(room_pos, rotation, Direction::East, -1 + (x - start_x) * 8);
301
302 if is_house(grid, x - 1, y) && !mansion.is_room_id(x - 1, y, floor_num, room_id) {
303 let template = if door_dir == Some(Direction::West) {
304 door_piece
305 } else {
306 wall_piece
307 };
308 add_piece(&mut pieces, template, room_pos, rotation, Mirror::None);
309 }
310
311 if grid.get(x + 1, y) == 1 && !is_corridor_start {
312 let p = relative(room_pos, rotation, Direction::East, 8);
313 let template = if door_dir == Some(Direction::East) {
314 door_piece
315 } else {
316 wall_piece
317 };
318 add_piece(&mut pieces, template, p, rotation, Mirror::None);
319 }
320
321 if is_house(grid, x, y + 1) && !mansion.is_room_id(x, y + 1, floor_num, room_id) {
322 let p = relative(
323 relative(room_pos, rotation, Direction::South, 7),
324 rotation,
325 Direction::East,
326 7,
327 );
328 let template = if door_dir == Some(Direction::South) {
329 door_piece
330 } else {
331 wall_piece
332 };
333 add_piece(
334 &mut pieces,
335 template,
336 p,
337 compose_rotation(rotation, Rotation::Clockwise90),
338 Mirror::None,
339 );
340 }
341
342 if grid.get(x, y - 1) == 1 && !is_corridor_start {
343 let p = relative(
344 relative(room_pos, rotation, Direction::North, 1),
345 rotation,
346 Direction::East,
347 7,
348 );
349 let template = if door_dir == Some(Direction::North) {
350 door_piece
351 } else {
352 wall_piece
353 };
354 add_piece(
355 &mut pieces,
356 template,
357 p,
358 compose_rotation(rotation, Rotation::Clockwise90),
359 Mirror::None,
360 );
361 }
362
363 if room_type == ROOM_1X1 {
364 add_room_1x1(&mut pieces, room_pos, rotation, door_dir, floor_num, rng);
365 } else if room_type == ROOM_1X2 && door_dir.is_some() {
366 let room_dir = mansion.get_1x2_room_direction(x, y, floor_num, room_id);
367 let is_stairs = (room_data & ROOM_STAIRS_FLAG) != 0;
368 if let (Some(rd), Some(dd)) = (room_dir, door_dir) {
369 add_room_1x2(
370 &mut pieces,
371 room_pos,
372 rotation,
373 rd,
374 dd,
375 floor_num,
376 is_stairs,
377 rng,
378 );
379 }
380 } else if let (ROOM_2X2, Some(dd)) = (room_type, door_dir)
381 && dd != Direction::Up
382 {
383 let mut room_dir = dd.rotate_y_clockwise();
384 let (ox, oz) = room_dir.offset_xz();
385 if !mansion.is_room_id(x + ox, y + oz, floor_num, room_id) {
386 room_dir = room_dir.opposite();
387 }
388 add_room_2x2(
389 &mut pieces,
390 room_pos,
391 rotation,
392 room_dir,
393 dd,
394 floor_num,
395 rng,
396 );
397 } else if room_type == ROOM_2X2 && door_dir == Some(Direction::Up) {
398 add_room_2x2_secret(&mut pieces, room_pos, rotation, floor_num, rng);
399 }
400 }
401 }
402 }
403
404 pieces
405}