Code/net/minecraft/world/level/levelgen/feature/LargeDripstoneFeature.java

238 lines
11 KiB
Java
Raw Permalink Normal View History

2025-07-01 06:20:03 +00:00
package net.minecraft.world.level.levelgen.feature;
import com.mojang.serialization.Codec;
import java.util.Optional;
import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.util.valueproviders.FloatProvider;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.levelgen.Column;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.feature.configurations.LargeDripstoneConfiguration;
import net.minecraft.world.phys.Vec3;
public class LargeDripstoneFeature extends Feature<LargeDripstoneConfiguration> {
public LargeDripstoneFeature(Codec<LargeDripstoneConfiguration> p_159960_) {
super(p_159960_);
}
@Override
public boolean place(FeaturePlaceContext<LargeDripstoneConfiguration> p_159967_) {
WorldGenLevel worldgenlevel = p_159967_.level();
BlockPos blockpos = p_159967_.origin();
LargeDripstoneConfiguration largedripstoneconfiguration = p_159967_.config();
RandomSource randomsource = p_159967_.random();
if (!DripstoneUtils.isEmptyOrWater(worldgenlevel, blockpos)) {
return false;
} else {
Optional<Column> optional = Column.scan(
worldgenlevel, blockpos, largedripstoneconfiguration.floorToCeilingSearchRange, DripstoneUtils::isEmptyOrWater, DripstoneUtils::isDripstoneBaseOrLava
);
if (!optional.isEmpty() && optional.get() instanceof Column.Range) {
Column.Range column$range = (Column.Range)optional.get();
if (column$range.height() < 4) {
return false;
} else {
int i = (int)(column$range.height() * largedripstoneconfiguration.maxColumnRadiusToCaveHeightRatio);
int j = Mth.clamp(i, largedripstoneconfiguration.columnRadius.getMinValue(), largedripstoneconfiguration.columnRadius.getMaxValue());
int k = Mth.randomBetweenInclusive(randomsource, largedripstoneconfiguration.columnRadius.getMinValue(), j);
LargeDripstoneFeature.LargeDripstone largedripstonefeature$largedripstone = makeDripstone(
blockpos.atY(column$range.ceiling() - 1),
false,
randomsource,
k,
largedripstoneconfiguration.stalactiteBluntness,
largedripstoneconfiguration.heightScale
);
LargeDripstoneFeature.LargeDripstone largedripstonefeature$largedripstone1 = makeDripstone(
blockpos.atY(column$range.floor() + 1),
true,
randomsource,
k,
largedripstoneconfiguration.stalagmiteBluntness,
largedripstoneconfiguration.heightScale
);
LargeDripstoneFeature.WindOffsetter largedripstonefeature$windoffsetter;
if (largedripstonefeature$largedripstone.isSuitableForWind(largedripstoneconfiguration)
&& largedripstonefeature$largedripstone1.isSuitableForWind(largedripstoneconfiguration)) {
largedripstonefeature$windoffsetter = new LargeDripstoneFeature.WindOffsetter(
blockpos.getY(), randomsource, largedripstoneconfiguration.windSpeed
);
} else {
largedripstonefeature$windoffsetter = LargeDripstoneFeature.WindOffsetter.noWind();
}
boolean flag = largedripstonefeature$largedripstone.moveBackUntilBaseIsInsideStoneAndShrinkRadiusIfNecessary(worldgenlevel, largedripstonefeature$windoffsetter);
boolean flag1 = largedripstonefeature$largedripstone1.moveBackUntilBaseIsInsideStoneAndShrinkRadiusIfNecessary(worldgenlevel, largedripstonefeature$windoffsetter);
if (flag) {
largedripstonefeature$largedripstone.placeBlocks(worldgenlevel, randomsource, largedripstonefeature$windoffsetter);
}
if (flag1) {
largedripstonefeature$largedripstone1.placeBlocks(worldgenlevel, randomsource, largedripstonefeature$windoffsetter);
}
return true;
}
} else {
return false;
}
}
}
private static LargeDripstoneFeature.LargeDripstone makeDripstone(
BlockPos p_225139_, boolean p_225140_, RandomSource p_225141_, int p_225142_, FloatProvider p_225143_, FloatProvider p_225144_
) {
return new LargeDripstoneFeature.LargeDripstone(p_225139_, p_225140_, p_225142_, p_225143_.sample(p_225141_), p_225144_.sample(p_225141_));
}
private void placeDebugMarkers(WorldGenLevel p_159962_, BlockPos p_159963_, Column.Range p_159964_, LargeDripstoneFeature.WindOffsetter p_159965_) {
p_159962_.setBlock(p_159965_.offset(p_159963_.atY(p_159964_.ceiling() - 1)), Blocks.DIAMOND_BLOCK.defaultBlockState(), 2);
p_159962_.setBlock(p_159965_.offset(p_159963_.atY(p_159964_.floor() + 1)), Blocks.GOLD_BLOCK.defaultBlockState(), 2);
for (BlockPos.MutableBlockPos blockpos$mutableblockpos = p_159963_.atY(p_159964_.floor() + 2).mutable();
blockpos$mutableblockpos.getY() < p_159964_.ceiling() - 1;
blockpos$mutableblockpos.move(Direction.UP)
) {
BlockPos blockpos = p_159965_.offset(blockpos$mutableblockpos);
if (DripstoneUtils.isEmptyOrWater(p_159962_, blockpos) || p_159962_.getBlockState(blockpos).is(Blocks.DRIPSTONE_BLOCK)) {
p_159962_.setBlock(blockpos, Blocks.CREEPER_HEAD.defaultBlockState(), 2);
}
}
}
static final class LargeDripstone {
private BlockPos root;
private final boolean pointingUp;
private int radius;
private final double bluntness;
private final double scale;
LargeDripstone(BlockPos p_197116_, boolean p_197117_, int p_197118_, double p_197119_, double p_197120_) {
this.root = p_197116_;
this.pointingUp = p_197117_;
this.radius = p_197118_;
this.bluntness = p_197119_;
this.scale = p_197120_;
}
private int getHeight() {
return this.getHeightAtRadius(0.0F);
}
private int getMinY() {
return this.pointingUp ? this.root.getY() : this.root.getY() - this.getHeight();
}
private int getMaxY() {
return !this.pointingUp ? this.root.getY() : this.root.getY() + this.getHeight();
}
boolean moveBackUntilBaseIsInsideStoneAndShrinkRadiusIfNecessary(WorldGenLevel p_159990_, LargeDripstoneFeature.WindOffsetter p_159991_) {
while (this.radius > 1) {
BlockPos.MutableBlockPos blockpos$mutableblockpos = this.root.mutable();
int i = Math.min(10, this.getHeight());
for (int j = 0; j < i; j++) {
if (p_159990_.getBlockState(blockpos$mutableblockpos).is(Blocks.LAVA)) {
return false;
}
if (DripstoneUtils.isCircleMostlyEmbeddedInStone(p_159990_, p_159991_.offset(blockpos$mutableblockpos), this.radius)) {
this.root = blockpos$mutableblockpos;
return true;
}
blockpos$mutableblockpos.move(this.pointingUp ? Direction.DOWN : Direction.UP);
}
this.radius /= 2;
}
return false;
}
private int getHeightAtRadius(float p_159988_) {
return (int)DripstoneUtils.getDripstoneHeight(p_159988_, this.radius, this.scale, this.bluntness);
}
void placeBlocks(WorldGenLevel p_225146_, RandomSource p_225147_, LargeDripstoneFeature.WindOffsetter p_225148_) {
for (int i = -this.radius; i <= this.radius; i++) {
for (int j = -this.radius; j <= this.radius; j++) {
float f = Mth.sqrt(i * i + j * j);
if (!(f > this.radius)) {
int k = this.getHeightAtRadius(f);
if (k > 0) {
if (p_225147_.nextFloat() < 0.2) {
k = (int)(k * Mth.randomBetween(p_225147_, 0.8F, 1.0F));
}
BlockPos.MutableBlockPos blockpos$mutableblockpos = this.root.offset(i, 0, j).mutable();
boolean flag = false;
int l = this.pointingUp
? p_225146_.getHeight(
Heightmap.Types.WORLD_SURFACE_WG, blockpos$mutableblockpos.getX(), blockpos$mutableblockpos.getZ()
)
: Integer.MAX_VALUE;
for (int i1 = 0; i1 < k && blockpos$mutableblockpos.getY() < l; i1++) {
BlockPos blockpos = p_225148_.offset(blockpos$mutableblockpos);
if (DripstoneUtils.isEmptyOrWaterOrLava(p_225146_, blockpos)) {
flag = true;
Block block = Blocks.DRIPSTONE_BLOCK;
p_225146_.setBlock(blockpos, block.defaultBlockState(), 2);
} else if (flag && p_225146_.getBlockState(blockpos).is(BlockTags.BASE_STONE_OVERWORLD)) {
break;
}
blockpos$mutableblockpos.move(this.pointingUp ? Direction.UP : Direction.DOWN);
}
}
}
}
}
}
boolean isSuitableForWind(LargeDripstoneConfiguration p_159997_) {
return this.radius >= p_159997_.minRadiusForWind && this.bluntness >= p_159997_.minBluntnessForWind;
}
}
static final class WindOffsetter {
private final int originY;
@Nullable
private final Vec3 windSpeed;
WindOffsetter(int p_225150_, RandomSource p_225151_, FloatProvider p_225152_) {
this.originY = p_225150_;
float f = p_225152_.sample(p_225151_);
float f1 = Mth.randomBetween(p_225151_, 0.0F, (float) Math.PI);
this.windSpeed = new Vec3(Mth.cos(f1) * f, 0.0, Mth.sin(f1) * f);
}
private WindOffsetter() {
this.originY = 0;
this.windSpeed = null;
}
static LargeDripstoneFeature.WindOffsetter noWind() {
return new LargeDripstoneFeature.WindOffsetter();
}
BlockPos offset(BlockPos p_160009_) {
if (this.windSpeed == null) {
return p_160009_;
} else {
int i = this.originY - p_160009_.getY();
Vec3 vec3 = this.windSpeed.scale(i);
return p_160009_.offset(Mth.floor(vec3.x), 0, Mth.floor(vec3.z));
}
}
}
}