use shulkr::util::Direction;
use shulkr::world::block::{AttachFace, Block, BlockState, Properties, RedstoneSide};
use crate::util::rng::RandomSource;
use crate::world::worldgen::region::WorldGenRegion;
use crate::world::worldgen::structure::scattered_feature_piece::ScatteredFeature;
use crate::world::worldgen::structure::structure_piece::{
PieceBase, PieceKind, StructurePiece, air,
};
use shulkr::util::BoundingBox;
pub const WIDTH: i32 = 12;
pub const DEPTH: i32 = 15;
#[derive(Debug)]
pub struct JungleTemplePiece {
pub scattered: ScatteredFeature,
placed_main_chest: bool,
placed_hidden_chest: bool,
placed_trap_1: bool,
placed_trap_2: bool,
}
fn moss_stone_selector(random: &mut RandomSource) -> BlockState {
if random.next_f32() < 0.4 {
Block::COBBLESTONE.default_state()
} else {
Block::MOSSY_COBBLESTONE.default_state()
}
}
impl JungleTemplePiece {
#[expect(
clippy::new_ret_no_self,
reason = "mirrors the vanilla piece constructor"
)]
pub fn new(random: &mut RandomSource, west: i32, north: i32) -> StructurePiece {
let direction = random.next_horizontal_direction();
let base = ScatteredFeature::make_base(west, 64, north, WIDTH, 10, DEPTH, direction);
StructurePiece::new(
base,
PieceKind::JungleTemple(Self {
scattered: ScatteredFeature::new(WIDTH, 10, DEPTH),
placed_main_chest: false,
placed_hidden_chest: false,
placed_trap_1: false,
placed_trap_2: false,
}),
)
}
#[expect(
clippy::too_many_lines,
reason = "mirrors vanilla JungleTemplePiece::postProcess"
)]
pub fn post_process(
&mut self,
base: &mut PieceBase,
region: &WorldGenRegion<'_>,
random: &mut RandomSource,
chunk_bb: BoundingBox,
) {
if !self
.scattered
.update_average_ground_height(base, region, chunk_bb, 0)
{
return;
}
let width = self.scattered.width;
let depth = self.scattered.depth;
let piece = &*base;
let stones =
|x0: i32, y0: i32, z0: i32, x1: i32, y1: i32, z1: i32, random: &mut RandomSource| {
piece.generate_box_selected(
region,
chunk_bb,
x0,
y0,
z0,
x1,
y1,
z1,
false,
random,
|random, _, _, _, _| moss_stone_selector(random),
);
};
stones(0, -4, 0, width - 1, 0, depth - 1, random);
stones(2, 1, 2, 9, 2, 2, random);
stones(2, 1, 12, 9, 2, 12, random);
stones(2, 1, 3, 2, 2, 11, random);
stones(9, 1, 3, 9, 2, 11, random);
stones(1, 3, 1, 10, 6, 1, random);
stones(1, 3, 13, 10, 6, 13, random);
stones(1, 3, 2, 1, 6, 12, random);
stones(10, 3, 2, 10, 6, 12, random);
stones(2, 3, 2, 9, 3, 12, random);
stones(2, 6, 2, 9, 6, 12, random);
stones(3, 7, 3, 8, 7, 11, random);
stones(4, 8, 4, 7, 8, 10, random);
let air_box = |x0: i32, y0: i32, z0: i32, x1: i32, y1: i32, z1: i32| {
piece.generate_air_box(region, chunk_bb, x0, y0, z0, x1, y1, z1);
};
let place = |state: BlockState, x: i32, y: i32, z: i32| {
piece.place_block(region, state, x, y, z, chunk_bb);
};
air_box(3, 1, 3, 8, 2, 11);
air_box(4, 3, 6, 7, 3, 9);
air_box(2, 4, 2, 9, 5, 12);
air_box(4, 6, 5, 7, 6, 9);
air_box(5, 7, 6, 6, 7, 8);
air_box(5, 1, 2, 6, 2, 2);
air_box(5, 2, 12, 6, 2, 12);
air_box(5, 5, 1, 6, 5, 1);
air_box(5, 5, 13, 6, 5, 13);
place(air(), 1, 5, 5);
place(air(), 10, 5, 5);
place(air(), 1, 5, 9);
place(air(), 10, 5, 9);
let mut z = 0;
while z <= 14 {
stones(2, 4, z, 2, 5, z, random);
stones(4, 4, z, 4, 5, z, random);
stones(7, 4, z, 7, 5, z, random);
stones(9, 4, z, 9, 5, z, random);
z += 14;
}
stones(5, 6, 0, 6, 6, 0, random);
let mut x = 0;
while x <= 11 {
let mut z = 2;
while z <= 12 {
stones(x, 4, z, x, 5, z, random);
z += 2;
}
stones(x, 6, 5, x, 6, 5, random);
stones(x, 6, 9, x, 6, 9, random);
x += 11;
}
stones(2, 7, 2, 2, 9, 2, random);
stones(9, 7, 2, 9, 9, 2, random);
stones(2, 7, 12, 2, 9, 12, random);
stones(9, 7, 12, 9, 9, 12, random);
stones(4, 9, 4, 4, 9, 4, random);
stones(7, 9, 4, 7, 9, 4, random);
stones(4, 9, 10, 4, 9, 10, random);
stones(7, 9, 10, 7, 9, 10, random);
stones(5, 9, 7, 6, 9, 7, random);
let stairs = Block::COBBLESTONE_STAIRS.default_state();
let east_stairs = stairs.with(Properties::HORIZONTAL_FACING, Direction::East);
let west_stairs = stairs.with(Properties::HORIZONTAL_FACING, Direction::West);
let south_stairs = stairs.with(Properties::HORIZONTAL_FACING, Direction::South);
let north_stairs = stairs.with(Properties::HORIZONTAL_FACING, Direction::North);
place(north_stairs, 5, 9, 6);
place(north_stairs, 6, 9, 6);
place(south_stairs, 5, 9, 8);
place(south_stairs, 6, 9, 8);
place(north_stairs, 4, 0, 0);
place(north_stairs, 5, 0, 0);
place(north_stairs, 6, 0, 0);
place(north_stairs, 7, 0, 0);
place(north_stairs, 4, 1, 8);
place(north_stairs, 4, 2, 9);
place(north_stairs, 4, 3, 10);
place(north_stairs, 7, 1, 8);
place(north_stairs, 7, 2, 9);
place(north_stairs, 7, 3, 10);
stones(4, 1, 9, 4, 1, 9, random);
stones(7, 1, 9, 7, 1, 9, random);
stones(4, 1, 10, 7, 2, 10, random);
stones(5, 4, 5, 6, 4, 5, random);
place(east_stairs, 4, 4, 5);
place(west_stairs, 7, 4, 5);
for i in 0..4 {
place(south_stairs, 5, -i, 6 + i);
place(south_stairs, 6, -i, 6 + i);
air_box(5, -i, 7 + i, 6, -i, 9 + i);
}
air_box(1, -3, 12, 10, -1, 13);
air_box(1, -3, 1, 3, -1, 13);
air_box(1, -3, 1, 9, -1, 5);
let mut z = 1;
while z <= 13 {
stones(1, -3, z, 1, -2, z, random);
z += 2;
}
let mut z = 2;
while z <= 12 {
stones(1, -1, z, 3, -1, z, random);
z += 2;
}
stones(2, -2, 1, 5, -2, 1, random);
stones(7, -2, 1, 9, -2, 1, random);
stones(6, -3, 1, 6, -3, 1, random);
stones(6, -1, 1, 6, -1, 1, random);
let hook = Block::TRIPWIRE_HOOK.default_state();
let hook = |facing: Direction| {
hook.with(Properties::HORIZONTAL_FACING, facing)
.with(Properties::ATTACHED, true)
};
let tripwire = Block::TRIPWIRE
.default_state()
.with(Properties::ATTACHED, true);
let tripwire_ew = tripwire
.with(Properties::EAST, true)
.with(Properties::WEST, true);
let tripwire_ns = tripwire
.with(Properties::NORTH, true)
.with(Properties::SOUTH, true);
place(hook(Direction::East), 1, -3, 8);
place(hook(Direction::West), 4, -3, 8);
place(tripwire_ew, 2, -3, 8);
place(tripwire_ew, 3, -3, 8);
let wire = Block::REDSTONE_WIRE.default_state();
let redstone_wire_ns = wire
.with(Properties::NORTH_REDSTONE, RedstoneSide::Side)
.with(Properties::SOUTH_REDSTONE, RedstoneSide::Side);
place(redstone_wire_ns, 5, -3, 7);
place(redstone_wire_ns, 5, -3, 6);
place(redstone_wire_ns, 5, -3, 5);
place(redstone_wire_ns, 5, -3, 4);
place(redstone_wire_ns, 5, -3, 3);
place(redstone_wire_ns, 5, -3, 2);
place(
wire.with(Properties::NORTH_REDSTONE, RedstoneSide::Side)
.with(Properties::WEST_REDSTONE, RedstoneSide::Side),
5,
-3,
1,
);
place(
wire.with(Properties::EAST_REDSTONE, RedstoneSide::Side)
.with(Properties::WEST_REDSTONE, RedstoneSide::Side),
4,
-3,
1,
);
let mossy = Block::MOSSY_COBBLESTONE.default_state();
place(mossy, 3, -3, 1);
if !self.placed_trap_1 {
self.placed_trap_1 =
piece.create_dispenser(region, chunk_bb, random, 3, -2, 1, Direction::North);
}
let vine = Block::VINE.default_state();
place(vine.with(Properties::SOUTH, true), 3, -2, 2);
place(hook(Direction::North), 7, -3, 1);
place(hook(Direction::South), 7, -3, 5);
place(tripwire_ns, 7, -3, 2);
place(tripwire_ns, 7, -3, 3);
place(tripwire_ns, 7, -3, 4);
place(
wire.with(Properties::EAST_REDSTONE, RedstoneSide::Side)
.with(Properties::WEST_REDSTONE, RedstoneSide::Side),
8,
-3,
6,
);
place(
wire.with(Properties::WEST_REDSTONE, RedstoneSide::Side)
.with(Properties::SOUTH_REDSTONE, RedstoneSide::Side),
9,
-3,
6,
);
place(
wire.with(Properties::NORTH_REDSTONE, RedstoneSide::Side)
.with(Properties::SOUTH_REDSTONE, RedstoneSide::Up),
9,
-3,
5,
);
place(mossy, 9, -3, 4);
place(redstone_wire_ns, 9, -2, 4);
if !self.placed_trap_2 {
self.placed_trap_2 =
piece.create_dispenser(region, chunk_bb, random, 9, -2, 3, Direction::West);
}
place(vine.with(Properties::EAST, true), 8, -1, 3);
place(vine.with(Properties::EAST, true), 8, -2, 3);
if !self.placed_main_chest {
self.placed_main_chest = piece.create_chest(region, chunk_bb, random, 8, -3, 3);
}
place(mossy, 9, -3, 2);
place(mossy, 8, -3, 1);
place(mossy, 4, -3, 5);
place(mossy, 5, -2, 5);
place(mossy, 5, -1, 5);
place(mossy, 6, -3, 5);
place(mossy, 7, -2, 5);
place(mossy, 7, -1, 5);
place(mossy, 8, -3, 5);
stones(9, -1, 1, 9, -1, 5, random);
air_box(8, -3, 8, 10, -1, 10);
let chiseled = Block::CHISELED_STONE_BRICKS.default_state();
place(chiseled, 8, -2, 11);
place(chiseled, 9, -2, 11);
place(chiseled, 10, -2, 11);
let lever = Block::LEVER
.default_state()
.with(Properties::HORIZONTAL_FACING, Direction::North)
.with(Properties::ATTACH_FACE, AttachFace::Wall);
place(lever, 8, -2, 12);
place(lever, 9, -2, 12);
place(lever, 10, -2, 12);
stones(8, -3, 8, 8, -3, 10, random);
stones(10, -3, 8, 10, -3, 10, random);
place(mossy, 10, -2, 9);
place(redstone_wire_ns, 8, -2, 9);
place(redstone_wire_ns, 8, -2, 10);
place(
wire.with(Properties::NORTH_REDSTONE, RedstoneSide::Side)
.with(Properties::SOUTH_REDSTONE, RedstoneSide::Side)
.with(Properties::EAST_REDSTONE, RedstoneSide::Side)
.with(Properties::WEST_REDSTONE, RedstoneSide::Side),
10,
-1,
9,
);
let piston = Block::STICKY_PISTON.default_state();
place(piston.with(Properties::FACING, Direction::Up), 9, -2, 8);
place(piston.with(Properties::FACING, Direction::West), 10, -2, 8);
place(piston.with(Properties::FACING, Direction::West), 10, -1, 8);
place(
Block::REPEATER
.default_state()
.with(Properties::HORIZONTAL_FACING, Direction::North),
10,
-2,
10,
);
if !self.placed_hidden_chest {
self.placed_hidden_chest = piece.create_chest(region, chunk_bb, random, 9, -3, 10);
}
}
}