Skip to main content

steel_core/worldgen/structure/piece_placer/
stronghold.rs

1use steel_registry::blocks::block_state_ext::BlockStateExt as _;
2use steel_registry::blocks::properties::{
3    AttachFace, BlockStateProperties, DoubleBlockHalf, SlabType,
4};
5use steel_registry::{Registry, vanilla_blocks};
6use steel_utils::random::Random;
7use steel_utils::random::worldgen_random::WorldgenRandom;
8use steel_utils::{BlockStateId, BoundingBox, Direction};
9
10use crate::worldgen::region::WorldGenRegion;
11use steel_worldgen::structure::stronghold::{StrongholdPieceData, StrongholdSmallDoorType};
12
13use super::{StructurePiecePlacer, scattered_feature::ScatteredFeaturePlacer};
14
15const STRONGHOLD_CORRIDOR_LOOT: &str = "minecraft:chests/stronghold_corridor";
16const STRONGHOLD_CROSSING_LOOT: &str = "minecraft:chests/stronghold_crossing";
17const STRONGHOLD_LIBRARY_LOOT: &str = "minecraft:chests/stronghold_library";
18const SILVERFISH_ENTITY: &str = "minecraft:silverfish";
19
20impl StructurePiecePlacer {
21    pub(super) fn place_stronghold_piece(
22        region: &mut WorldGenRegion<'_>,
23        registry: &Registry,
24        bounding_box: BoundingBox,
25        orientation: Option<Direction>,
26        data: &mut StrongholdPieceData,
27        clip: BoundingBox,
28        random: &mut WorldgenRandom,
29    ) -> bool {
30        let Some(
31            orientation @ (Direction::North | Direction::South | Direction::West | Direction::East),
32        ) = orientation
33        else {
34            return false;
35        };
36
37        let mut piece_bounding_box = bounding_box;
38        let mut placer = ScatteredFeaturePlacer::new(
39            region,
40            registry,
41            &mut piece_bounding_box,
42            Some(orientation),
43            clip,
44        );
45        match data {
46            StrongholdPieceData::Straight {
47                entry_door,
48                left_child,
49                right_child,
50            } => place_straight(&mut placer, random, *entry_door, *left_child, *right_child),
51            StrongholdPieceData::PrisonHall { entry_door } => {
52                place_prison_hall(&mut placer, random, *entry_door);
53            }
54            StrongholdPieceData::LeftTurn { entry_door } => {
55                place_turn(&mut placer, random, *entry_door, true, orientation);
56            }
57            StrongholdPieceData::RightTurn { entry_door } => {
58                place_turn(&mut placer, random, *entry_door, false, orientation);
59            }
60            StrongholdPieceData::RoomCrossing {
61                entry_door,
62                crossing_type,
63            } => place_room_crossing(&mut placer, random, *entry_door, *crossing_type),
64            StrongholdPieceData::StraightStairsDown { entry_door } => {
65                place_straight_stairs_down(&mut placer, random, *entry_door);
66            }
67            StrongholdPieceData::StairsDown { entry_door, .. } => {
68                place_stairs_down(&mut placer, random, *entry_door);
69            }
70            StrongholdPieceData::FiveCrossing {
71                entry_door,
72                left_low,
73                left_high,
74                right_low,
75                right_high,
76            } => place_five_crossing(
77                &mut placer,
78                random,
79                *entry_door,
80                *left_low,
81                *left_high,
82                *right_low,
83                *right_high,
84            ),
85            StrongholdPieceData::ChestCorridor {
86                entry_door,
87                has_placed_chest,
88            } => place_chest_corridor(&mut placer, random, *entry_door, has_placed_chest),
89            StrongholdPieceData::Library {
90                entry_door,
91                is_tall,
92            } => place_library(&mut placer, random, *entry_door, *is_tall),
93            StrongholdPieceData::PortalRoom { has_placed_spawner } => {
94                place_portal_room(&mut placer, random, has_placed_spawner);
95            }
96            StrongholdPieceData::FillerCorridor { steps } => {
97                place_filler_corridor(&mut placer, *steps);
98            }
99        }
100        true
101    }
102}
103
104fn stone_bricks() -> BlockStateId {
105    vanilla_blocks::STONE_BRICKS.default_state()
106}
107
108fn cracked_stone_bricks() -> BlockStateId {
109    vanilla_blocks::CRACKED_STONE_BRICKS.default_state()
110}
111
112fn mossy_stone_bricks() -> BlockStateId {
113    vanilla_blocks::MOSSY_STONE_BRICKS.default_state()
114}
115
116fn infested_stone_bricks() -> BlockStateId {
117    vanilla_blocks::INFESTED_STONE_BRICKS.default_state()
118}
119
120fn cave_air() -> BlockStateId {
121    vanilla_blocks::CAVE_AIR.default_state()
122}
123
124fn smooth_stone_slab() -> BlockStateId {
125    vanilla_blocks::SMOOTH_STONE_SLAB.default_state()
126}
127
128fn double_smooth_stone_slab() -> BlockStateId {
129    vanilla_blocks::SMOOTH_STONE_SLAB
130        .default_state()
131        .set_value(&BlockStateProperties::SLAB_TYPE, SlabType::Double)
132}
133
134fn wall_torch(facing: Direction) -> BlockStateId {
135    vanilla_blocks::WALL_TORCH
136        .default_state()
137        .set_value(&BlockStateProperties::HORIZONTAL_FACING, facing)
138}
139
140fn ladder(facing: Direction) -> BlockStateId {
141    vanilla_blocks::LADDER
142        .default_state()
143        .set_value(&BlockStateProperties::FACING, facing)
144}
145
146fn stone_brick_stairs(facing: Direction) -> BlockStateId {
147    vanilla_blocks::STONE_BRICK_STAIRS
148        .default_state()
149        .set_value(&BlockStateProperties::FACING, facing)
150}
151
152fn cobblestone_stairs(facing: Direction) -> BlockStateId {
153    vanilla_blocks::COBBLESTONE_STAIRS
154        .default_state()
155        .set_value(&BlockStateProperties::FACING, facing)
156}
157
158const CONNECT_NORTH: u8 = 1;
159const CONNECT_EAST: u8 = 2;
160const CONNECT_SOUTH: u8 = 4;
161const CONNECT_WEST: u8 = 8;
162
163fn iron_bars(mask: u8) -> BlockStateId {
164    vanilla_blocks::IRON_BARS
165        .default_state()
166        .set_value(&BlockStateProperties::NORTH, mask & CONNECT_NORTH != 0)
167        .set_value(&BlockStateProperties::EAST, mask & CONNECT_EAST != 0)
168        .set_value(&BlockStateProperties::SOUTH, mask & CONNECT_SOUTH != 0)
169        .set_value(&BlockStateProperties::WEST, mask & CONNECT_WEST != 0)
170}
171
172fn oak_fence(mask: u8) -> BlockStateId {
173    vanilla_blocks::OAK_FENCE
174        .default_state()
175        .set_value(&BlockStateProperties::NORTH, mask & CONNECT_NORTH != 0)
176        .set_value(&BlockStateProperties::EAST, mask & CONNECT_EAST != 0)
177        .set_value(&BlockStateProperties::SOUTH, mask & CONNECT_SOUTH != 0)
178        .set_value(&BlockStateProperties::WEST, mask & CONNECT_WEST != 0)
179}
180
181fn upper_door(state: BlockStateId) -> BlockStateId {
182    state.set_value(
183        &BlockStateProperties::DOUBLE_BLOCK_HALF,
184        DoubleBlockHalf::Upper,
185    )
186}
187
188fn stone_button(facing: Direction) -> BlockStateId {
189    vanilla_blocks::STONE_BUTTON
190        .default_state()
191        .set_value(&BlockStateProperties::ATTACH_FACE, AttachFace::Wall)
192        .set_value(&BlockStateProperties::HORIZONTAL_FACING, facing)
193}
194
195fn end_portal_frame(facing: Direction, has_eye: bool) -> BlockStateId {
196    vanilla_blocks::END_PORTAL_FRAME
197        .default_state()
198        .set_value(&BlockStateProperties::HORIZONTAL_FACING, facing)
199        .set_value(&BlockStateProperties::EYE, has_eye)
200}
201
202fn smooth_stone_selector(
203    random: &mut WorldgenRandom,
204    _x: i32,
205    _y: i32,
206    _z: i32,
207    is_edge: bool,
208) -> BlockStateId {
209    if !is_edge {
210        return cave_air();
211    }
212
213    let selection = random.next_f32();
214    if selection < 0.2 {
215        cracked_stone_bricks()
216    } else if selection < 0.5 {
217        mossy_stone_bricks()
218    } else if selection < 0.55 {
219        infested_stone_bricks()
220    } else {
221        stone_bricks()
222    }
223}
224
225#[expect(
226    clippy::too_many_arguments,
227    reason = "mirrors vanilla StructurePiece.generateBox selector overload"
228)]
229fn stronghold_box(
230    placer: &mut ScatteredFeaturePlacer<'_, '_>,
231    random: &mut WorldgenRandom,
232    x0: i32,
233    y0: i32,
234    z0: i32,
235    x1: i32,
236    y1: i32,
237    z1: i32,
238    skip_air: bool,
239) {
240    placer.generate_box_with_selector(
241        x0,
242        y0,
243        z0,
244        x1,
245        y1,
246        z1,
247        skip_air,
248        random,
249        smooth_stone_selector,
250    );
251}
252
253#[expect(
254    clippy::too_many_arguments,
255    reason = "mirrors vanilla StructurePiece.generateMaybeBox for stronghold cobwebs"
256)]
257fn generate_maybe_box(
258    placer: &mut ScatteredFeaturePlacer<'_, '_>,
259    random: &mut WorldgenRandom,
260    probability: f32,
261    x0: i32,
262    y0: i32,
263    z0: i32,
264    x1: i32,
265    y1: i32,
266    z1: i32,
267    edge: BlockStateId,
268    fill: BlockStateId,
269) {
270    for y in y0..=y1 {
271        for x in x0..=x1 {
272            for z in z0..=z1 {
273                if random.next_f32() > probability {
274                    continue;
275                }
276                let state = if y != y0 && y != y1 && x != x0 && x != x1 && z != z0 && z != z1 {
277                    fill
278                } else {
279                    edge
280                };
281                placer.place_block(state, x, y, z);
282            }
283        }
284    }
285}
286
287fn maybe_generate_block(
288    placer: &mut ScatteredFeaturePlacer<'_, '_>,
289    random: &mut WorldgenRandom,
290    probability: f32,
291    x: i32,
292    y: i32,
293    z: i32,
294    state: BlockStateId,
295) {
296    if random.next_f32() < probability {
297        placer.place_block(state, x, y, z);
298    }
299}
300
301fn generate_small_door(
302    placer: &mut ScatteredFeaturePlacer<'_, '_>,
303    door_type: StrongholdSmallDoorType,
304    foot_x: i32,
305    foot_y: i32,
306    foot_z: i32,
307) {
308    match door_type {
309        StrongholdSmallDoorType::Opening => {
310            placer.generate_box(
311                foot_x,
312                foot_y,
313                foot_z,
314                foot_x + 2,
315                foot_y + 2,
316                foot_z,
317                cave_air(),
318                cave_air(),
319                false,
320            );
321        }
322        StrongholdSmallDoorType::WoodDoor => {
323            let stone_bricks = stone_bricks();
324            placer.place_block(stone_bricks, foot_x, foot_y, foot_z);
325            placer.place_block(stone_bricks, foot_x, foot_y + 1, foot_z);
326            placer.place_block(stone_bricks, foot_x, foot_y + 2, foot_z);
327            placer.place_block(stone_bricks, foot_x + 1, foot_y + 2, foot_z);
328            placer.place_block(stone_bricks, foot_x + 2, foot_y + 2, foot_z);
329            placer.place_block(stone_bricks, foot_x + 2, foot_y + 1, foot_z);
330            placer.place_block(stone_bricks, foot_x + 2, foot_y, foot_z);
331            let door = vanilla_blocks::OAK_DOOR.default_state();
332            placer.place_block(door, foot_x + 1, foot_y, foot_z);
333            placer.place_block(upper_door(door), foot_x + 1, foot_y + 1, foot_z);
334        }
335        StrongholdSmallDoorType::Grates => {
336            let air = cave_air();
337            placer.place_block(air, foot_x + 1, foot_y, foot_z);
338            placer.place_block(air, foot_x + 1, foot_y + 1, foot_z);
339            let west_bars = iron_bars(CONNECT_WEST);
340            placer.place_block(west_bars, foot_x, foot_y, foot_z);
341            placer.place_block(west_bars, foot_x, foot_y + 1, foot_z);
342            let east_west_bars = iron_bars(CONNECT_EAST | CONNECT_WEST);
343            placer.place_block(east_west_bars, foot_x, foot_y + 2, foot_z);
344            placer.place_block(east_west_bars, foot_x + 1, foot_y + 2, foot_z);
345            placer.place_block(east_west_bars, foot_x + 2, foot_y + 2, foot_z);
346            let east_bars = iron_bars(CONNECT_EAST);
347            placer.place_block(east_bars, foot_x + 2, foot_y + 1, foot_z);
348            placer.place_block(east_bars, foot_x + 2, foot_y, foot_z);
349        }
350        StrongholdSmallDoorType::IronDoor => {
351            let stone_bricks = stone_bricks();
352            placer.place_block(stone_bricks, foot_x, foot_y, foot_z);
353            placer.place_block(stone_bricks, foot_x, foot_y + 1, foot_z);
354            placer.place_block(stone_bricks, foot_x, foot_y + 2, foot_z);
355            placer.place_block(stone_bricks, foot_x + 1, foot_y + 2, foot_z);
356            placer.place_block(stone_bricks, foot_x + 2, foot_y + 2, foot_z);
357            placer.place_block(stone_bricks, foot_x + 2, foot_y + 1, foot_z);
358            placer.place_block(stone_bricks, foot_x + 2, foot_y, foot_z);
359            let door = vanilla_blocks::IRON_DOOR.default_state();
360            placer.place_block(door, foot_x + 1, foot_y, foot_z);
361            placer.place_block(upper_door(door), foot_x + 1, foot_y + 1, foot_z);
362            placer.place_block(
363                stone_button(Direction::North),
364                foot_x + 2,
365                foot_y + 1,
366                foot_z + 1,
367            );
368            placer.place_block(
369                stone_button(Direction::South),
370                foot_x + 2,
371                foot_y + 1,
372                foot_z - 1,
373            );
374        }
375    }
376}
377
378fn place_chest_corridor(
379    placer: &mut ScatteredFeaturePlacer<'_, '_>,
380    random: &mut WorldgenRandom,
381    entry_door: StrongholdSmallDoorType,
382    has_placed_chest: &mut bool,
383) {
384    stronghold_box(placer, random, 0, 0, 0, 4, 4, 6, true);
385    generate_small_door(placer, entry_door, 1, 1, 0);
386    generate_small_door(placer, StrongholdSmallDoorType::Opening, 1, 1, 6);
387    let stone_bricks = stone_bricks();
388    placer.generate_box(3, 1, 2, 3, 1, 4, stone_bricks, stone_bricks, false);
389    let slab = smooth_stone_slab();
390    placer.place_block(slab, 3, 1, 1);
391    placer.place_block(slab, 3, 1, 5);
392    placer.place_block(slab, 3, 2, 2);
393    placer.place_block(slab, 3, 2, 4);
394
395    for z in 2..=4 {
396        placer.place_block(slab, 2, 1, z);
397    }
398
399    let chest_pos = placer.world_pos(3, 2, 3);
400    if !*has_placed_chest && placer.clip().contains_blockpos(chest_pos) {
401        *has_placed_chest = true;
402        let _ = placer.create_chest(random, 3, 2, 3, STRONGHOLD_CORRIDOR_LOOT);
403    }
404}
405
406fn place_filler_corridor(placer: &mut ScatteredFeaturePlacer<'_, '_>, steps: i32) {
407    let stone_bricks = stone_bricks();
408    let air = cave_air();
409    for i in 0..steps {
410        placer.place_block(stone_bricks, 0, 0, i);
411        placer.place_block(stone_bricks, 1, 0, i);
412        placer.place_block(stone_bricks, 2, 0, i);
413        placer.place_block(stone_bricks, 3, 0, i);
414        placer.place_block(stone_bricks, 4, 0, i);
415
416        for y in 1..=3 {
417            placer.place_block(stone_bricks, 0, y, i);
418            placer.place_block(air, 1, y, i);
419            placer.place_block(air, 2, y, i);
420            placer.place_block(air, 3, y, i);
421            placer.place_block(stone_bricks, 4, y, i);
422        }
423
424        placer.place_block(stone_bricks, 0, 4, i);
425        placer.place_block(stone_bricks, 1, 4, i);
426        placer.place_block(stone_bricks, 2, 4, i);
427        placer.place_block(stone_bricks, 3, 4, i);
428        placer.place_block(stone_bricks, 4, 4, i);
429    }
430}
431
432#[expect(
433    clippy::fn_params_excessive_bools,
434    reason = "straight transcription of vanilla StrongholdPieces.FiveCrossing"
435)]
436fn place_five_crossing(
437    placer: &mut ScatteredFeaturePlacer<'_, '_>,
438    random: &mut WorldgenRandom,
439    entry_door: StrongholdSmallDoorType,
440    left_low: bool,
441    left_high: bool,
442    right_low: bool,
443    right_high: bool,
444) {
445    stronghold_box(placer, random, 0, 0, 0, 9, 8, 10, true);
446    generate_small_door(placer, entry_door, 4, 3, 0);
447    let air = cave_air();
448    if left_low {
449        placer.generate_box(0, 3, 1, 0, 5, 3, air, air, false);
450    }
451    if right_low {
452        placer.generate_box(9, 3, 1, 9, 5, 3, air, air, false);
453    }
454    if left_high {
455        placer.generate_box(0, 5, 7, 0, 7, 9, air, air, false);
456    }
457    if right_high {
458        placer.generate_box(9, 5, 7, 9, 7, 9, air, air, false);
459    }
460
461    placer.generate_box(5, 1, 10, 7, 3, 10, air, air, false);
462    stronghold_box(placer, random, 1, 2, 1, 8, 2, 6, false);
463    stronghold_box(placer, random, 4, 1, 5, 4, 4, 9, false);
464    stronghold_box(placer, random, 8, 1, 5, 8, 4, 9, false);
465    stronghold_box(placer, random, 1, 4, 7, 3, 4, 9, false);
466    stronghold_box(placer, random, 1, 3, 5, 3, 3, 6, false);
467    let slab = smooth_stone_slab();
468    placer.generate_box(1, 3, 4, 3, 3, 4, slab, slab, false);
469    placer.generate_box(1, 4, 6, 3, 4, 6, slab, slab, false);
470    stronghold_box(placer, random, 5, 1, 7, 7, 1, 8, false);
471    placer.generate_box(5, 1, 9, 7, 1, 9, slab, slab, false);
472    placer.generate_box(5, 2, 7, 7, 2, 7, slab, slab, false);
473    placer.generate_box(4, 5, 7, 4, 5, 9, slab, slab, false);
474    placer.generate_box(8, 5, 7, 8, 5, 9, slab, slab, false);
475    let double_slab = double_smooth_stone_slab();
476    placer.generate_box(5, 5, 7, 7, 5, 9, double_slab, double_slab, false);
477    placer.place_block(wall_torch(Direction::South), 6, 5, 6);
478}
479
480fn place_turn(
481    placer: &mut ScatteredFeaturePlacer<'_, '_>,
482    random: &mut WorldgenRandom,
483    entry_door: StrongholdSmallDoorType,
484    is_left_turn: bool,
485    orientation: Direction,
486) {
487    stronghold_box(placer, random, 0, 0, 0, 4, 4, 4, true);
488    generate_small_door(placer, entry_door, 1, 1, 0);
489    let opens_west = if is_left_turn {
490        matches!(orientation, Direction::North | Direction::East)
491    } else {
492        !matches!(orientation, Direction::North | Direction::East)
493    };
494    let air = cave_air();
495    if opens_west {
496        placer.generate_box(0, 1, 1, 0, 3, 3, air, air, false);
497    } else {
498        placer.generate_box(4, 1, 1, 4, 3, 3, air, air, false);
499    }
500}
501
502#[expect(
503    clippy::too_many_lines,
504    reason = "straight transcription of vanilla StrongholdPieces.Library"
505)]
506fn place_library(
507    placer: &mut ScatteredFeaturePlacer<'_, '_>,
508    random: &mut WorldgenRandom,
509    entry_door: StrongholdSmallDoorType,
510    is_tall: bool,
511) {
512    let current_height = if is_tall { 11 } else { 6 };
513    stronghold_box(placer, random, 0, 0, 0, 13, current_height - 1, 14, true);
514    generate_small_door(placer, entry_door, 4, 1, 0);
515    let cobweb = vanilla_blocks::COBWEB.default_state();
516    generate_maybe_box(placer, random, 0.07, 2, 1, 1, 11, 4, 13, cobweb, cobweb);
517    let oak_planks = vanilla_blocks::OAK_PLANKS.default_state();
518    let bookshelf = vanilla_blocks::BOOKSHELF.default_state();
519
520    for depth in 1..=13 {
521        if (depth - 1) % 4 == 0 {
522            placer.generate_box(1, 1, depth, 1, 4, depth, oak_planks, oak_planks, false);
523            placer.generate_box(12, 1, depth, 12, 4, depth, oak_planks, oak_planks, false);
524            placer.place_block(wall_torch(Direction::East), 2, 3, depth);
525            placer.place_block(wall_torch(Direction::West), 11, 3, depth);
526            if is_tall {
527                placer.generate_box(1, 6, depth, 1, 9, depth, oak_planks, oak_planks, false);
528                placer.generate_box(12, 6, depth, 12, 9, depth, oak_planks, oak_planks, false);
529            }
530        } else {
531            placer.generate_box(1, 1, depth, 1, 4, depth, bookshelf, bookshelf, false);
532            placer.generate_box(12, 1, depth, 12, 4, depth, bookshelf, bookshelf, false);
533            if is_tall {
534                placer.generate_box(1, 6, depth, 1, 9, depth, bookshelf, bookshelf, false);
535                placer.generate_box(12, 6, depth, 12, 9, depth, bookshelf, bookshelf, false);
536            }
537        }
538    }
539
540    for dx in (3..12).step_by(2) {
541        placer.generate_box(3, 1, dx, 4, 3, dx, bookshelf, bookshelf, false);
542        placer.generate_box(6, 1, dx, 7, 3, dx, bookshelf, bookshelf, false);
543        placer.generate_box(9, 1, dx, 10, 3, dx, bookshelf, bookshelf, false);
544    }
545
546    if is_tall {
547        placer.generate_box(1, 5, 1, 3, 5, 13, oak_planks, oak_planks, false);
548        placer.generate_box(10, 5, 1, 12, 5, 13, oak_planks, oak_planks, false);
549        placer.generate_box(4, 5, 1, 9, 5, 2, oak_planks, oak_planks, false);
550        placer.generate_box(4, 5, 12, 9, 5, 13, oak_planks, oak_planks, false);
551        placer.place_block(oak_planks, 9, 5, 11);
552        placer.place_block(oak_planks, 8, 5, 11);
553        placer.place_block(oak_planks, 9, 5, 10);
554        let west_east_fence = oak_fence(CONNECT_WEST | CONNECT_EAST);
555        let north_south_fence = oak_fence(CONNECT_NORTH | CONNECT_SOUTH);
556        placer.generate_box(
557            3,
558            6,
559            3,
560            3,
561            6,
562            11,
563            north_south_fence,
564            north_south_fence,
565            false,
566        );
567        placer.generate_box(
568            10,
569            6,
570            3,
571            10,
572            6,
573            9,
574            north_south_fence,
575            north_south_fence,
576            false,
577        );
578        placer.generate_box(4, 6, 2, 9, 6, 2, west_east_fence, west_east_fence, false);
579        placer.generate_box(4, 6, 12, 7, 6, 12, west_east_fence, west_east_fence, false);
580        placer.place_block(oak_fence(CONNECT_NORTH | CONNECT_EAST), 3, 6, 2);
581        placer.place_block(oak_fence(CONNECT_SOUTH | CONNECT_EAST), 3, 6, 12);
582        placer.place_block(oak_fence(CONNECT_NORTH | CONNECT_WEST), 10, 6, 2);
583
584        for i in 0..=2 {
585            placer.place_block(oak_fence(CONNECT_SOUTH | CONNECT_WEST), 8 + i, 6, 12 - i);
586            if i != 2 {
587                placer.place_block(oak_fence(CONNECT_NORTH | CONNECT_EAST), 8 + i, 6, 11 - i);
588            }
589        }
590
591        let ladder = ladder(Direction::South);
592        for y in 1..=7 {
593            placer.place_block(ladder, 10, y, 13);
594        }
595
596        let east_fence = oak_fence(CONNECT_EAST);
597        let west_fence = oak_fence(CONNECT_WEST);
598        placer.place_block(east_fence, 6, 9, 7);
599        placer.place_block(west_fence, 7, 9, 7);
600        placer.place_block(east_fence, 6, 8, 7);
601        placer.place_block(west_fence, 7, 8, 7);
602        let all_sides_fence =
603            oak_fence(CONNECT_NORTH | CONNECT_SOUTH | CONNECT_WEST | CONNECT_EAST);
604        placer.place_block(all_sides_fence, 6, 7, 7);
605        placer.place_block(all_sides_fence, 7, 7, 7);
606        placer.place_block(east_fence, 5, 7, 7);
607        placer.place_block(west_fence, 8, 7, 7);
608        placer.place_block(oak_fence(CONNECT_EAST | CONNECT_NORTH), 6, 7, 6);
609        placer.place_block(oak_fence(CONNECT_EAST | CONNECT_SOUTH), 6, 7, 8);
610        placer.place_block(oak_fence(CONNECT_WEST | CONNECT_NORTH), 7, 7, 6);
611        placer.place_block(oak_fence(CONNECT_WEST | CONNECT_SOUTH), 7, 7, 8);
612        let torch = vanilla_blocks::TORCH.default_state();
613        placer.place_block(torch, 5, 8, 7);
614        placer.place_block(torch, 8, 8, 7);
615        placer.place_block(torch, 6, 8, 6);
616        placer.place_block(torch, 6, 8, 8);
617        placer.place_block(torch, 7, 8, 6);
618        placer.place_block(torch, 7, 8, 8);
619    }
620
621    let _ = placer.create_chest(random, 3, 3, 5, STRONGHOLD_LIBRARY_LOOT);
622    if is_tall {
623        placer.place_block(cave_air(), 12, 9, 1);
624        let _ = placer.create_chest(random, 12, 8, 1, STRONGHOLD_LIBRARY_LOOT);
625    }
626}
627
628fn place_portal_room(
629    placer: &mut ScatteredFeaturePlacer<'_, '_>,
630    random: &mut WorldgenRandom,
631    has_placed_spawner: &mut bool,
632) {
633    stronghold_box(placer, random, 0, 0, 0, 10, 7, 15, false);
634    generate_small_door(placer, StrongholdSmallDoorType::Grates, 4, 1, 0);
635    stronghold_box(placer, random, 1, 6, 1, 1, 6, 14, false);
636    stronghold_box(placer, random, 9, 6, 1, 9, 6, 14, false);
637    stronghold_box(placer, random, 2, 6, 1, 8, 6, 2, false);
638    stronghold_box(placer, random, 2, 6, 14, 8, 6, 14, false);
639    stronghold_box(placer, random, 1, 1, 1, 2, 1, 4, false);
640    stronghold_box(placer, random, 8, 1, 1, 9, 1, 4, false);
641    let lava = vanilla_blocks::LAVA.default_state();
642    placer.generate_box(1, 1, 1, 1, 1, 3, lava, lava, false);
643    placer.generate_box(9, 1, 1, 9, 1, 3, lava, lava, false);
644    stronghold_box(placer, random, 3, 1, 8, 7, 1, 12, false);
645    placer.generate_box(4, 1, 9, 6, 1, 11, lava, lava, false);
646    let north_south_bars = iron_bars(CONNECT_NORTH | CONNECT_SOUTH);
647    let west_east_bars = iron_bars(CONNECT_WEST | CONNECT_EAST);
648
649    for z in (3..14).step_by(2) {
650        placer.generate_box(0, 3, z, 0, 4, z, north_south_bars, north_south_bars, false);
651        placer.generate_box(
652            10,
653            3,
654            z,
655            10,
656            4,
657            z,
658            north_south_bars,
659            north_south_bars,
660            false,
661        );
662    }
663
664    for x in (2..9).step_by(2) {
665        placer.generate_box(x, 3, 15, x, 4, 15, west_east_bars, west_east_bars, false);
666    }
667
668    let stairs = stone_brick_stairs(Direction::North);
669    stronghold_box(placer, random, 4, 1, 5, 6, 1, 7, false);
670    stronghold_box(placer, random, 4, 2, 6, 6, 2, 7, false);
671    stronghold_box(placer, random, 4, 3, 7, 6, 3, 7, false);
672
673    for x in 4..=6 {
674        placer.place_block(stairs, x, 1, 4);
675        placer.place_block(stairs, x, 2, 5);
676        placer.place_block(stairs, x, 3, 6);
677    }
678
679    let mut all_eyes = true;
680    let mut eyes = [false; 12];
681    for eye in &mut eyes {
682        *eye = random.next_f32() > 0.9;
683        all_eyes &= *eye;
684    }
685
686    placer.place_block(end_portal_frame(Direction::North, eyes[0]), 4, 3, 8);
687    placer.place_block(end_portal_frame(Direction::North, eyes[1]), 5, 3, 8);
688    placer.place_block(end_portal_frame(Direction::North, eyes[2]), 6, 3, 8);
689    placer.place_block(end_portal_frame(Direction::South, eyes[3]), 4, 3, 12);
690    placer.place_block(end_portal_frame(Direction::South, eyes[4]), 5, 3, 12);
691    placer.place_block(end_portal_frame(Direction::South, eyes[5]), 6, 3, 12);
692    placer.place_block(end_portal_frame(Direction::East, eyes[6]), 3, 3, 9);
693    placer.place_block(end_portal_frame(Direction::East, eyes[7]), 3, 3, 10);
694    placer.place_block(end_portal_frame(Direction::East, eyes[8]), 3, 3, 11);
695    placer.place_block(end_portal_frame(Direction::West, eyes[9]), 7, 3, 9);
696    placer.place_block(end_portal_frame(Direction::West, eyes[10]), 7, 3, 10);
697    placer.place_block(end_portal_frame(Direction::West, eyes[11]), 7, 3, 11);
698
699    if all_eyes {
700        let portal = vanilla_blocks::END_PORTAL.default_state();
701        placer.place_block(portal, 4, 3, 9);
702        placer.place_block(portal, 5, 3, 9);
703        placer.place_block(portal, 6, 3, 9);
704        placer.place_block(portal, 4, 3, 10);
705        placer.place_block(portal, 5, 3, 10);
706        placer.place_block(portal, 6, 3, 10);
707        placer.place_block(portal, 4, 3, 11);
708        placer.place_block(portal, 5, 3, 11);
709        placer.place_block(portal, 6, 3, 11);
710    }
711
712    let spawner_pos = placer.world_pos(5, 3, 6);
713    if !*has_placed_spawner && placer.clip().contains_blockpos(spawner_pos) {
714        *has_placed_spawner = true;
715        let _ = placer.create_spawner(5, 3, 6, SILVERFISH_ENTITY);
716    }
717}
718
719fn place_prison_hall(
720    placer: &mut ScatteredFeaturePlacer<'_, '_>,
721    random: &mut WorldgenRandom,
722    entry_door: StrongholdSmallDoorType,
723) {
724    stronghold_box(placer, random, 0, 0, 0, 8, 4, 10, true);
725    generate_small_door(placer, entry_door, 1, 1, 0);
726    placer.generate_box(1, 1, 10, 3, 3, 10, cave_air(), cave_air(), false);
727    stronghold_box(placer, random, 4, 1, 1, 4, 3, 1, false);
728    stronghold_box(placer, random, 4, 1, 3, 4, 3, 3, false);
729    stronghold_box(placer, random, 4, 1, 7, 4, 3, 7, false);
730    stronghold_box(placer, random, 4, 1, 9, 4, 3, 9, false);
731
732    let north_south_bars = iron_bars(CONNECT_NORTH | CONNECT_SOUTH);
733    let north_south_east_bars = iron_bars(CONNECT_NORTH | CONNECT_SOUTH | CONNECT_EAST);
734    let west_east_bars = iron_bars(CONNECT_WEST | CONNECT_EAST);
735    for y in 1..=3 {
736        placer.place_block(north_south_bars, 4, y, 4);
737        placer.place_block(north_south_east_bars, 4, y, 5);
738        placer.place_block(north_south_bars, 4, y, 6);
739        placer.place_block(west_east_bars, 5, y, 5);
740        placer.place_block(west_east_bars, 6, y, 5);
741        placer.place_block(west_east_bars, 7, y, 5);
742    }
743
744    placer.place_block(north_south_bars, 4, 3, 2);
745    placer.place_block(north_south_bars, 4, 3, 8);
746    let door_bottom = vanilla_blocks::IRON_DOOR
747        .default_state()
748        .set_value(&BlockStateProperties::HORIZONTAL_FACING, Direction::West);
749    let door_top = upper_door(door_bottom);
750    placer.place_block(door_bottom, 4, 1, 2);
751    placer.place_block(door_top, 4, 2, 2);
752    placer.place_block(door_bottom, 4, 1, 8);
753    placer.place_block(door_top, 4, 2, 8);
754}
755
756fn place_room_crossing(
757    placer: &mut ScatteredFeaturePlacer<'_, '_>,
758    random: &mut WorldgenRandom,
759    entry_door: StrongholdSmallDoorType,
760    crossing_type: i32,
761) {
762    stronghold_box(placer, random, 0, 0, 0, 10, 6, 10, true);
763    generate_small_door(placer, entry_door, 4, 1, 0);
764    let air = cave_air();
765    placer.generate_box(4, 1, 10, 6, 3, 10, air, air, false);
766    placer.generate_box(0, 1, 4, 0, 3, 6, air, air, false);
767    placer.generate_box(10, 1, 4, 10, 3, 6, air, air, false);
768    match crossing_type {
769        0 => {
770            let stone_bricks = stone_bricks();
771            placer.place_block(stone_bricks, 5, 1, 5);
772            placer.place_block(stone_bricks, 5, 2, 5);
773            placer.place_block(stone_bricks, 5, 3, 5);
774            placer.place_block(wall_torch(Direction::West), 4, 3, 5);
775            placer.place_block(wall_torch(Direction::East), 6, 3, 5);
776            placer.place_block(wall_torch(Direction::South), 5, 3, 4);
777            placer.place_block(wall_torch(Direction::North), 5, 3, 6);
778            let slab = smooth_stone_slab();
779            placer.place_block(slab, 4, 1, 4);
780            placer.place_block(slab, 4, 1, 5);
781            placer.place_block(slab, 4, 1, 6);
782            placer.place_block(slab, 6, 1, 4);
783            placer.place_block(slab, 6, 1, 5);
784            placer.place_block(slab, 6, 1, 6);
785            placer.place_block(slab, 5, 1, 4);
786            placer.place_block(slab, 5, 1, 6);
787        }
788        1 => {
789            let stone_bricks = stone_bricks();
790            for i in 0..5 {
791                placer.place_block(stone_bricks, 3, 1, 3 + i);
792                placer.place_block(stone_bricks, 7, 1, 3 + i);
793                placer.place_block(stone_bricks, 3 + i, 1, 3);
794                placer.place_block(stone_bricks, 3 + i, 1, 7);
795            }
796            placer.place_block(stone_bricks, 5, 1, 5);
797            placer.place_block(stone_bricks, 5, 2, 5);
798            placer.place_block(stone_bricks, 5, 3, 5);
799            placer.place_block(vanilla_blocks::WATER.default_state(), 5, 4, 5);
800        }
801        2 => {
802            let cobblestone = vanilla_blocks::COBBLESTONE.default_state();
803            for z in 1..=9 {
804                placer.place_block(cobblestone, 1, 3, z);
805                placer.place_block(cobblestone, 9, 3, z);
806            }
807            for x in 1..=9 {
808                placer.place_block(cobblestone, x, 3, 1);
809                placer.place_block(cobblestone, x, 3, 9);
810            }
811            placer.place_block(cobblestone, 5, 1, 4);
812            placer.place_block(cobblestone, 5, 1, 6);
813            placer.place_block(cobblestone, 5, 3, 4);
814            placer.place_block(cobblestone, 5, 3, 6);
815            placer.place_block(cobblestone, 4, 1, 5);
816            placer.place_block(cobblestone, 6, 1, 5);
817            placer.place_block(cobblestone, 4, 3, 5);
818            placer.place_block(cobblestone, 6, 3, 5);
819            for y in 1..=3 {
820                placer.place_block(cobblestone, 4, y, 4);
821                placer.place_block(cobblestone, 6, y, 4);
822                placer.place_block(cobblestone, 4, y, 6);
823                placer.place_block(cobblestone, 6, y, 6);
824            }
825            placer.place_block(vanilla_blocks::WALL_TORCH.default_state(), 5, 3, 5);
826            let oak_planks = vanilla_blocks::OAK_PLANKS.default_state();
827            for z in 2..=8 {
828                placer.place_block(oak_planks, 2, 3, z);
829                placer.place_block(oak_planks, 3, 3, z);
830                if z <= 3 || z >= 7 {
831                    placer.place_block(oak_planks, 4, 3, z);
832                    placer.place_block(oak_planks, 5, 3, z);
833                    placer.place_block(oak_planks, 6, 3, z);
834                }
835                placer.place_block(oak_planks, 7, 3, z);
836                placer.place_block(oak_planks, 8, 3, z);
837            }
838            let ladder = ladder(Direction::West);
839            placer.place_block(ladder, 9, 1, 3);
840            placer.place_block(ladder, 9, 2, 3);
841            placer.place_block(ladder, 9, 3, 3);
842            let _ = placer.create_chest(random, 3, 4, 8, STRONGHOLD_CROSSING_LOOT);
843        }
844        _ => {}
845    }
846}
847
848fn place_stairs_down(
849    placer: &mut ScatteredFeaturePlacer<'_, '_>,
850    random: &mut WorldgenRandom,
851    entry_door: StrongholdSmallDoorType,
852) {
853    stronghold_box(placer, random, 0, 0, 0, 4, 10, 4, true);
854    generate_small_door(placer, entry_door, 1, 7, 0);
855    generate_small_door(placer, StrongholdSmallDoorType::Opening, 1, 1, 4);
856    let stone_bricks = stone_bricks();
857    let slab = smooth_stone_slab();
858    placer.place_block(stone_bricks, 2, 6, 1);
859    placer.place_block(stone_bricks, 1, 5, 1);
860    placer.place_block(slab, 1, 6, 1);
861    placer.place_block(stone_bricks, 1, 5, 2);
862    placer.place_block(stone_bricks, 1, 4, 3);
863    placer.place_block(slab, 1, 5, 3);
864    placer.place_block(stone_bricks, 2, 4, 3);
865    placer.place_block(stone_bricks, 3, 3, 3);
866    placer.place_block(slab, 3, 4, 3);
867    placer.place_block(stone_bricks, 3, 3, 2);
868    placer.place_block(stone_bricks, 3, 2, 1);
869    placer.place_block(slab, 3, 3, 1);
870    placer.place_block(stone_bricks, 2, 2, 1);
871    placer.place_block(stone_bricks, 1, 1, 1);
872    placer.place_block(slab, 1, 2, 1);
873    placer.place_block(stone_bricks, 1, 1, 2);
874    placer.place_block(slab, 1, 1, 3);
875}
876
877fn place_straight(
878    placer: &mut ScatteredFeaturePlacer<'_, '_>,
879    random: &mut WorldgenRandom,
880    entry_door: StrongholdSmallDoorType,
881    left_child: bool,
882    right_child: bool,
883) {
884    stronghold_box(placer, random, 0, 0, 0, 4, 4, 6, true);
885    generate_small_door(placer, entry_door, 1, 1, 0);
886    generate_small_door(placer, StrongholdSmallDoorType::Opening, 1, 1, 6);
887    maybe_generate_block(placer, random, 0.1, 1, 2, 1, wall_torch(Direction::East));
888    maybe_generate_block(placer, random, 0.1, 3, 2, 1, wall_torch(Direction::West));
889    maybe_generate_block(placer, random, 0.1, 1, 2, 5, wall_torch(Direction::East));
890    maybe_generate_block(placer, random, 0.1, 3, 2, 5, wall_torch(Direction::West));
891    let air = cave_air();
892    if left_child {
893        placer.generate_box(0, 1, 2, 0, 3, 4, air, air, false);
894    }
895    if right_child {
896        placer.generate_box(4, 1, 2, 4, 3, 4, air, air, false);
897    }
898}
899
900fn place_straight_stairs_down(
901    placer: &mut ScatteredFeaturePlacer<'_, '_>,
902    random: &mut WorldgenRandom,
903    entry_door: StrongholdSmallDoorType,
904) {
905    stronghold_box(placer, random, 0, 0, 0, 4, 10, 7, true);
906    generate_small_door(placer, entry_door, 1, 7, 0);
907    generate_small_door(placer, StrongholdSmallDoorType::Opening, 1, 1, 7);
908    let stairs = cobblestone_stairs(Direction::South);
909    let stone_bricks = stone_bricks();
910    for i in 0..6 {
911        placer.place_block(stairs, 1, 6 - i, 1 + i);
912        placer.place_block(stairs, 2, 6 - i, 1 + i);
913        placer.place_block(stairs, 3, 6 - i, 1 + i);
914        if i < 5 {
915            placer.place_block(stone_bricks, 1, 5 - i, 1 + i);
916            placer.place_block(stone_bricks, 2, 5 - i, 1 + i);
917            placer.place_block(stone_bricks, 3, 5 - i, 1 + i);
918        }
919    }
920}