package net.minecraft.world.level.levelgen.feature; import com.mojang.serialization.Codec; import java.util.function.Predicate; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.tags.FluidTags; import net.minecraft.util.RandomSource; import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.levelgen.feature.configurations.RootSystemConfiguration; public class RootSystemFeature extends Feature { public RootSystemFeature(Codec codec) { super(codec); } @Override public boolean place(FeaturePlaceContext context) { WorldGenLevel worldGenLevel = context.level(); BlockPos blockPos = context.origin(); if (!worldGenLevel.getBlockState(blockPos).isAir()) { return false; } else { RandomSource randomSource = context.random(); BlockPos blockPos2 = context.origin(); RootSystemConfiguration rootSystemConfiguration = context.config(); BlockPos.MutableBlockPos mutableBlockPos = blockPos2.mutable(); if (placeDirtAndTree(worldGenLevel, context.chunkGenerator(), rootSystemConfiguration, randomSource, mutableBlockPos, blockPos2)) { placeRoots(worldGenLevel, rootSystemConfiguration, randomSource, blockPos2, mutableBlockPos); } return true; } } private static boolean spaceForTree(WorldGenLevel level, RootSystemConfiguration config, BlockPos pos) { BlockPos.MutableBlockPos mutableBlockPos = pos.mutable(); for (int i = 1; i <= config.requiredVerticalSpaceForTree; i++) { mutableBlockPos.move(Direction.UP); BlockState blockState = level.getBlockState(mutableBlockPos); if (!isAllowedTreeSpace(blockState, i, config.allowedVerticalWaterForTree)) { return false; } } return true; } private static boolean isAllowedTreeSpace(BlockState state, int y, int allowedVerticalWater) { if (state.isAir()) { return true; } else { int i = y + 1; return i <= allowedVerticalWater && state.getFluidState().is(FluidTags.WATER); } } private static boolean placeDirtAndTree( WorldGenLevel level, ChunkGenerator chunkGenerator, RootSystemConfiguration config, RandomSource random, BlockPos.MutableBlockPos mutablePos, BlockPos basePos ) { for (int i = 0; i < config.rootColumnMaxHeight; i++) { mutablePos.move(Direction.UP); if (config.allowedTreePosition.test(level, mutablePos) && spaceForTree(level, config, mutablePos)) { BlockPos blockPos = mutablePos.below(); if (level.getFluidState(blockPos).is(FluidTags.LAVA) || !level.getBlockState(blockPos).isSolid()) { return false; } if (config.treeFeature.value().place(level, chunkGenerator, random, mutablePos)) { placeDirt(basePos, basePos.getY() + i, level, config, random); return true; } } } return false; } private static void placeDirt(BlockPos pos, int maxY, WorldGenLevel level, RootSystemConfiguration config, RandomSource random) { int i = pos.getX(); int j = pos.getZ(); BlockPos.MutableBlockPos mutableBlockPos = pos.mutable(); for (int k = pos.getY(); k < maxY; k++) { placeRootedDirt(level, config, random, i, j, mutableBlockPos.set(i, k, j)); } } private static void placeRootedDirt(WorldGenLevel level, RootSystemConfiguration config, RandomSource random, int x, int z, BlockPos.MutableBlockPos pos) { int i = config.rootRadius; Predicate predicate = blockState -> blockState.is(config.rootReplaceable); for (int j = 0; j < config.rootPlacementAttempts; j++) { pos.setWithOffset(pos, random.nextInt(i) - random.nextInt(i), 0, random.nextInt(i) - random.nextInt(i)); if (predicate.test(level.getBlockState(pos))) { level.setBlock(pos, config.rootStateProvider.getState(random, pos), 2); } pos.setX(x); pos.setZ(z); } } private static void placeRoots(WorldGenLevel level, RootSystemConfiguration config, RandomSource random, BlockPos basePos, BlockPos.MutableBlockPos mutablePos) { int i = config.hangingRootRadius; int j = config.hangingRootsVerticalSpan; for (int k = 0; k < config.hangingRootPlacementAttempts; k++) { mutablePos.setWithOffset(basePos, random.nextInt(i) - random.nextInt(i), random.nextInt(j) - random.nextInt(j), random.nextInt(i) - random.nextInt(i)); if (level.isEmptyBlock(mutablePos)) { BlockState blockState = config.hangingRootStateProvider.getState(random, mutablePos); if (blockState.canSurvive(level, mutablePos) && level.getBlockState(mutablePos.above()).isFaceSturdy(level, mutablePos, Direction.DOWN)) { level.setBlock(mutablePos, blockState, 2); } } } } }