minecraft-src/net/minecraft/world/level/levelgen/feature/TreeFeature.java
2025-07-04 03:45:38 +03:00

246 lines
9.5 KiB
Java

package net.minecraft.world.level.levelgen.feature;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.mojang.serialization.Codec;
import java.util.Iterator;
import java.util.List;
import java.util.OptionalInt;
import java.util.Set;
import java.util.function.BiConsumer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelSimulatedReader;
import net.minecraft.world.level.LevelWriter;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.LeavesBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.levelgen.feature.configurations.TreeConfiguration;
import net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacer.FoliageAttachment;
import net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacer.FoliageSetter;
import net.minecraft.world.level.levelgen.feature.rootplacers.RootPlacer;
import net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecorator.Context;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape;
import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
public class TreeFeature extends Feature<TreeConfiguration> {
private static final int BLOCK_UPDATE_FLAGS = 19;
public TreeFeature(Codec<TreeConfiguration> codec) {
super(codec);
}
public static boolean isVine(LevelSimulatedReader level, BlockPos pos) {
return level.isStateAtPosition(pos, blockState -> blockState.is(Blocks.VINE));
}
public static boolean isAirOrLeaves(LevelSimulatedReader level, BlockPos pos) {
return level.isStateAtPosition(pos, blockState -> blockState.isAir() || blockState.is(BlockTags.LEAVES));
}
private static void setBlockKnownShape(LevelWriter level, BlockPos pos, BlockState state) {
level.setBlock(pos, state, 19);
}
public static boolean validTreePos(LevelSimulatedReader level, BlockPos pos) {
return level.isStateAtPosition(pos, blockState -> blockState.isAir() || blockState.is(BlockTags.REPLACEABLE_BY_TREES));
}
private boolean doPlace(
WorldGenLevel level,
RandomSource random,
BlockPos pos,
BiConsumer<BlockPos, BlockState> rootBlockSetter,
BiConsumer<BlockPos, BlockState> trunkBlockSetter,
FoliageSetter foliageBlockSetter,
TreeConfiguration config
) {
int i = config.trunkPlacer.getTreeHeight(random);
int j = config.foliagePlacer.foliageHeight(random, i, config);
int k = i - j;
int l = config.foliagePlacer.foliageRadius(random, k);
BlockPos blockPos = (BlockPos)config.rootPlacer.map(rootPlacer -> rootPlacer.getTrunkOrigin(pos, random)).orElse(pos);
int m = Math.min(pos.getY(), blockPos.getY());
int n = Math.max(pos.getY(), blockPos.getY()) + i + 1;
if (m >= level.getMinY() + 1 && n <= level.getMaxY() + 1) {
OptionalInt optionalInt = config.minimumSize.minClippedHeight();
int o = this.getMaxFreeTreeHeight(level, i, blockPos, config);
if (o >= i || !optionalInt.isEmpty() && o >= optionalInt.getAsInt()) {
if (config.rootPlacer.isPresent() && !((RootPlacer)config.rootPlacer.get()).placeRoots(level, rootBlockSetter, random, pos, blockPos, config)) {
return false;
} else {
List<FoliageAttachment> list = config.trunkPlacer.placeTrunk(level, trunkBlockSetter, random, o, blockPos, config);
list.forEach(foliageAttachment -> config.foliagePlacer.createFoliage(level, foliageBlockSetter, random, config, o, foliageAttachment, j, l));
return true;
}
} else {
return false;
}
} else {
return false;
}
}
private int getMaxFreeTreeHeight(LevelSimulatedReader level, int trunkHeight, BlockPos topPosition, TreeConfiguration config) {
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
for (int i = 0; i <= trunkHeight + 1; i++) {
int j = config.minimumSize.getSizeAtHeight(trunkHeight, i);
for (int k = -j; k <= j; k++) {
for (int l = -j; l <= j; l++) {
mutableBlockPos.setWithOffset(topPosition, k, i, l);
if (!config.trunkPlacer.isFree(level, mutableBlockPos) || !config.ignoreVines && isVine(level, mutableBlockPos)) {
return i - 2;
}
}
}
}
return trunkHeight;
}
@Override
protected void setBlock(LevelWriter level, BlockPos pos, BlockState state) {
setBlockKnownShape(level, pos, state);
}
@Override
public final boolean place(FeaturePlaceContext<TreeConfiguration> context) {
final WorldGenLevel worldGenLevel = context.level();
RandomSource randomSource = context.random();
BlockPos blockPos = context.origin();
TreeConfiguration treeConfiguration = context.config();
Set<BlockPos> set = Sets.<BlockPos>newHashSet();
Set<BlockPos> set2 = Sets.<BlockPos>newHashSet();
final Set<BlockPos> set3 = Sets.<BlockPos>newHashSet();
Set<BlockPos> set4 = Sets.<BlockPos>newHashSet();
BiConsumer<BlockPos, BlockState> biConsumer = (blockPosx, blockState) -> {
set.add(blockPosx.immutable());
worldGenLevel.setBlock(blockPosx, blockState, 19);
};
BiConsumer<BlockPos, BlockState> biConsumer2 = (blockPosx, blockState) -> {
set2.add(blockPosx.immutable());
worldGenLevel.setBlock(blockPosx, blockState, 19);
};
FoliageSetter foliageSetter = new FoliageSetter() {
@Override
public void set(BlockPos pos, BlockState state) {
set3.add(pos.immutable());
worldGenLevel.setBlock(pos, state, 19);
}
@Override
public boolean isSet(BlockPos pos) {
return set3.contains(pos);
}
};
BiConsumer<BlockPos, BlockState> biConsumer3 = (blockPosx, blockState) -> {
set4.add(blockPosx.immutable());
worldGenLevel.setBlock(blockPosx, blockState, 19);
};
boolean bl = this.doPlace(worldGenLevel, randomSource, blockPos, biConsumer, biConsumer2, foliageSetter, treeConfiguration);
if (bl && (!set2.isEmpty() || !set3.isEmpty())) {
if (!treeConfiguration.decorators.isEmpty()) {
Context context2 = new Context(worldGenLevel, biConsumer3, randomSource, set2, set3, set);
treeConfiguration.decorators.forEach(treeDecorator -> treeDecorator.place(context2));
}
return (Boolean)BoundingBox.encapsulatingPositions(Iterables.concat(set, set2, set3, set4)).map(boundingBox -> {
DiscreteVoxelShape discreteVoxelShape = updateLeaves(worldGenLevel, boundingBox, set2, set4, set);
StructureTemplate.updateShapeAtEdge(worldGenLevel, 3, discreteVoxelShape, boundingBox.minX(), boundingBox.minY(), boundingBox.minZ());
return true;
}).orElse(false);
} else {
return false;
}
}
private static DiscreteVoxelShape updateLeaves(
LevelAccessor level, BoundingBox box, Set<BlockPos> rootPositions, Set<BlockPos> trunkPositions, Set<BlockPos> foliagePositions
) {
DiscreteVoxelShape discreteVoxelShape = new BitSetDiscreteVoxelShape(box.getXSpan(), box.getYSpan(), box.getZSpan());
int i = 7;
List<Set<BlockPos>> list = Lists.<Set<BlockPos>>newArrayList();
for (int j = 0; j < 7; j++) {
list.add(Sets.newHashSet());
}
for (BlockPos blockPos : Lists.newArrayList(Sets.union(trunkPositions, foliagePositions))) {
if (box.isInside(blockPos)) {
discreteVoxelShape.fill(blockPos.getX() - box.minX(), blockPos.getY() - box.minY(), blockPos.getZ() - box.minZ());
}
}
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
int k = 0;
((Set)list.get(0)).addAll(rootPositions);
while (true) {
while (k >= 7 || !((Set)list.get(k)).isEmpty()) {
if (k >= 7) {
return discreteVoxelShape;
}
Iterator<BlockPos> iterator = ((Set)list.get(k)).iterator();
BlockPos blockPos2 = (BlockPos)iterator.next();
iterator.remove();
if (box.isInside(blockPos2)) {
if (k != 0) {
BlockState blockState = level.getBlockState(blockPos2);
setBlockKnownShape(level, blockPos2, blockState.setValue(BlockStateProperties.DISTANCE, k));
}
discreteVoxelShape.fill(blockPos2.getX() - box.minX(), blockPos2.getY() - box.minY(), blockPos2.getZ() - box.minZ());
for (Direction direction : Direction.values()) {
mutableBlockPos.setWithOffset(blockPos2, direction);
if (box.isInside(mutableBlockPos)) {
int l = mutableBlockPos.getX() - box.minX();
int m = mutableBlockPos.getY() - box.minY();
int n = mutableBlockPos.getZ() - box.minZ();
if (!discreteVoxelShape.isFull(l, m, n)) {
BlockState blockState2 = level.getBlockState(mutableBlockPos);
OptionalInt optionalInt = LeavesBlock.getOptionalDistanceAt(blockState2);
if (!optionalInt.isEmpty()) {
int o = Math.min(optionalInt.getAsInt(), k + 1);
if (o < 7) {
((Set)list.get(o)).add(mutableBlockPos.immutable());
k = Math.min(k, o);
}
}
}
}
}
}
}
k++;
}
}
public static List<BlockPos> getLowestTrunkOrRootOfTree(Context context) {
List<BlockPos> list = Lists.<BlockPos>newArrayList();
List<BlockPos> list2 = context.roots();
List<BlockPos> list3 = context.logs();
if (list2.isEmpty()) {
list.addAll(list3);
} else if (!list3.isEmpty() && ((BlockPos)list2.get(0)).getY() == ((BlockPos)list3.get(0)).getY()) {
list.addAll(list3);
list.addAll(list2);
} else {
list.addAll(list2);
}
return list;
}
}