287 lines
15 KiB
Java
287 lines
15 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*/
|
|
package net.minecraft.world.level.levelgen;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.Optional;
|
|
import java.util.function.Function;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Holder;
|
|
import net.minecraft.core.Registry;
|
|
import net.minecraft.core.registries.Registries;
|
|
import net.minecraft.resources.Identifier;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.util.RandomSource;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.LevelHeightAccessor;
|
|
import net.minecraft.world.level.biome.Biome;
|
|
import net.minecraft.world.level.biome.BiomeManager;
|
|
import net.minecraft.world.level.biome.Biomes;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.chunk.BlockColumn;
|
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.dimension.DimensionType;
|
|
import net.minecraft.world.level.levelgen.Heightmap;
|
|
import net.minecraft.world.level.levelgen.NoiseChunk;
|
|
import net.minecraft.world.level.levelgen.Noises;
|
|
import net.minecraft.world.level.levelgen.PositionalRandomFactory;
|
|
import net.minecraft.world.level.levelgen.RandomState;
|
|
import net.minecraft.world.level.levelgen.SurfaceRules;
|
|
import net.minecraft.world.level.levelgen.WorldGenerationContext;
|
|
import net.minecraft.world.level.levelgen.carver.CarvingContext;
|
|
import net.minecraft.world.level.levelgen.synth.NormalNoise;
|
|
|
|
public class SurfaceSystem {
|
|
private static final BlockState WHITE_TERRACOTTA = Blocks.WHITE_TERRACOTTA.defaultBlockState();
|
|
private static final BlockState ORANGE_TERRACOTTA = Blocks.ORANGE_TERRACOTTA.defaultBlockState();
|
|
private static final BlockState TERRACOTTA = Blocks.TERRACOTTA.defaultBlockState();
|
|
private static final BlockState YELLOW_TERRACOTTA = Blocks.YELLOW_TERRACOTTA.defaultBlockState();
|
|
private static final BlockState BROWN_TERRACOTTA = Blocks.BROWN_TERRACOTTA.defaultBlockState();
|
|
private static final BlockState RED_TERRACOTTA = Blocks.RED_TERRACOTTA.defaultBlockState();
|
|
private static final BlockState LIGHT_GRAY_TERRACOTTA = Blocks.LIGHT_GRAY_TERRACOTTA.defaultBlockState();
|
|
private static final BlockState PACKED_ICE = Blocks.PACKED_ICE.defaultBlockState();
|
|
private static final BlockState SNOW_BLOCK = Blocks.SNOW_BLOCK.defaultBlockState();
|
|
private final BlockState defaultBlock;
|
|
private final int seaLevel;
|
|
private final BlockState[] clayBands;
|
|
private final NormalNoise clayBandsOffsetNoise;
|
|
private final NormalNoise badlandsPillarNoise;
|
|
private final NormalNoise badlandsPillarRoofNoise;
|
|
private final NormalNoise badlandsSurfaceNoise;
|
|
private final NormalNoise icebergPillarNoise;
|
|
private final NormalNoise icebergPillarRoofNoise;
|
|
private final NormalNoise icebergSurfaceNoise;
|
|
private final PositionalRandomFactory noiseRandom;
|
|
private final NormalNoise surfaceNoise;
|
|
private final NormalNoise surfaceSecondaryNoise;
|
|
|
|
public SurfaceSystem(RandomState randomState, BlockState defaultBlock, int seaLevel, PositionalRandomFactory noiseRandom) {
|
|
this.defaultBlock = defaultBlock;
|
|
this.seaLevel = seaLevel;
|
|
this.noiseRandom = noiseRandom;
|
|
this.clayBandsOffsetNoise = randomState.getOrCreateNoise(Noises.CLAY_BANDS_OFFSET);
|
|
this.clayBands = SurfaceSystem.generateBands(noiseRandom.fromHashOf(Identifier.withDefaultNamespace("clay_bands")));
|
|
this.surfaceNoise = randomState.getOrCreateNoise(Noises.SURFACE);
|
|
this.surfaceSecondaryNoise = randomState.getOrCreateNoise(Noises.SURFACE_SECONDARY);
|
|
this.badlandsPillarNoise = randomState.getOrCreateNoise(Noises.BADLANDS_PILLAR);
|
|
this.badlandsPillarRoofNoise = randomState.getOrCreateNoise(Noises.BADLANDS_PILLAR_ROOF);
|
|
this.badlandsSurfaceNoise = randomState.getOrCreateNoise(Noises.BADLANDS_SURFACE);
|
|
this.icebergPillarNoise = randomState.getOrCreateNoise(Noises.ICEBERG_PILLAR);
|
|
this.icebergPillarRoofNoise = randomState.getOrCreateNoise(Noises.ICEBERG_PILLAR_ROOF);
|
|
this.icebergSurfaceNoise = randomState.getOrCreateNoise(Noises.ICEBERG_SURFACE);
|
|
}
|
|
|
|
public void buildSurface(RandomState randomState, BiomeManager biomeManager, Registry<Biome> biomes, boolean useLegacyRandom, WorldGenerationContext generationContext, final ChunkAccess protoChunk, NoiseChunk noiseChunk, SurfaceRules.RuleSource ruleSource) {
|
|
final BlockPos.MutableBlockPos columnPos = new BlockPos.MutableBlockPos();
|
|
final ChunkPos chunkPos = protoChunk.getPos();
|
|
int minBlockX = chunkPos.getMinBlockX();
|
|
int minBlockZ = chunkPos.getMinBlockZ();
|
|
BlockColumn column = new BlockColumn(){
|
|
|
|
@Override
|
|
public BlockState getBlock(int blockY) {
|
|
return protoChunk.getBlockState(columnPos.setY(blockY));
|
|
}
|
|
|
|
@Override
|
|
public void setBlock(int blockY, BlockState state) {
|
|
LevelHeightAccessor heightAccessor = protoChunk.getHeightAccessorForGeneration();
|
|
if (heightAccessor.isInsideBuildHeight(blockY)) {
|
|
protoChunk.setBlockState(columnPos.setY(blockY), state);
|
|
if (!state.getFluidState().isEmpty()) {
|
|
protoChunk.markPosForPostprocessing(columnPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
public String toString() {
|
|
return "ChunkBlockColumn " + String.valueOf(chunkPos);
|
|
}
|
|
};
|
|
SurfaceRules.Context context = new SurfaceRules.Context(this, randomState, protoChunk, noiseChunk, biomeManager::getBiome, biomes, generationContext);
|
|
SurfaceRules.SurfaceRule rule = (SurfaceRules.SurfaceRule)ruleSource.apply(context);
|
|
BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos();
|
|
for (int x = 0; x < 16; ++x) {
|
|
for (int z = 0; z < 16; ++z) {
|
|
int blockX = minBlockX + x;
|
|
int blockZ = minBlockZ + z;
|
|
int startingHeight = protoChunk.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z) + 1;
|
|
columnPos.setX(blockX).setZ(blockZ);
|
|
Holder<Biome> surfaceBiome = biomeManager.getBiome(blockPos.set(blockX, useLegacyRandom ? 0 : startingHeight, blockZ));
|
|
if (surfaceBiome.is(Biomes.ERODED_BADLANDS)) {
|
|
this.erodedBadlandsExtension(column, blockX, blockZ, startingHeight, protoChunk);
|
|
}
|
|
int height = protoChunk.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z) + 1;
|
|
context.updateXZ(blockX, blockZ);
|
|
int stoneAboveDepth = 0;
|
|
int waterHeight = Integer.MIN_VALUE;
|
|
int nextCeilingStoneY = Integer.MAX_VALUE;
|
|
int endY = protoChunk.getMinY();
|
|
for (int y = height; y >= endY; --y) {
|
|
BlockState state;
|
|
BlockState old = column.getBlock(y);
|
|
if (old.isAir()) {
|
|
stoneAboveDepth = 0;
|
|
waterHeight = Integer.MIN_VALUE;
|
|
continue;
|
|
}
|
|
if (!old.getFluidState().isEmpty()) {
|
|
if (waterHeight != Integer.MIN_VALUE) continue;
|
|
waterHeight = y + 1;
|
|
continue;
|
|
}
|
|
if (nextCeilingStoneY >= y) {
|
|
nextCeilingStoneY = DimensionType.WAY_BELOW_MIN_Y;
|
|
for (int lookaheadY = y - 1; lookaheadY >= endY - 1; --lookaheadY) {
|
|
BlockState nextState = column.getBlock(lookaheadY);
|
|
if (this.isStone(nextState)) continue;
|
|
nextCeilingStoneY = lookaheadY + 1;
|
|
break;
|
|
}
|
|
}
|
|
int stoneBelowDepth = y - nextCeilingStoneY + 1;
|
|
context.updateY(++stoneAboveDepth, stoneBelowDepth, waterHeight, blockX, y, blockZ);
|
|
if (old != this.defaultBlock || (state = rule.tryApply(blockX, y, blockZ)) == null) continue;
|
|
column.setBlock(y, state);
|
|
}
|
|
if (!surfaceBiome.is(Biomes.FROZEN_OCEAN) && !surfaceBiome.is(Biomes.DEEP_FROZEN_OCEAN)) continue;
|
|
this.frozenOceanExtension(context.getMinSurfaceLevel(), surfaceBiome.value(), column, blockPos, blockX, blockZ, startingHeight);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected int getSurfaceDepth(int blockX, int blockZ) {
|
|
double noiseValue = this.surfaceNoise.getValue(blockX, 0.0, blockZ);
|
|
return (int)(noiseValue * 2.75 + 3.0 + this.noiseRandom.at(blockX, 0, blockZ).nextDouble() * 0.25);
|
|
}
|
|
|
|
protected double getSurfaceSecondary(int blockX, int blockZ) {
|
|
return this.surfaceSecondaryNoise.getValue(blockX, 0.0, blockZ);
|
|
}
|
|
|
|
private boolean isStone(BlockState state) {
|
|
return !state.isAir() && state.getFluidState().isEmpty();
|
|
}
|
|
|
|
public int getSeaLevel() {
|
|
return this.seaLevel;
|
|
}
|
|
|
|
@Deprecated
|
|
public Optional<BlockState> topMaterial(SurfaceRules.RuleSource ruleSource, CarvingContext carvingContext, Function<BlockPos, Holder<Biome>> biomeGetter, ChunkAccess chunk, NoiseChunk noiseChunk, BlockPos pos, boolean underFluid) {
|
|
SurfaceRules.Context context = new SurfaceRules.Context(this, carvingContext.randomState(), chunk, noiseChunk, biomeGetter, (Registry<Biome>)carvingContext.registryAccess().lookupOrThrow(Registries.BIOME), carvingContext);
|
|
SurfaceRules.SurfaceRule rule = (SurfaceRules.SurfaceRule)ruleSource.apply(context);
|
|
int blockX = pos.getX();
|
|
int blockY = pos.getY();
|
|
int blockZ = pos.getZ();
|
|
context.updateXZ(blockX, blockZ);
|
|
context.updateY(1, 1, underFluid ? blockY + 1 : Integer.MIN_VALUE, blockX, blockY, blockZ);
|
|
BlockState state = rule.tryApply(blockX, blockY, blockZ);
|
|
return Optional.ofNullable(state);
|
|
}
|
|
|
|
private void erodedBadlandsExtension(BlockColumn column, int blockX, int blockZ, int height, LevelHeightAccessor protoChunk) {
|
|
BlockState oldState;
|
|
int y;
|
|
double pillarNoiseScale = 0.2;
|
|
double pillarBuffer = Math.min(Math.abs(this.badlandsSurfaceNoise.getValue(blockX, 0.0, blockZ) * 8.25), this.badlandsPillarNoise.getValue((double)blockX * 0.2, 0.0, (double)blockZ * 0.2) * 15.0);
|
|
if (pillarBuffer <= 0.0) {
|
|
return;
|
|
}
|
|
double floorNoiseSampleResolution = 0.75;
|
|
double floorAmplitude = 1.5;
|
|
double pillarFloor = Math.abs(this.badlandsPillarRoofNoise.getValue((double)blockX * 0.75, 0.0, (double)blockZ * 0.75) * 1.5);
|
|
double extensionTop = 64.0 + Math.min(pillarBuffer * pillarBuffer * 2.5, Math.ceil(pillarFloor * 50.0) + 24.0);
|
|
int startY = Mth.floor(extensionTop);
|
|
if (height > startY) {
|
|
return;
|
|
}
|
|
for (y = startY; y >= protoChunk.getMinY() && !(oldState = column.getBlock(y)).is(this.defaultBlock.getBlock()); --y) {
|
|
if (!oldState.is(Blocks.WATER)) continue;
|
|
return;
|
|
}
|
|
for (y = startY; y >= protoChunk.getMinY() && column.getBlock(y).isAir(); --y) {
|
|
column.setBlock(y, this.defaultBlock);
|
|
}
|
|
}
|
|
|
|
private void frozenOceanExtension(int minSurfaceLevel, Biome surfaceBiome, BlockColumn column, BlockPos.MutableBlockPos blockPos, int blockX, int blockZ, int height) {
|
|
double extensionBottom;
|
|
double pillarScale = 1.28;
|
|
double iceberg = Math.min(Math.abs(this.icebergSurfaceNoise.getValue(blockX, 0.0, blockZ) * 8.25), this.icebergPillarNoise.getValue((double)blockX * 1.28, 0.0, (double)blockZ * 1.28) * 15.0);
|
|
if (iceberg <= 1.8) {
|
|
return;
|
|
}
|
|
double roofScale = 1.17;
|
|
double roofAmplitude = 1.5;
|
|
double icebergRoof = Math.abs(this.icebergPillarRoofNoise.getValue((double)blockX * 1.17, 0.0, (double)blockZ * 1.17) * 1.5);
|
|
double top = Math.min(iceberg * iceberg * 1.2, Math.ceil(icebergRoof * 40.0) + 14.0);
|
|
if (surfaceBiome.shouldMeltFrozenOceanIcebergSlightly(blockPos.set(blockX, this.seaLevel, blockZ), this.seaLevel)) {
|
|
top -= 2.0;
|
|
}
|
|
if (top > 2.0) {
|
|
extensionBottom = (double)this.seaLevel - top - 7.0;
|
|
top += (double)this.seaLevel;
|
|
} else {
|
|
top = 0.0;
|
|
extensionBottom = 0.0;
|
|
}
|
|
double extensionTop = top;
|
|
RandomSource random = this.noiseRandom.at(blockX, 0, blockZ);
|
|
int maxSnowDepth = 2 + random.nextInt(4);
|
|
int minSnowHeight = this.seaLevel + 18 + random.nextInt(10);
|
|
int snowDepth = 0;
|
|
for (int y = Math.max(height, (int)extensionTop + 1); y >= minSurfaceLevel; --y) {
|
|
if (!(column.getBlock(y).isAir() && y < (int)extensionTop && random.nextDouble() > 0.01) && (!column.getBlock(y).is(Blocks.WATER) || y <= (int)extensionBottom || y >= this.seaLevel || extensionBottom == 0.0 || !(random.nextDouble() > 0.15))) continue;
|
|
if (snowDepth <= maxSnowDepth && y > minSnowHeight) {
|
|
column.setBlock(y, SNOW_BLOCK);
|
|
++snowDepth;
|
|
continue;
|
|
}
|
|
column.setBlock(y, PACKED_ICE);
|
|
}
|
|
}
|
|
|
|
private static BlockState[] generateBands(RandomSource random) {
|
|
Object[] clayBands = new BlockState[192];
|
|
Arrays.fill(clayBands, TERRACOTTA);
|
|
for (int i = 0; i < clayBands.length; ++i) {
|
|
if ((i += random.nextInt(5) + 1) >= clayBands.length) continue;
|
|
clayBands[i] = ORANGE_TERRACOTTA;
|
|
}
|
|
SurfaceSystem.makeBands(random, (BlockState[])clayBands, 1, YELLOW_TERRACOTTA);
|
|
SurfaceSystem.makeBands(random, (BlockState[])clayBands, 2, BROWN_TERRACOTTA);
|
|
SurfaceSystem.makeBands(random, (BlockState[])clayBands, 1, RED_TERRACOTTA);
|
|
int whiteBandCount = random.nextIntBetweenInclusive(9, 15);
|
|
int i = 0;
|
|
for (int start = 0; i < whiteBandCount && start < clayBands.length; ++i, start += random.nextInt(16) + 4) {
|
|
clayBands[start] = WHITE_TERRACOTTA;
|
|
if (start - 1 > 0 && random.nextBoolean()) {
|
|
clayBands[start - 1] = LIGHT_GRAY_TERRACOTTA;
|
|
}
|
|
if (start + 1 >= clayBands.length || !random.nextBoolean()) continue;
|
|
clayBands[start + 1] = LIGHT_GRAY_TERRACOTTA;
|
|
}
|
|
return clayBands;
|
|
}
|
|
|
|
private static void makeBands(RandomSource random, BlockState[] clayBands, int baseWidth, BlockState state) {
|
|
int bandCount = random.nextIntBetweenInclusive(6, 15);
|
|
for (int i = 0; i < bandCount; ++i) {
|
|
int width = baseWidth + random.nextInt(3);
|
|
int start = random.nextInt(clayBands.length);
|
|
for (int p = 0; start + p < clayBands.length && p < width; ++p) {
|
|
clayBands[start + p] = state;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected BlockState getBand(int worldX, int y, int worldZ) {
|
|
int offset = (int)Math.round(this.clayBandsOffsetNoise.getValue(worldX, 0.0, worldZ) * 4.0);
|
|
return this.clayBands[(y + offset + this.clayBands.length) % this.clayBands.length];
|
|
}
|
|
}
|
|
|