package net.minecraft.world.level.levelgen.feature; import com.mojang.serialization.Codec; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Function; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.block.RotatedPillarBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.feature.configurations.FallenTreeConfiguration; import net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecorator; public class FallenTreeFeature extends Feature { private static final int STUMP_HEIGHT = 1; private static final int STUMP_HEIGHT_PLUS_EMPTY_SPACE = 2; private static final int FALLEN_LOG_MAX_FALL_HEIGHT_TO_GROUND = 5; private static final int FALLEN_LOG_MAX_GROUND_GAP = 2; private static final int FALLEN_LOG_MAX_SPACE_FROM_STUMP = 2; private static final int BLOCK_UPDATE_FLAGS = 19; public FallenTreeFeature(Codec p_392549_) { super(p_392549_); } @Override public boolean place(FeaturePlaceContext p_393978_) { this.placeFallenTree(p_393978_.config(), p_393978_.origin(), p_393978_.level(), p_393978_.random()); return true; } private void placeFallenTree(FallenTreeConfiguration p_392644_, BlockPos p_394187_, WorldGenLevel p_395647_, RandomSource p_391733_) { this.placeStump(p_392644_, p_395647_, p_391733_, p_394187_.mutable()); Direction direction = Direction.Plane.HORIZONTAL.getRandomDirection(p_391733_); int i = p_392644_.logLength.sample(p_391733_) - 2; BlockPos.MutableBlockPos blockpos$mutableblockpos = p_394187_.relative(direction, 2 + p_391733_.nextInt(2)).mutable(); this.setGroundHeightForFallenLogStartPos(p_395647_, blockpos$mutableblockpos); if (this.canPlaceEntireFallenLog(p_395647_, i, blockpos$mutableblockpos, direction)) { this.placeFallenLog(p_392644_, p_395647_, p_391733_, i, blockpos$mutableblockpos, direction); } } private void setGroundHeightForFallenLogStartPos(WorldGenLevel p_396419_, BlockPos.MutableBlockPos p_397012_) { p_397012_.move(Direction.UP, 1); for (int i = 0; i < 6; i++) { if (this.mayPlaceOn(p_396419_, p_397012_)) { return; } p_397012_.move(Direction.DOWN); } } private void placeStump(FallenTreeConfiguration p_395657_, WorldGenLevel p_396801_, RandomSource p_392489_, BlockPos.MutableBlockPos p_394715_) { BlockPos blockpos = this.placeLogBlock(p_395657_, p_396801_, p_392489_, p_394715_, Function.identity()); this.decorateLogs(p_396801_, p_392489_, Set.of(blockpos), p_395657_.stumpDecorators); } private boolean canPlaceEntireFallenLog(WorldGenLevel p_394921_, int p_393137_, BlockPos.MutableBlockPos p_393745_, Direction p_393391_) { int i = 0; for (int j = 0; j < p_393137_; j++) { if (!TreeFeature.validTreePos(p_394921_, p_393745_)) { return false; } if (!this.isOverSolidGround(p_394921_, p_393745_)) { if (++i > 2) { return false; } } else { i = 0; } p_393745_.move(p_393391_); } p_393745_.move(p_393391_.getOpposite(), p_393137_); return true; } private void placeFallenLog( FallenTreeConfiguration p_394838_, WorldGenLevel p_396397_, RandomSource p_394699_, int p_396625_, BlockPos.MutableBlockPos p_394037_, Direction p_396491_ ) { Set set = new HashSet<>(); for (int i = 0; i < p_396625_; i++) { set.add(this.placeLogBlock(p_394838_, p_396397_, p_394699_, p_394037_, getSidewaysStateModifier(p_396491_))); p_394037_.move(p_396491_); } this.decorateLogs(p_396397_, p_394699_, set, p_394838_.logDecorators); } private boolean mayPlaceOn(LevelAccessor p_394406_, BlockPos p_392150_) { return TreeFeature.validTreePos(p_394406_, p_392150_) && this.isOverSolidGround(p_394406_, p_392150_); } private boolean isOverSolidGround(LevelAccessor p_395614_, BlockPos p_395166_) { return p_395614_.getBlockState(p_395166_.below()).isFaceSturdy(p_395614_, p_395166_, Direction.UP); } private BlockPos placeLogBlock( FallenTreeConfiguration p_395209_, WorldGenLevel p_394250_, RandomSource p_392111_, BlockPos.MutableBlockPos p_394770_, Function p_397029_ ) { p_394250_.setBlock(p_394770_, p_397029_.apply(p_395209_.trunkProvider.getState(p_392111_, p_394770_)), 3); this.markAboveForPostProcessing(p_394250_, p_394770_); return p_394770_.immutable(); } private void decorateLogs(WorldGenLevel p_395747_, RandomSource p_396895_, Set p_394439_, List p_394258_) { if (!p_394258_.isEmpty()) { TreeDecorator.Context treedecorator$context = new TreeDecorator.Context( p_395747_, this.getDecorationSetter(p_395747_), p_396895_, p_394439_, Set.of(), Set.of() ); p_394258_.forEach(p_393767_ -> p_393767_.place(treedecorator$context)); } } private BiConsumer getDecorationSetter(WorldGenLevel p_392222_) { return (p_391644_, p_394520_) -> p_392222_.setBlock(p_391644_, p_394520_, 19); } private static Function getSidewaysStateModifier(Direction p_397923_) { return p_391565_ -> p_391565_.trySetValue(RotatedPillarBlock.AXIS, p_397923_.getAxis()); } }