package com.tiedup.remake.worldgen; import javax.annotation.Nullable; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.LanternBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.levelgen.structure.BoundingBox; enum RoomTheme { OUBLIETTE { @Override public BlockState wallBlock(RandomSource r, int ry) { if ( ry == 1 && r.nextFloat() < 0.30f ) return Blocks.MOSSY_COBBLESTONE.defaultBlockState(); return r.nextFloat() < 0.20f ? Blocks.CRACKED_DEEPSLATE_BRICKS.defaultBlockState() : Blocks.DEEPSLATE_BRICKS.defaultBlockState(); } @Override public BlockState floorBlock( RandomSource r, int rx, int rz, boolean isEdge ) { boolean isCorner = (rx == 1 || rx == 11) && (rz == 1 || rz == 11); if (isCorner) return Blocks.MOSSY_COBBLESTONE.defaultBlockState(); return r.nextFloat() < 0.15f ? Blocks.COBBLESTONE.defaultBlockState() : Blocks.DEEPSLATE_TILES.defaultBlockState(); } @Override public BlockState ceilingBlock(RandomSource r) { return r.nextFloat() < 0.20f ? Blocks.CRACKED_DEEPSLATE_BRICKS.defaultBlockState() : Blocks.DEEPSLATE_BRICKS.defaultBlockState(); } @Override public BlockState wallShellBlock() { return Blocks.DEEPSLATE_BRICKS.defaultBlockState(); } @Override public void placeDecorations( WorldGenLevel level, RandomSource random, int baseX, int baseZ, int floorY, RoomLayout layout, BoundingBox chunkBB ) { int[][] corners = layout.innerCorners(); // Cobwebs at corners (low + high) for (int[] c : corners) { if ( layout.isInShape(c[0], c[1]) && !layout.isWall(c[0], c[1]) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + c[0], floorY + 1, baseZ + c[1]), Blocks.COBWEB.defaultBlockState(), chunkBB ); HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + c[0], floorY + 9, baseZ + c[1]), Blocks.COBWEB.defaultBlockState(), chunkBB ); } } // Water cauldron near first corner int cx = corners[0][0] + 1, cz = corners[0][1] + 1; if (layout.isInShape(cx, cz) && !layout.isWall(cx, cz)) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + cx, floorY + 1, baseZ + cz), Blocks.WATER_CAULDRON.defaultBlockState().setValue( BlockStateProperties.LEVEL_CAULDRON, 3 ), chunkBB ); } // Soul lanterns on wall midpoints for (int[] wallPos : new int[][] { { 6, 1 }, { 6, 11 }, { 1, 6 }, { 11, 6 }, }) { if (layout.isWall(wallPos[0], wallPos[1])) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + wallPos[0], floorY + 3, baseZ + wallPos[1] ), Blocks.SOUL_LANTERN.defaultBlockState(), chunkBB ); } } // Corner furniture: barrel + brewing_stand + chain int[] fc = corners[1]; int fcx = fc[0] + (fc[0] < 6 ? 1 : -1); int fcz = fc[1] + (fc[1] < 6 ? 1 : -1); if (layout.isInShape(fcx, fcz) && !layout.isWall(fcx, fcz)) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + fcx, floorY + 1, baseZ + fcz), Blocks.BARREL.defaultBlockState(), chunkBB ); int fcx2 = fcx + (fcx < 6 ? 1 : -1); if (layout.isInShape(fcx2, fcz) && !layout.isWall(fcx2, fcz)) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + fcx2, floorY + 1, baseZ + fcz), Blocks.BREWING_STAND.defaultBlockState(), chunkBB ); } // Chain above barrel for (int cy = 8; cy <= 10; cy++) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + fcx, floorY + cy, baseZ + fcz), Blocks.CHAIN.defaultBlockState(), chunkBB ); } } // Side chains placeSharedChains(level, baseX, baseZ, floorY, chunkBB); // Hanging lanterns placeSharedHangingLanterns(level, baseX, baseZ, floorY, chunkBB); } @Override public BlockState pillarBlock(RandomSource r, int ry) { if ( ry == 1 || ry == 10 ) return Blocks.POLISHED_DEEPSLATE.defaultBlockState(); return Blocks.DEEPSLATE_BRICK_WALL.defaultBlockState(); } @Override @Nullable public BlockState scatterBlock(RandomSource r) { float f = r.nextFloat(); if (f < 0.40f) return Blocks.COBWEB.defaultBlockState(); if (f < 0.70f) return Blocks.CANDLE.defaultBlockState() .setValue(BlockStateProperties.CANDLES, 1 + r.nextInt(3)) .setValue(BlockStateProperties.LIT, true); return Blocks.MOSS_CARPET.defaultBlockState(); } @Override public BlockState wallAccentBlock() { return Blocks.POLISHED_DEEPSLATE.defaultBlockState(); } }, INFERNO { @Override public BlockState wallBlock(RandomSource r, int ry) { return r.nextFloat() < 0.20f ? Blocks.CRACKED_NETHER_BRICKS.defaultBlockState() : Blocks.NETHER_BRICKS.defaultBlockState(); } @Override public BlockState floorBlock( RandomSource r, int rx, int rz, boolean isEdge ) { if (isEdge) return Blocks.MAGMA_BLOCK.defaultBlockState(); return r.nextFloat() < 0.08f ? Blocks.GILDED_BLACKSTONE.defaultBlockState() : Blocks.BLACKSTONE.defaultBlockState(); } @Override public BlockState ceilingBlock(RandomSource r) { return r.nextFloat() < 0.20f ? Blocks.CRACKED_NETHER_BRICKS.defaultBlockState() : Blocks.NETHER_BRICKS.defaultBlockState(); } @Override public BlockState wallShellBlock() { return Blocks.NETHER_BRICKS.defaultBlockState(); } @Override public void placeDecorations( WorldGenLevel level, RandomSource random, int baseX, int baseZ, int floorY, RoomLayout layout, BoundingBox chunkBB ) { int[][] corners = layout.innerCorners(); // Soul fire at corners: soul_sand at ry=0, soul_fire at ry=1 for (int[] c : corners) { if ( layout.isInShape(c[0], c[1]) && !layout.isWall(c[0], c[1]) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + c[0], floorY, baseZ + c[1]), Blocks.SOUL_SAND.defaultBlockState(), chunkBB ); HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + c[0], floorY + 1, baseZ + c[1]), Blocks.SOUL_FIRE.defaultBlockState(), chunkBB ); } } // Crying obsidian accents at wall midpoints for (int[] wallPos : new int[][] { { 6, 1 }, { 6, 11 }, { 1, 6 }, { 11, 6 }, }) { if (layout.isWall(wallPos[0], wallPos[1])) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + wallPos[0], floorY + 2, baseZ + wallPos[1] ), Blocks.CRYING_OBSIDIAN.defaultBlockState(), chunkBB ); } } // Soul lanterns on wall midpoints for (int[] wallPos : new int[][] { { 6, 1 }, { 6, 11 }, { 1, 6 }, { 11, 6 }, }) { if (layout.isWall(wallPos[0], wallPos[1])) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + wallPos[0], floorY + 3, baseZ + wallPos[1] ), Blocks.SOUL_LANTERN.defaultBlockState(), chunkBB ); } } // Corner furniture: soul_campfire + cauldron(lava) + gilded_blackstone int[][] icorners = layout.innerCorners(); int[] ifc = icorners[1]; int ifcx = ifc[0] + (ifc[0] < 6 ? 1 : -1); int ifcz = ifc[1] + (ifc[1] < 6 ? 1 : -1); if (layout.isInShape(ifcx, ifcz) && !layout.isWall(ifcx, ifcz)) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + ifcx, floorY + 1, baseZ + ifcz), Blocks.SOUL_CAMPFIRE.defaultBlockState(), chunkBB ); int ifcx2 = ifcx + (ifcx < 6 ? 1 : -1); if ( layout.isInShape(ifcx2, ifcz) && !layout.isWall(ifcx2, ifcz) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + ifcx2, floorY + 1, baseZ + ifcz), Blocks.LAVA_CAULDRON.defaultBlockState(), chunkBB ); } if ( layout.isInShape(ifcx, ifcz + (ifcz < 6 ? 1 : -1)) && !layout.isWall(ifcx, ifcz + (ifcz < 6 ? 1 : -1)) ) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + ifcx, floorY + 1, baseZ + ifcz + (ifcz < 6 ? 1 : -1) ), Blocks.GILDED_BLACKSTONE.defaultBlockState(), chunkBB ); } } // Side chains placeSharedChains(level, baseX, baseZ, floorY, chunkBB); // Hanging lanterns placeSharedHangingLanterns(level, baseX, baseZ, floorY, chunkBB); } @Override public BlockState pillarBlock(RandomSource r, int ry) { if ( ry == 1 || ry == 10 ) return Blocks.QUARTZ_PILLAR.defaultBlockState(); return Blocks.NETHER_BRICK_WALL.defaultBlockState(); } @Override @Nullable public BlockState scatterBlock(RandomSource r) { float f = r.nextFloat(); if (f < 0.40f) return Blocks.SOUL_SAND.defaultBlockState(); if (f < 0.70f) return Blocks.NETHER_WART_BLOCK.defaultBlockState(); return Blocks.BONE_BLOCK.defaultBlockState(); } @Override public BlockState wallAccentBlock() { return Blocks.RED_NETHER_BRICKS.defaultBlockState(); } }, CRYPT { @Override public BlockState wallBlock(RandomSource r, int ry) { float f = r.nextFloat(); if ( ry == 1 && f < 0.20f ) return Blocks.MOSSY_COBBLESTONE.defaultBlockState(); if (f < 0.15f) return Blocks.MOSSY_STONE_BRICKS.defaultBlockState(); if ( f < 0.30f ) return Blocks.CRACKED_STONE_BRICKS.defaultBlockState(); return Blocks.STONE_BRICKS.defaultBlockState(); } @Override public BlockState floorBlock( RandomSource r, int rx, int rz, boolean isEdge ) { boolean isCorner = (rx == 1 || rx == 11) && (rz == 1 || rz == 11); if (isCorner) return Blocks.MOSSY_COBBLESTONE.defaultBlockState(); return r.nextFloat() < 0.20f ? Blocks.COBBLESTONE.defaultBlockState() : Blocks.STONE_BRICKS.defaultBlockState(); } @Override public BlockState ceilingBlock(RandomSource r) { return r.nextFloat() < 0.20f ? Blocks.CRACKED_STONE_BRICKS.defaultBlockState() : Blocks.STONE_BRICKS.defaultBlockState(); } @Override public BlockState wallShellBlock() { return Blocks.STONE_BRICKS.defaultBlockState(); } @Override public void placeDecorations( WorldGenLevel level, RandomSource random, int baseX, int baseZ, int floorY, RoomLayout layout, BoundingBox chunkBB ) { int[][] corners = layout.innerCorners(); // Cobwebs at corners (low + high) for (int[] c : corners) { if ( layout.isInShape(c[0], c[1]) && !layout.isWall(c[0], c[1]) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + c[0], floorY + 1, baseZ + c[1]), Blocks.COBWEB.defaultBlockState(), chunkBB ); HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + c[0], floorY + 9, baseZ + c[1]), Blocks.COBWEB.defaultBlockState(), chunkBB ); } } // Wall torches at midpoints for (int[] wallPos : new int[][] { { 6, 0 }, { 6, 12 }, { 0, 6 }, { 12, 6 }, }) { if (layout.isWall(wallPos[0], wallPos[1])) { Direction torchDir = getTorchDirection( wallPos[0], wallPos[1] ); if (torchDir != null) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + wallPos[0], floorY + 3, baseZ + wallPos[1] ), Blocks.WALL_TORCH.defaultBlockState().setValue( net.minecraft.world.level.block.WallTorchBlock.FACING, torchDir ), chunkBB ); } } } // Skull in first corner int[] sc = corners[0]; if ( layout.isInShape(sc[0], sc[1]) && !layout.isWall(sc[0], sc[1]) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + sc[0], floorY + 1, baseZ + sc[1]), Blocks.SKELETON_SKULL.defaultBlockState(), chunkBB ); } // Corner furniture: lectern + candles on floor int[] cfc = corners[1]; int cfcx = cfc[0] + (cfc[0] < 6 ? 1 : -1); int cfcz = cfc[1] + (cfc[1] < 6 ? 1 : -1); if (layout.isInShape(cfcx, cfcz) && !layout.isWall(cfcx, cfcz)) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + cfcx, floorY + 1, baseZ + cfcz), Blocks.LECTERN.defaultBlockState(), chunkBB ); int cfcx2 = cfcx + (cfcx < 6 ? 1 : -1); if ( layout.isInShape(cfcx2, cfcz) && !layout.isWall(cfcx2, cfcz) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + cfcx2, floorY + 1, baseZ + cfcz), Blocks.CANDLE.defaultBlockState() .setValue(BlockStateProperties.CANDLES, 4) .setValue(BlockStateProperties.LIT, true), chunkBB ); } } placeSharedChains(level, baseX, baseZ, floorY, chunkBB); placeSharedHangingLanterns(level, baseX, baseZ, floorY, chunkBB); } @Override public BlockState pillarBlock(RandomSource r, int ry) { if ( ry == 1 || ry == 10 ) return Blocks.CHISELED_STONE_BRICKS.defaultBlockState(); return Blocks.STONE_BRICK_WALL.defaultBlockState(); } @Override @Nullable public BlockState scatterBlock(RandomSource r) { float f = r.nextFloat(); if (f < 0.40f) return Blocks.COBWEB.defaultBlockState(); if (f < 0.70f) return Blocks.CANDLE.defaultBlockState() .setValue(BlockStateProperties.CANDLES, 1 + r.nextInt(3)) .setValue(BlockStateProperties.LIT, true); return Blocks.BONE_BLOCK.defaultBlockState(); } @Override public BlockState wallAccentBlock() { return Blocks.MOSSY_STONE_BRICKS.defaultBlockState(); } }, ICE { @Override public BlockState wallBlock(RandomSource r, int ry) { float f = r.nextFloat(); if (f < 0.10f) return Blocks.ICE.defaultBlockState(); if (f < 0.30f) return Blocks.BLUE_ICE.defaultBlockState(); return Blocks.PACKED_ICE.defaultBlockState(); } @Override public BlockState floorBlock( RandomSource r, int rx, int rz, boolean isEdge ) { if (isEdge) return Blocks.BLUE_ICE.defaultBlockState(); return r.nextFloat() < 0.20f ? Blocks.SNOW_BLOCK.defaultBlockState() : Blocks.PACKED_ICE.defaultBlockState(); } @Override public BlockState ceilingBlock(RandomSource r) { return r.nextFloat() < 0.20f ? Blocks.BLUE_ICE.defaultBlockState() : Blocks.PACKED_ICE.defaultBlockState(); } @Override public BlockState wallShellBlock() { return Blocks.PACKED_ICE.defaultBlockState(); } @Override public void placeDecorations( WorldGenLevel level, RandomSource random, int baseX, int baseZ, int floorY, RoomLayout layout, BoundingBox chunkBB ) { int[][] corners = layout.innerCorners(); // Snow layers in corners for (int[] c : corners) { if ( layout.isInShape(c[0], c[1]) && !layout.isWall(c[0], c[1]) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + c[0], floorY + 1, baseZ + c[1]), Blocks.SNOW.defaultBlockState().setValue( net.minecraft.world.level.block.SnowLayerBlock.LAYERS, 2 + random.nextInt(3) ), chunkBB ); } } // Lanterns on wall midpoints for (int[] wallPos : new int[][] { { 6, 1 }, { 6, 11 }, { 1, 6 }, { 11, 6 }, }) { if (layout.isWall(wallPos[0], wallPos[1])) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + wallPos[0], floorY + 3, baseZ + wallPos[1] ), Blocks.LANTERN.defaultBlockState(), chunkBB ); } } // Ice stalactites near ceiling corners for (int[] c : corners) { int cx = c[0], cz = c[1]; if (layout.isInShape(cx, cz) && !layout.isWall(cx, cz)) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + cx, floorY + 10, baseZ + cz), Blocks.ICE.defaultBlockState(), chunkBB ); } } // Corner furniture: cauldron(powder_snow) + blue_ice seat + lantern int[] ifc2 = corners[1]; int ifc2x = ifc2[0] + (ifc2[0] < 6 ? 1 : -1); int ifc2z = ifc2[1] + (ifc2[1] < 6 ? 1 : -1); if ( layout.isInShape(ifc2x, ifc2z) && !layout.isWall(ifc2x, ifc2z) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + ifc2x, floorY + 1, baseZ + ifc2z), Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue( BlockStateProperties.LEVEL_CAULDRON, 3 ), chunkBB ); int ifc2x2 = ifc2x + (ifc2x < 6 ? 1 : -1); if ( layout.isInShape(ifc2x2, ifc2z) && !layout.isWall(ifc2x2, ifc2z) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + ifc2x2, floorY + 1, baseZ + ifc2z), Blocks.BLUE_ICE.defaultBlockState(), chunkBB ); } if ( layout.isInShape(ifc2x, ifc2z + (ifc2z < 6 ? 1 : -1)) && !layout.isWall(ifc2x, ifc2z + (ifc2z < 6 ? 1 : -1)) ) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + ifc2x, floorY + 2, baseZ + ifc2z + (ifc2z < 6 ? 1 : -1) ), Blocks.LANTERN.defaultBlockState(), chunkBB ); } } placeSharedChains(level, baseX, baseZ, floorY, chunkBB); placeSharedHangingLanterns(level, baseX, baseZ, floorY, chunkBB); } @Override public BlockState pillarBlock(RandomSource r, int ry) { if (ry == 1 || ry == 10) return Blocks.BLUE_ICE.defaultBlockState(); return r.nextFloat() < 0.5f ? Blocks.PACKED_ICE.defaultBlockState() : Blocks.BLUE_ICE.defaultBlockState(); } @Override @Nullable public BlockState scatterBlock(RandomSource r) { float f = r.nextFloat(); if (f < 0.50f) return Blocks.SNOW.defaultBlockState().setValue( net.minecraft.world.level.block.SnowLayerBlock.LAYERS, 1 + r.nextInt(2) ); if (f < 0.70f) return Blocks.POWDER_SNOW.defaultBlockState(); return Blocks.ICE.defaultBlockState(); } @Override public BlockState wallAccentBlock() { return Blocks.BLUE_ICE.defaultBlockState(); } }, SCULK { @Override public BlockState wallBlock(RandomSource r, int ry) { float f = r.nextFloat(); if ( f < 0.10f ) return Blocks.CRACKED_DEEPSLATE_BRICKS.defaultBlockState(); if (f < 0.40f) return Blocks.SCULK.defaultBlockState(); return Blocks.DEEPSLATE_BRICKS.defaultBlockState(); } @Override public BlockState floorBlock( RandomSource r, int rx, int rz, boolean isEdge ) { if (isEdge) return Blocks.SCULK.defaultBlockState(); return r.nextFloat() < 0.30f ? Blocks.SCULK.defaultBlockState() : Blocks.DEEPSLATE_TILES.defaultBlockState(); } @Override public BlockState ceilingBlock(RandomSource r) { float f = r.nextFloat(); if ( f < 0.10f ) return Blocks.CRACKED_DEEPSLATE_BRICKS.defaultBlockState(); if (f < 0.30f) return Blocks.SCULK.defaultBlockState(); return Blocks.DEEPSLATE_BRICKS.defaultBlockState(); } @Override public BlockState wallShellBlock() { return Blocks.DEEPSLATE_BRICKS.defaultBlockState(); } @Override public void placeDecorations( WorldGenLevel level, RandomSource random, int baseX, int baseZ, int floorY, RoomLayout layout, BoundingBox chunkBB ) { // Sculk veins on wall midpoints at ry=2 for (int[] wallPos : new int[][] { { 6, 1 }, { 6, 11 }, { 1, 6 }, { 11, 6 }, }) { if (layout.isWall(wallPos[0], wallPos[1])) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + wallPos[0], floorY + 2, baseZ + wallPos[1] ), Blocks.SCULK_VEIN.defaultBlockState(), chunkBB ); } } // Soul lanterns on wall midpoints at ry=3 for (int[] wallPos : new int[][] { { 6, 1 }, { 6, 11 }, { 1, 6 }, { 11, 6 }, }) { if (layout.isWall(wallPos[0], wallPos[1])) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + wallPos[0], floorY + 3, baseZ + wallPos[1] ), Blocks.SOUL_LANTERN.defaultBlockState(), chunkBB ); } } // Sculk catalyst in first corner int[][] corners = layout.innerCorners(); int[] sc = corners[0]; if ( layout.isInShape(sc[0], sc[1]) && !layout.isWall(sc[0], sc[1]) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + sc[0], floorY + 1, baseZ + sc[1]), Blocks.SCULK_CATALYST.defaultBlockState(), chunkBB ); } // Corner furniture: sculk_shrieker + sculk_sensor + candle int[][] scorners = layout.innerCorners(); int[] sfc = scorners[1]; int sfcx = sfc[0] + (sfc[0] < 6 ? 1 : -1); int sfcz = sfc[1] + (sfc[1] < 6 ? 1 : -1); if (layout.isInShape(sfcx, sfcz) && !layout.isWall(sfcx, sfcz)) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + sfcx, floorY + 1, baseZ + sfcz), Blocks.SCULK_SHRIEKER.defaultBlockState(), chunkBB ); int sfcx2 = sfcx + (sfcx < 6 ? 1 : -1); if ( layout.isInShape(sfcx2, sfcz) && !layout.isWall(sfcx2, sfcz) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + sfcx2, floorY + 1, baseZ + sfcz), Blocks.SCULK_SENSOR.defaultBlockState(), chunkBB ); } if ( layout.isInShape(sfcx, sfcz + (sfcz < 6 ? 1 : -1)) && !layout.isWall(sfcx, sfcz + (sfcz < 6 ? 1 : -1)) ) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + sfcx, floorY + 1, baseZ + sfcz + (sfcz < 6 ? 1 : -1) ), Blocks.CANDLE.defaultBlockState() .setValue(BlockStateProperties.CANDLES, 3) .setValue(BlockStateProperties.LIT, true), chunkBB ); } } placeSharedChains(level, baseX, baseZ, floorY, chunkBB); placeSharedHangingLanterns(level, baseX, baseZ, floorY, chunkBB); } @Override public BlockState pillarBlock(RandomSource r, int ry) { if (ry == 1 || ry == 10) return Blocks.SCULK.defaultBlockState(); return r.nextFloat() < 0.3f ? Blocks.SCULK.defaultBlockState() : Blocks.DEEPSLATE_TILE_WALL.defaultBlockState(); } @Override @Nullable public BlockState scatterBlock(RandomSource r) { float f = r.nextFloat(); if (f < 0.50f) return Blocks.SCULK.defaultBlockState(); if (f < 0.80f) return Blocks.SCULK_VEIN.defaultBlockState(); return Blocks.MOSS_CARPET.defaultBlockState(); } @Override public BlockState wallAccentBlock() { return Blocks.SCULK_CATALYST.defaultBlockState(); } }, SANDSTONE { @Override public BlockState wallBlock(RandomSource r, int ry) { float f = r.nextFloat(); if (f < 0.10f) return Blocks.CHISELED_SANDSTONE.defaultBlockState(); if (f < 0.30f) return Blocks.SANDSTONE.defaultBlockState(); return Blocks.CUT_SANDSTONE.defaultBlockState(); } @Override public BlockState floorBlock( RandomSource r, int rx, int rz, boolean isEdge ) { return r.nextFloat() < 0.15f ? Blocks.SAND.defaultBlockState() : Blocks.SMOOTH_SANDSTONE.defaultBlockState(); } @Override public BlockState ceilingBlock(RandomSource r) { return r.nextFloat() < 0.20f ? Blocks.SANDSTONE.defaultBlockState() : Blocks.CUT_SANDSTONE.defaultBlockState(); } @Override public BlockState wallShellBlock() { return Blocks.CUT_SANDSTONE.defaultBlockState(); } @Override public void placeDecorations( WorldGenLevel level, RandomSource random, int baseX, int baseZ, int floorY, RoomLayout layout, BoundingBox chunkBB ) { // Wall torches at midpoints for (int[] wallPos : new int[][] { { 6, 0 }, { 6, 12 }, { 0, 6 }, { 12, 6 }, }) { if (layout.isWall(wallPos[0], wallPos[1])) { Direction torchDir = getTorchDirection( wallPos[0], wallPos[1] ); if (torchDir != null) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + wallPos[0], floorY + 3, baseZ + wallPos[1] ), Blocks.WALL_TORCH.defaultBlockState().setValue( net.minecraft.world.level.block.WallTorchBlock.FACING, torchDir ), chunkBB ); } } } // Orange terracotta accents at wall midpoints ry=2 for (int[] wallPos : new int[][] { { 6, 1 }, { 6, 11 }, { 1, 6 }, { 11, 6 }, }) { if (layout.isWall(wallPos[0], wallPos[1])) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + wallPos[0], floorY + 2, baseZ + wallPos[1] ), Blocks.ORANGE_TERRACOTTA.defaultBlockState(), chunkBB ); } } // TNT hidden in first corner int[][] corners = layout.innerCorners(); int[] tc = corners[0]; if ( layout.isInShape(tc[0], tc[1]) && !layout.isWall(tc[0], tc[1]) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + tc[0], floorY + 1, baseZ + tc[1]), Blocks.TNT.defaultBlockState(), chunkBB ); } // Corner furniture: barrel + flower_pot + orange_terracotta bench int[] ssfc = corners[1]; int ssfcx = ssfc[0] + (ssfc[0] < 6 ? 1 : -1); int ssfcz = ssfc[1] + (ssfc[1] < 6 ? 1 : -1); if ( layout.isInShape(ssfcx, ssfcz) && !layout.isWall(ssfcx, ssfcz) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + ssfcx, floorY + 1, baseZ + ssfcz), Blocks.BARREL.defaultBlockState(), chunkBB ); int ssfcx2 = ssfcx + (ssfcx < 6 ? 1 : -1); if ( layout.isInShape(ssfcx2, ssfcz) && !layout.isWall(ssfcx2, ssfcz) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + ssfcx2, floorY + 1, baseZ + ssfcz), Blocks.FLOWER_POT.defaultBlockState(), chunkBB ); } if ( layout.isInShape(ssfcx, ssfcz + (ssfcz < 6 ? 1 : -1)) && !layout.isWall(ssfcx, ssfcz + (ssfcz < 6 ? 1 : -1)) ) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + ssfcx, floorY + 1, baseZ + ssfcz + (ssfcz < 6 ? 1 : -1) ), Blocks.ORANGE_TERRACOTTA.defaultBlockState(), chunkBB ); } } placeSharedChains(level, baseX, baseZ, floorY, chunkBB); placeSharedHangingLanterns(level, baseX, baseZ, floorY, chunkBB); } @Override public BlockState pillarBlock(RandomSource r, int ry) { if ( ry == 1 || ry == 10 ) return Blocks.CHISELED_SANDSTONE.defaultBlockState(); return Blocks.SANDSTONE_WALL.defaultBlockState(); } @Override @Nullable public BlockState scatterBlock(RandomSource r) { float f = r.nextFloat(); if (f < 0.40f) return Blocks.SAND.defaultBlockState(); if (f < 0.70f) return Blocks.DEAD_BUSH.defaultBlockState(); return Blocks.CANDLE.defaultBlockState() .setValue(BlockStateProperties.CANDLES, 1 + r.nextInt(3)) .setValue(BlockStateProperties.LIT, true); } @Override public BlockState wallAccentBlock() { return Blocks.CHISELED_SANDSTONE.defaultBlockState(); } }; public abstract BlockState wallBlock(RandomSource r, int ry); public abstract BlockState floorBlock( RandomSource r, int rx, int rz, boolean isEdge ); public abstract BlockState ceilingBlock(RandomSource r); /** Solid block used for wall positions on the floor layer. */ public abstract BlockState wallShellBlock(); public abstract void placeDecorations( WorldGenLevel level, RandomSource random, int baseX, int baseZ, int floorY, RoomLayout layout, BoundingBox chunkBB ); /** Block used for pillars (may vary by height). */ public abstract BlockState pillarBlock(RandomSource r, int ry); /** Random scatter block for floor decoration, or null to skip. */ @Nullable public abstract BlockState scatterBlock(RandomSource r); /** Accent block for decorative wall bands. */ public abstract BlockState wallAccentBlock(); // ── Shared structural features ────────────────────────────────── /** Pillar positions -- verified to be inside all 4 layouts. */ private static final int[][] PILLAR_POSITIONS = { { 4, 4 }, { 4, 8 }, { 8, 4 }, { 8, 8 }, }; /** Place 4 full-height pillars at verified positions. */ static void placeSharedPillars( WorldGenLevel level, RandomSource random, int baseX, int baseZ, int floorY, RoomLayout layout, RoomTheme theme, BoundingBox chunkBB ) { for (int[] p : PILLAR_POSITIONS) { if ( !layout.isInShape(p[0], p[1]) || layout.isWall(p[0], p[1]) ) continue; for (int ry = 1; ry <= 10; ry++) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + p[0], floorY + ry, baseZ + p[1]), theme.pillarBlock(random, ry), chunkBB ); } } } /** Place random floor scatter (~12% of interior positions). */ static void placeSharedFloorScatter( WorldGenLevel level, RandomSource random, int baseX, int baseZ, int floorY, RoomLayout layout, RoomTheme theme, BoundingBox chunkBB ) { for (int rx = 1; rx <= 11; rx++) { for (int rz = 1; rz <= 11; rz++) { if ( !layout.isInShape(rx, rz) || layout.isWall(rx, rz) ) continue; // Skip pillar positions if ((rx == 4 || rx == 8) && (rz == 4 || rz == 8)) continue; // Skip cage center area (5-7, 5-7) if (rx >= 5 && rx <= 7 && rz >= 5 && rz <= 7) continue; // Skip corner positions (used by decorations/chests) if ((rx <= 2 || rx >= 10) && (rz <= 2 || rz >= 10)) continue; if (random.nextFloat() < 0.12f) { BlockState scatter = theme.scatterBlock(random); if (scatter != null) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + rx, floorY + 1, baseZ + rz), scatter, chunkBB ); } } } } } /** Place ceiling cobwebs and extra hanging chains. */ static void placeSharedCeilingDecor( WorldGenLevel level, RandomSource random, int baseX, int baseZ, int floorY, RoomLayout layout, BoundingBox chunkBB ) { // Cobwebs along walls at ceiling level int[][] cobwebCandidates = { { 2, 1 }, { 4, 1 }, { 8, 1 }, { 10, 1 }, { 2, 11 }, { 4, 11 }, { 8, 11 }, { 10, 11 }, { 1, 4 }, { 1, 8 }, { 11, 4 }, { 11, 8 }, }; for (int[] pos : cobwebCandidates) { if ( layout.isInShape(pos[0], pos[1]) && !layout.isWall(pos[0], pos[1]) && random.nextFloat() < 0.45f ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + pos[0], floorY + 10, baseZ + pos[1]), Blocks.COBWEB.defaultBlockState(), chunkBB ); } } // Extra hanging chains at random interior positions int[][] chainCandidates = { { 5, 3 }, { 7, 9 }, { 3, 7 } }; for (int[] pos : chainCandidates) { if ( layout.isInShape(pos[0], pos[1]) && !layout.isWall(pos[0], pos[1]) && random.nextFloat() < 0.6f ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + pos[0], floorY + 10, baseZ + pos[1]), Blocks.CHAIN.defaultBlockState(), chunkBB ); HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + pos[0], floorY + 9, baseZ + pos[1]), Blocks.CHAIN.defaultBlockState(), chunkBB ); } } } /** Place additional wall-mounted lighting. */ static void placeSharedWallLighting( WorldGenLevel level, RandomSource random, int baseX, int baseZ, int floorY, RoomLayout layout, BoundingBox chunkBB ) { // Lanterns on pillar sides (facing center) at ry=1 (on the floor) int[][] pillarLanternPositions = { { 5, 4 }, { 4, 7 }, { 8, 5 }, { 7, 8 }, }; for (int[] pos : pillarLanternPositions) { if ( layout.isInShape(pos[0], pos[1]) && !layout.isWall(pos[0], pos[1]) ) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + pos[0], floorY + 1, baseZ + pos[1]), Blocks.LANTERN.defaultBlockState(), chunkBB ); } } // Extra wall sconces at quarter-points int[][] wallSconces = { { 4, 0 }, { 8, 0 }, { 4, 12 }, { 8, 12 }, { 0, 4 }, { 0, 8 }, { 12, 4 }, { 12, 8 }, }; for (int[] pos : wallSconces) { if (layout.isWall(pos[0], pos[1]) && random.nextFloat() < 0.5f) { Direction torchDir = getTorchDirection(pos[0], pos[1]); if (torchDir != null) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + pos[0], floorY + 3, baseZ + pos[1] ), Blocks.WALL_TORCH.defaultBlockState().setValue( net.minecraft.world.level.block.WallTorchBlock.FACING, torchDir ), chunkBB ); } } } } /** Place wall accent bands at ry=5 and ry=8. */ static void placeSharedWallBands( WorldGenLevel level, int baseX, int baseZ, int floorY, RoomLayout layout, RoomTheme theme, BoundingBox chunkBB ) { int[][] bandPositions = { { 6, 1 }, { 6, 11 }, { 1, 6 }, { 11, 6 }, { 4, 1 }, { 8, 1 }, { 4, 11 }, { 8, 11 }, { 1, 4 }, { 1, 8 }, { 11, 4 }, { 11, 8 }, }; for (int[] pos : bandPositions) { if (layout.isWall(pos[0], pos[1])) { // Band at ry=5 HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + pos[0], floorY + 5, baseZ + pos[1]), theme.wallAccentBlock(), chunkBB ); // Optional band at ry=8 HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + pos[0], floorY + 8, baseZ + pos[1]), theme.wallAccentBlock(), chunkBB ); } } } /** Chains on cage flanks and ceiling corners -- shared by all themes. */ static void placeSharedChains( WorldGenLevel level, int baseX, int baseZ, int floorY, BoundingBox chunkBB ) { for (int[] chainPos : new int[][] { { 3, 6 }, { 9, 6 } }) { for (int ry = 5; ry <= 10; ry++) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + chainPos[0], floorY + ry, baseZ + chainPos[1] ), Blocks.CHAIN.defaultBlockState(), chunkBB ); } } for (int[] chainPos : new int[][] { { 3, 3 }, { 3, 9 }, { 9, 3 }, { 9, 9 }, }) { HangingCagePiece.safeSetBlock( level, new BlockPos( baseX + chainPos[0], floorY + 10, baseZ + chainPos[1] ), Blocks.CHAIN.defaultBlockState(), chunkBB ); } } /** Determine torch facing direction for a wall position (torch faces inward). */ static Direction getTorchDirection(int rx, int rz) { if (rz == 0) return Direction.SOUTH; if (rz == 12) return Direction.NORTH; if (rx == 0) return Direction.EAST; if (rx == 12) return Direction.WEST; return null; } /** Hanging lanterns -- shared by all themes. */ static void placeSharedHangingLanterns( WorldGenLevel level, int baseX, int baseZ, int floorY, BoundingBox chunkBB ) { for (int[] pos : new int[][] { { 3, 3 }, { 9, 9 } }) { HangingCagePiece.safeSetBlock( level, new BlockPos(baseX + pos[0], floorY + 9, baseZ + pos[1]), Blocks.LANTERN.defaultBlockState().setValue( LanternBlock.HANGING, true ), chunkBB ); } } }