Files
TiedUp-/src/main/java/com/tiedup/remake/worldgen/RoomTheme.java
NotEvil a71093ba9c Remove internal phase comments and format code
Strip all Phase references, TODO/FUTURE roadmap notes, and internal
planning comments from the codebase. Run Prettier for consistent
formatting across all Java files.
2026-04-12 01:25:55 +02:00

1369 lines
48 KiB
Java

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
);
}
}
}