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