Skip to main content

steel_worldgen/structure/mansion/
placement.rs

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}