jayhillx

MysticTreeFeature

Nov 5th, 2025
499
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 11.68 KB | None | 0 0
  1. package com.mysticsbiomes.common.levelgen.feature.tree;
  2.  
  3. import com.google.common.collect.Iterables;
  4. import com.google.common.collect.Lists;
  5. import com.google.common.collect.Sets;
  6. import com.mojang.serialization.Codec;
  7. import com.mysticsbiomes.common.levelgen.feature.config.NewMysticTreeConfiguration;
  8. import net.minecraft.core.BlockPos;
  9. import net.minecraft.core.Direction;
  10. import net.minecraft.tags.BlockTags;
  11. import net.minecraft.util.RandomSource;
  12. import net.minecraft.world.level.LevelAccessor;
  13. import net.minecraft.world.level.LevelSimulatedReader;
  14. import net.minecraft.world.level.WorldGenLevel;
  15. import net.minecraft.world.level.block.LeavesBlock;
  16. import net.minecraft.world.level.block.state.BlockState;
  17. import net.minecraft.world.level.block.state.properties.BlockStateProperties;
  18. import net.minecraft.world.level.levelgen.feature.Feature;
  19. import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
  20. import net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecorator;
  21. import net.minecraft.world.level.levelgen.structure.BoundingBox;
  22. import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
  23. import net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape;
  24. import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
  25.  
  26. import java.util.*;
  27. import java.util.function.BiConsumer;
  28.  
  29. public abstract class NewMysticTreeFeature extends Feature<NewMysticTreeConfiguration> {
  30.  
  31.     public NewMysticTreeFeature(Codec<NewMysticTreeConfiguration> codec) {
  32.         super(codec);
  33.     }
  34.  
  35.     @Override
  36.     public boolean place(FeaturePlaceContext<NewMysticTreeConfiguration> context) {
  37.         final WorldGenLevel level = context.level();
  38.         RandomSource random = context.random();
  39.         BlockPos initialPos = context.origin();
  40.         NewMysticTreeConfiguration config = context.config();
  41.  
  42.         Set<BlockPos> trunkPositions = Sets.newHashSet();
  43.         Set<BlockPos> branchPositions = Sets.newHashSet();
  44.         final Set<BlockPos> foliagePositions = Sets.newHashSet();
  45.         Set<BlockPos> decoratorPositions = Sets.newHashSet();
  46.  
  47.         BiConsumer<BlockPos, BlockState> trunkSetter = setter(level, trunkPositions);
  48.         BiConsumer<BlockPos, BlockState> branchSetter = setter(level, branchPositions);
  49.         BiConsumer<BlockPos, BlockState> foliageSetter = setter(level, foliagePositions);
  50.         BiConsumer<BlockPos, BlockState> decoratorSetter = setter(level, decoratorPositions);
  51.  
  52.         boolean flag = this.doPlace(level, random, initialPos, trunkSetter, branchSetter, foliageSetter, config);
  53.         if (flag && (!branchPositions.isEmpty() || !foliagePositions.isEmpty())) {
  54.             if (!config.decorators.isEmpty()) {
  55.                 config.decorators.forEach((decorator) -> decorator.place(new TreeDecorator.Context(level, decoratorSetter, random, branchPositions, foliagePositions, trunkPositions)));
  56.             }
  57.             return BoundingBox.encapsulatingPositions(Iterables.concat(trunkPositions, branchPositions, foliagePositions, decoratorPositions)).map((box) -> {
  58.                 StructureTemplate.updateShapeAtEdge(level, 3, updateLeaves(level, box, branchPositions, decoratorPositions, trunkPositions), box.minX(), box.minY(), box.minZ());
  59.                 return true;
  60.             }).orElse(false);
  61.         } else {
  62.             return false;
  63.         }
  64.     }
  65.  
  66.     private static BiConsumer<BlockPos, BlockState> setter(LevelAccessor level, Set<BlockPos> set) {
  67.         return (pos, state) -> {
  68.             set.add(pos.immutable());
  69.             level.setBlock(pos, state, 19);
  70.         };
  71.     }
  72.  
  73.     public boolean doPlace(WorldGenLevel level, RandomSource random, BlockPos initialPos, BiConsumer<BlockPos, BlockState> trunkSetter, BiConsumer<BlockPos, BlockState> branchSetter, BiConsumer<BlockPos, BlockState> foliageSetter, NewMysticTreeConfiguration config) {
  74.         int trunkHeight = config.trunkShape.getTrunkHeight(random);
  75.         int minBuildY = Math.min(initialPos.getY(), initialPos.getY());
  76.         int maxBuildY = Math.max(initialPos.getY(), initialPos.getY()) + trunkHeight + 1;
  77.  
  78.         if (minBuildY >= level.getMinBuildHeight() + 1 && maxBuildY <= level.getMaxBuildHeight()) {
  79.             for (int currentY = 0; currentY <= trunkHeight; currentY++) {
  80.                 config.trunkShape.placeLog(level, random, initialPos.above(currentY), Direction.Axis.Y, trunkSetter, config);
  81.             }
  82.  
  83.             int foliageRadius = config.foliageShape.radius.sample(random);
  84.             int foliageHeight = config.foliageShape.height.sample(random);
  85.             this.placeFoliage(level, random, initialPos.above(trunkHeight), config, foliageSetter, foliageRadius, foliageHeight);
  86.  
  87.             List<Direction> list = new ArrayList<>();
  88.             for (int count = 0; count < config.branchShape.branchCount.sample(random); count++) {
  89.                 Direction branchDirection = Direction.Plane.HORIZONTAL.getRandomDirection(random);
  90.                 list.add(branchDirection);
  91.  
  92.                 for (Direction directions : list) {
  93.                     if (branchDirection != directions) {
  94.                         int branchYStart = trunkHeight / 2 - config.branchShape.branchYStart.sample(random);
  95.                         this.generateBranch(level, random, initialPos.above(branchYStart), branchDirection, branchSetter, foliageSetter, config);
  96.                     }
  97.                 }
  98.             }
  99.             return true;
  100.         } else {
  101.             return false;
  102.         }
  103.     }
  104.    
  105.     private int getMaxFreeTreeHeight(LevelSimulatedReader level, RandomSource random, BlockPos initialPos, int height, NewMysticTreeConfiguration config) {
  106.         BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
  107.  
  108.         for (int y = 0; y <= height + 1; y++) {
  109.             int radius = config.foliageShape.foliageRadius(random);
  110.  
  111.             for (int xOffset = -radius; xOffset <= radius; xOffset++) {
  112.                 for (int zOffset = -radius; zOffset <= radius; zOffset++) {
  113.                     mutablePos.setWithOffset(initialPos, xOffset, y, zOffset);
  114.  
  115.                     boolean flag = !config.trunkShape.isFree(level, mutablePos);
  116.                     if (flag) {
  117.                         return y - 2;
  118.                     }
  119.                 }
  120.             }
  121.         }
  122.         return height;
  123.     }
  124.  
  125.     private static DiscreteVoxelShape updateLeaves(LevelAccessor level, BoundingBox box, Set<BlockPos> logs, Set<BlockPos> leaves, Set<BlockPos> persistentLeaves) {
  126.         DiscreteVoxelShape shape = new BitSetDiscreteVoxelShape(box.getXSpan(), box.getYSpan(), box.getZSpan());
  127.         List<Set<BlockPos>> list = Lists.newArrayList();
  128.  
  129.         for (int i = 0; i < 7; i++) {
  130.             list.add(Sets.newHashSet());
  131.         }
  132.  
  133.         for (BlockPos pos : Lists.newArrayList(Sets.union(leaves, persistentLeaves))) {
  134.             if (box.isInside(pos)) {
  135.                 shape.fill(pos.getX() - box.minX(), pos.getY() - box.minY(), pos.getZ() - box.minZ());
  136.             }
  137.         }
  138.  
  139.         BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
  140.         int currentDistance = 0;
  141.         list.getFirst().addAll(logs);
  142.  
  143.         while (true) {
  144.             while (currentDistance >= 7 || !list.get(currentDistance).isEmpty()) {
  145.                 if (currentDistance >= 7) return shape;
  146.  
  147.                 Iterator<BlockPos> iterator = list.get(currentDistance).iterator();
  148.                 BlockPos currentPos = iterator.next();
  149.                 iterator.remove();
  150.  
  151.                 if (box.isInside(currentPos)) {
  152.                     if (currentDistance != 0) {
  153.                         BlockState state = level.getBlockState(currentPos);
  154.                         level.setBlock(currentPos, state.setValue(BlockStateProperties.DISTANCE, 1), 19);
  155.                     }
  156.  
  157.                     shape.fill(currentPos.getX() - box.minX(), currentPos.getY() - box.minY(), currentPos.getZ() - box.minZ());
  158.  
  159.                     for (Direction direction : Direction.values()) {
  160.                         mutablePos.setWithOffset(currentPos, direction);
  161.                         if (box.isInside(mutablePos)) {
  162.                             int dx = mutablePos.getX() - box.minX();
  163.                             int dy = mutablePos.getY() - box.minY();
  164.                             int dz = mutablePos.getZ() - box.minZ();
  165.  
  166.                             if (!shape.isFull(dx, dy, dz)) {
  167.                                 OptionalInt distance = LeavesBlock.getOptionalDistanceAt(level.getBlockState(mutablePos));
  168.                                 if (distance.isPresent()) {
  169.                                     int nextDistance = Math.min(distance.getAsInt(), currentDistance + 1);
  170.                                     if (nextDistance < 7) {
  171.                                         list.get(nextDistance).add(mutablePos.immutable());
  172.                                         currentDistance = Math.min(currentDistance, nextDistance);
  173.                                     }
  174.                                 }
  175.                             }
  176.                         }
  177.                     }
  178.                 }
  179.             }
  180.             ++currentDistance;
  181.         }
  182.     }
  183.  
  184.     public static boolean validTreePos(LevelSimulatedReader level, BlockPos pos) {
  185.         return level.isStateAtPosition(pos, state -> state.isAir() || state.is(BlockTags.REPLACEABLE_BY_TREES));
  186.     }
  187.  
  188.     public static boolean isAirOrLeaves(LevelSimulatedReader level, BlockPos pos) {
  189.         return level.isStateAtPosition(pos, state -> state.isAir() || state.is(BlockTags.LEAVES));
  190.     }
  191.  
  192.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  193.  
  194.     protected void generateBranch(LevelAccessor level, RandomSource random, BlockPos startPos, Direction branchDirection, BiConsumer<BlockPos, BlockState> branchSetter, BiConsumer<BlockPos, BlockState> foliageSetter, NewMysticTreeConfiguration config) {
  195.         BlockPos.MutableBlockPos mutablePos = startPos.mutable();
  196.  
  197.         int branchLength = config.branchShape.branchLength.sample(random);
  198.         int extendAmount = config.branchShape.extendAmount.sample(random);
  199.         if (extendAmount > 0) {
  200.             for (int i = 0; i < extendAmount; i++) {
  201.                 mutablePos.move(branchDirection);
  202.                 config.trunkShape.placeLog(level, random, mutablePos, branchDirection.getAxis(), branchSetter, config);
  203.             }
  204.         }
  205.  
  206.         int steps = 0;
  207.         while (steps <= branchLength) {
  208.             int forwardInterval = 1 + Math.round((float) steps / branchLength * (config.branchShape.forwardInterval + 1));
  209.             int upwardInterval = 1 + Math.round((float) steps / branchLength * (config.branchShape.upwardInterval + 1));
  210.  
  211.             for (int i = 0; i < forwardInterval && steps < branchLength; i++, steps++) {
  212.                 mutablePos.move(branchDirection);
  213.                 config.trunkShape.placeLog(level, random, mutablePos, branchDirection.getAxis(), branchSetter, config);
  214.             }
  215.  
  216.             for (int i = 0; i < upwardInterval && steps < branchLength; i++, steps++) {
  217.                 mutablePos.move(Direction.UP);
  218.                 config.trunkShape.placeLog(level, random, mutablePos, Direction.Axis.Y, branchSetter, config);
  219.             }
  220.  
  221.             if (steps == branchLength) {
  222.                 this.placeFoliage(level, random, mutablePos, config, foliageSetter, config.foliageShape.radius.sample(random), config.foliageShape.height.sample(random));
  223.                 return;
  224.             }
  225.         }
  226.     }
  227.  
  228.     public abstract void placeFoliage(LevelAccessor level, RandomSource random, BlockPos pos, NewMysticTreeConfiguration config, BiConsumer<BlockPos, BlockState> foliageSetter, int foliageRadius, int foliageHeight);
  229.  
  230. }
Advertisement
Add Comment
Please, Sign In to add comment