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}