package net.minecraft.world.level.levelgen.feature; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.core.BlockPos; import net.minecraft.tags.FluidTags; import net.minecraft.util.RandomSource; import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate; import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider; @Deprecated public class LakeFeature extends Feature { private static final BlockState AIR = Blocks.CAVE_AIR.defaultBlockState(); public LakeFeature(final Codec codec) { super(codec); } @Override public boolean place(final FeaturePlaceContext context) { BlockPos origin = context.origin(); WorldGenLevel level = context.level(); RandomSource random = context.random(); LakeFeature.Configuration config = context.config(); if (origin.getY() <= level.getMinY() + 4) { return false; } else { origin = origin.offset(-8, -4, -8); boolean[] grid = new boolean[2048]; int spots = random.nextInt(4) + 4; for (int i = 0; i < spots; i++) { double xr = random.nextDouble() * 6.0 + 3.0; double yr = random.nextDouble() * 4.0 + 2.0; double zr = random.nextDouble() * 6.0 + 3.0; double xp = random.nextDouble() * (16.0 - xr - 2.0) + 1.0 + xr / 2.0; double yp = random.nextDouble() * (8.0 - yr - 4.0) + 2.0 + yr / 2.0; double zp = random.nextDouble() * (16.0 - zr - 2.0) + 1.0 + zr / 2.0; for (int xx = 1; xx < 15; xx++) { for (int zz = 1; zz < 15; zz++) { for (int yy = 1; yy < 7; yy++) { double xd = (xx - xp) / (xr / 2.0); double yd = (yy - yp) / (yr / 2.0); double zd = (zz - zp) / (zr / 2.0); double d = xd * xd + yd * yd + zd * zd; if (d < 1.0) { grid[(xx * 16 + zz) * 8 + yy] = true; } } } } } BlockState fluid = config.fluid().getState(level, random, origin); for (int xx = 0; xx < 16; xx++) { for (int zz = 0; zz < 16; zz++) { for (int yyx = 0; yyx < 8; yyx++) { boolean check = !grid[(xx * 16 + zz) * 8 + yyx] && ( xx < 15 && grid[((xx + 1) * 16 + zz) * 8 + yyx] || xx > 0 && grid[((xx - 1) * 16 + zz) * 8 + yyx] || zz < 15 && grid[(xx * 16 + zz + 1) * 8 + yyx] || zz > 0 && grid[(xx * 16 + (zz - 1)) * 8 + yyx] || yyx < 7 && grid[(xx * 16 + zz) * 8 + yyx + 1] || yyx > 0 && grid[(xx * 16 + zz) * 8 + (yyx - 1)] ); if (check) { BlockPos offsetPos = origin.offset(xx, yyx, zz); BlockState blockState = level.getBlockState(offsetPos); if (yyx >= 4 && blockState.liquid()) { return false; } if (yyx < 4 && !blockState.isSolid() && blockState != fluid) { return false; } if (!config.canPlaceFeature.test(level, offsetPos)) { return false; } } } } } for (int xx = 0; xx < 16; xx++) { for (int zz = 0; zz < 16; zz++) { for (int yyxx = 0; yyxx < 8; yyxx++) { if (grid[(xx * 16 + zz) * 8 + yyxx]) { BlockPos placePos = origin.offset(xx, yyxx, zz); if (config.canReplaceWithAirOrFluid.test(level, placePos)) { boolean placeAir = yyxx >= 4; level.setBlock(placePos, placeAir ? AIR : fluid, 2); if (placeAir) { level.scheduleTick(placePos, AIR.getBlock(), 0); this.markAboveForPostProcessing(level, placePos); } } } } } } BlockState barrier = config.barrier().getState(level, random, origin); if (!barrier.isAir()) { for (int xx = 0; xx < 16; xx++) { for (int zz = 0; zz < 16; zz++) { for (int yyxxx = 0; yyxxx < 8; yyxxx++) { boolean check = !grid[(xx * 16 + zz) * 8 + yyxxx] && ( xx < 15 && grid[((xx + 1) * 16 + zz) * 8 + yyxxx] || xx > 0 && grid[((xx - 1) * 16 + zz) * 8 + yyxxx] || zz < 15 && grid[(xx * 16 + zz + 1) * 8 + yyxxx] || zz > 0 && grid[(xx * 16 + (zz - 1)) * 8 + yyxxx] || yyxxx < 7 && grid[(xx * 16 + zz) * 8 + yyxxx + 1] || yyxxx > 0 && grid[(xx * 16 + zz) * 8 + (yyxxx - 1)] ); if (check && (yyxxx < 4 || random.nextInt(2) != 0)) { BlockPos offset = origin.offset(xx, yyxxx, zz); BlockState blockStatex = level.getBlockState(offset); if (blockStatex.isSolid() && config.canReplaceWithBarrier.test(level, offset)) { BlockPos barrierPos = origin.offset(xx, yyxxx, zz); level.setBlock(barrierPos, barrier, 2); this.markAboveForPostProcessing(level, barrierPos); } } } } } } if (fluid.getFluidState().is(FluidTags.WATER)) { for (int xx = 0; xx < 16; xx++) { for (int zz = 0; zz < 16; zz++) { int yyxxxx = 4; BlockPos offset = origin.offset(xx, 4, zz); if (level.getBiome(offset).value().shouldFreeze(level, offset, false) && config.canReplaceWithAirOrFluid.test(level, offset)) { level.setBlock(offset, Blocks.ICE.defaultBlockState(), 2); } } } } return true; } } public record Configuration( BlockStateProvider fluid, BlockStateProvider barrier, BlockPredicate canPlaceFeature, BlockPredicate canReplaceWithAirOrFluid, BlockPredicate canReplaceWithBarrier ) implements FeatureConfiguration { public static final Codec CODEC = RecordCodecBuilder.create( i -> i.group( BlockStateProvider.CODEC.fieldOf("fluid").forGetter(LakeFeature.Configuration::fluid), BlockStateProvider.CODEC.fieldOf("barrier").forGetter(LakeFeature.Configuration::barrier), BlockPredicate.CODEC.fieldOf("can_place_feature").forGetter(LakeFeature.Configuration::canPlaceFeature), BlockPredicate.CODEC.fieldOf("can_replace_with_air_or_fluid").forGetter(LakeFeature.Configuration::canReplaceWithAirOrFluid), BlockPredicate.CODEC.fieldOf("can_replace_with_barrier").forGetter(LakeFeature.Configuration::canReplaceWithBarrier) ) .apply(i, LakeFeature.Configuration::new) ); } }