package net.minecraft.world.level.levelgen.feature; import java.util.function.Consumer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.tags.BlockTags; import net.minecraft.util.Mth; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.PointedDripstoneBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.DripstoneThickness; public class DripstoneUtils { /** * The formula used to control dripstone columns radius. * @see This tweet by Henrik. */ protected static double getDripstoneHeight(double radius, double maxRadius, double scale, double minRadius) { if (radius < minRadius) { radius = minRadius; } double d = 0.384; double e = radius / maxRadius * 0.384; double f = 0.75 * Math.pow(e, 1.3333333333333333); double g = Math.pow(e, 0.6666666666666666); double h = 0.3333333333333333 * Math.log(e); double i = scale * (f - g - h); i = Math.max(i, 0.0); return i / 0.384 * maxRadius; } protected static boolean isCircleMostlyEmbeddedInStone(WorldGenLevel level, BlockPos pos, int radius) { if (isEmptyOrWaterOrLava(level, pos)) { return false; } else { float f = 6.0F; float g = 6.0F / radius; for (float h = 0.0F; h < (float) (Math.PI * 2); h += g) { int i = (int)(Mth.cos(h) * radius); int j = (int)(Mth.sin(h) * radius); if (isEmptyOrWaterOrLava(level, pos.offset(i, 0, j))) { return false; } } return true; } } protected static boolean isEmptyOrWater(LevelAccessor level, BlockPos pos) { return level.isStateAtPosition(pos, DripstoneUtils::isEmptyOrWater); } protected static boolean isEmptyOrWaterOrLava(LevelAccessor level, BlockPos pos) { return level.isStateAtPosition(pos, DripstoneUtils::isEmptyOrWaterOrLava); } protected static void buildBaseToTipColumn(Direction direction, int height, boolean mergeTip, Consumer blockSetter) { if (height >= 3) { blockSetter.accept(createPointedDripstone(direction, DripstoneThickness.BASE)); for (int i = 0; i < height - 3; i++) { blockSetter.accept(createPointedDripstone(direction, DripstoneThickness.MIDDLE)); } } if (height >= 2) { blockSetter.accept(createPointedDripstone(direction, DripstoneThickness.FRUSTUM)); } if (height >= 1) { blockSetter.accept(createPointedDripstone(direction, mergeTip ? DripstoneThickness.TIP_MERGE : DripstoneThickness.TIP)); } } protected static void growPointedDripstone(LevelAccessor level, BlockPos pos, Direction direction, int height, boolean mergeTip) { if (isDripstoneBase(level.getBlockState(pos.relative(direction.getOpposite())))) { BlockPos.MutableBlockPos mutableBlockPos = pos.mutable(); buildBaseToTipColumn(direction, height, mergeTip, blockState -> { if (blockState.is(Blocks.POINTED_DRIPSTONE)) { blockState = blockState.setValue(PointedDripstoneBlock.WATERLOGGED, level.isWaterAt(mutableBlockPos)); } level.setBlock(mutableBlockPos, blockState, 2); mutableBlockPos.move(direction); }); } } protected static boolean placeDripstoneBlockIfPossible(LevelAccessor level, BlockPos pos) { BlockState blockState = level.getBlockState(pos); if (blockState.is(BlockTags.DRIPSTONE_REPLACEABLE)) { level.setBlock(pos, Blocks.DRIPSTONE_BLOCK.defaultBlockState(), 2); return true; } else { return false; } } private static BlockState createPointedDripstone(Direction direction, DripstoneThickness dripstoneThickness) { return Blocks.POINTED_DRIPSTONE .defaultBlockState() .setValue(PointedDripstoneBlock.TIP_DIRECTION, direction) .setValue(PointedDripstoneBlock.THICKNESS, dripstoneThickness); } public static boolean isDripstoneBaseOrLava(BlockState state) { return isDripstoneBase(state) || state.is(Blocks.LAVA); } public static boolean isDripstoneBase(BlockState state) { return state.is(Blocks.DRIPSTONE_BLOCK) || state.is(BlockTags.DRIPSTONE_REPLACEABLE); } public static boolean isEmptyOrWater(BlockState state) { return state.isAir() || state.is(Blocks.WATER); } public static boolean isNeitherEmptyNorWater(BlockState state) { return !state.isAir() && !state.is(Blocks.WATER); } public static boolean isEmptyOrWaterOrLava(BlockState state) { return state.isAir() || state.is(Blocks.WATER) || state.is(Blocks.LAVA); } }