From 7ae89ef4e31bd1a1138cab401597fc2efa9ad758 Mon Sep 17 00:00:00 2001 From: grimmauld Date: Thu, 20 May 2021 08:31:35 +0200 Subject: [PATCH] Dynamic trees compat (WIP) --- build.gradle | 2 + .../java/com/simibubi/create/compat/Mods.java | 34 +++++++++++++++ .../compat/dynamictrees/DynamicTree.java | 42 +++++++++++++++++++ .../BlockBreakingMovementBehaviour.java | 11 +++-- .../actors/SawMovementBehaviour.java | 15 +++++++ .../components/saw/SawTileEntity.java | 10 ++++- .../create/foundation/utility/TreeCutter.java | 39 ++++++++++++----- 7 files changed, 139 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/simibubi/create/compat/Mods.java create mode 100644 src/main/java/com/simibubi/create/compat/dynamictrees/DynamicTree.java diff --git a/build.gradle b/build.gradle index c1cdabd3f..fafdf0df7 100644 --- a/build.gradle +++ b/build.gradle @@ -126,6 +126,8 @@ dependencies { runtimeOnly fg.deobf("mezz.jei:jei-1.16.4:${jei_version}") // implementation fg.deobf("curse.maven:druidcraft-340991:3101903") + implementation fg.deobf("curse.maven:dynamictrees-252818:3302576") + // i'll leave this here commented for easier testing //runtimeOnly fg.deobf("vazkii.arl:AutoRegLib:1.4-35.69") diff --git a/src/main/java/com/simibubi/create/compat/Mods.java b/src/main/java/com/simibubi/create/compat/Mods.java new file mode 100644 index 000000000..0fb250033 --- /dev/null +++ b/src/main/java/com/simibubi/create/compat/Mods.java @@ -0,0 +1,34 @@ +package com.simibubi.create.compat; + +import net.minecraftforge.fml.ModList; + +import java.util.Optional; +import java.util.function.Supplier; + +/** + * For compatibility with and without another mod present, we have to define load conditions of the specific code + */ +public enum Mods { + DYNAMICTREES; + + /** + * @return a boolean of whether the mod is loaded or not + */ + public boolean isLoaded() { + return ModList.get().isLoaded(asId()); + } + + public String asId() { + return name().toLowerCase(); + } + + /** + * Simple hook to run code if a mod is installed + * @param toRun will be run if the mod is loaded + */ + public Optional runIfInstalled(Supplier> toRun) { + if (isLoaded()) + return Optional.of(toRun.get().get()); + return Optional.empty(); + } +} diff --git a/src/main/java/com/simibubi/create/compat/dynamictrees/DynamicTree.java b/src/main/java/com/simibubi/create/compat/dynamictrees/DynamicTree.java new file mode 100644 index 000000000..e115b6879 --- /dev/null +++ b/src/main/java/com/simibubi/create/compat/dynamictrees/DynamicTree.java @@ -0,0 +1,42 @@ +package com.simibubi.create.compat.dynamictrees; + +import java.util.function.BiConsumer; + +import javax.annotation.Nullable; + +import com.ferreusveritas.dynamictrees.api.TreeHelper; +import com.ferreusveritas.dynamictrees.blocks.branches.BranchBlock; +import com.ferreusveritas.dynamictrees.util.BranchDestructionData; +import com.simibubi.create.foundation.utility.AbstractBlockBreakQueue; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class DynamicTree extends AbstractBlockBreakQueue { + private final BlockPos startCutPos; + + public DynamicTree(BlockPos startCutPos) { + this.startCutPos = startCutPos; + } + + @Override + public void destroyBlocks(World world, ItemStack toDamage, @Nullable PlayerEntity playerEntity, BiConsumer drop) { + BranchBlock start = TreeHelper.getBranch(world.getBlockState(startCutPos)); + if (start == null) + return; + + BranchDestructionData data = start.destroyBranchFromNode(world, startCutPos, Direction.DOWN, false, playerEntity); + + // Feed all the tree drops to drop bi-consumer + data.leavesDrops.forEach(stackPos -> drop.accept(stackPos.pos.add(startCutPos), stackPos.stack)); + start.getLogDrops(world, startCutPos, data.species, data.woodVolume).forEach(stack -> drop.accept(startCutPos, stack)); + } + + public static boolean isDynamicBranch(Block block) { + return TreeHelper.isBranch(block); + } +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/BlockBreakingMovementBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/BlockBreakingMovementBehaviour.java index 14c090563..419a5a577 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/BlockBreakingMovementBehaviour.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/BlockBreakingMovementBehaviour.java @@ -166,10 +166,10 @@ public class BlockBreakingMovementBehaviour extends MovementBehaviour { float breakSpeed = MathHelper.clamp(Math.abs(context.getAnimationSpeed()) / 500f, 1 / 128f, 16f); destroyProgress += MathHelper.clamp((int) (breakSpeed / blockHardness), 1, 10 - destroyProgress); world.playSound(null, breakingPos, stateToBreak.getSoundType().getHitSound(), SoundCategory.NEUTRAL, .25f, 1); - + if (destroyProgress >= 10) { world.sendBlockBreakProgress(id, breakingPos, -1); - + // break falling blocks from top to bottom BlockPos ogPos = breakingPos; BlockState stateAbove = world.getBlockState(breakingPos.up()); @@ -178,8 +178,9 @@ public class BlockBreakingMovementBehaviour extends MovementBehaviour { stateAbove = world.getBlockState(breakingPos.up()); } stateToBreak = world.getBlockState(breakingPos); - + context.stall = false; + if (shouldDestroyStartBlock(stateToBreak)) BlockHelper.destroyBlock(context.world, breakingPos, 1f, stack -> this.dropItem(context, stack)); onBlockBroken(context, ogPos, stateToBreak); ticksUntilNextProgress = -1; @@ -195,6 +196,10 @@ public class BlockBreakingMovementBehaviour extends MovementBehaviour { data.putInt("Progress", destroyProgress); } + protected boolean shouldDestroyStartBlock(BlockState stateToBreak) { + return true; + } + public boolean canBreak(World world, BlockPos breakingPos, BlockState state) { float blockHardness = state.getBlockHardness(world, breakingPos); return BlockBreakingKineticTileEntity.isBreakable(state, blockHardness); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/SawMovementBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/SawMovementBehaviour.java index e8ec8a692..a24d69459 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/SawMovementBehaviour.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/SawMovementBehaviour.java @@ -5,6 +5,7 @@ import com.simibubi.create.content.contraptions.components.saw.SawBlock; import com.simibubi.create.content.contraptions.components.saw.SawRenderer; import com.simibubi.create.content.contraptions.components.saw.SawTileEntity; import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext; +import com.simibubi.create.foundation.utility.AbstractBlockBreakQueue; import com.simibubi.create.foundation.utility.TreeCutter; import com.simibubi.create.foundation.utility.VecHelper; @@ -22,6 +23,8 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.items.ItemHandlerHelper; +import java.util.Optional; + public class SawMovementBehaviour extends BlockBreakingMovementBehaviour { @Override @@ -57,6 +60,13 @@ public class SawMovementBehaviour extends BlockBreakingMovementBehaviour { protected void onBlockBroken(MovementContext context, BlockPos pos, BlockState brokenState) { if (brokenState.isIn(BlockTags.LEAVES)) return; + + Optional dynamicTree = TreeCutter.findDynamicTree(brokenState.getBlock(), pos); + if (dynamicTree.isPresent()) { + dynamicTree.get().destroyBlocks(context.world, null, (stack, dropPos) -> dropItemFromCutTree(context, stack, dropPos)); + return; + } + TreeCutter.findTree(context.world, pos).destroyBlocks(context.world, null, (stack, dropPos) -> dropItemFromCutTree(context, stack, dropPos)); } @@ -80,6 +90,11 @@ public class SawMovementBehaviour extends BlockBreakingMovementBehaviour { SawRenderer.renderInContraption(context, ms, msLocal, buffer); } + @Override + protected boolean shouldDestroyStartBlock(BlockState stateToBreak) { + return !TreeCutter.canDynamicTreeCutFrom(stateToBreak.getBlock()); + } + @Override protected DamageSource getDamageSource() { return SawBlock.damageSourceSaw; diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawTileEntity.java index f7eca3cc1..1af805fd6 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawTileEntity.java @@ -3,6 +3,7 @@ package com.simibubi.create.content.contraptions.components.saw; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.Random; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -18,6 +19,7 @@ import com.simibubi.create.foundation.item.ItemHelper; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.belt.DirectBeltInputBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour; +import com.simibubi.create.foundation.utility.AbstractBlockBreakQueue; import com.simibubi.create.foundation.utility.TreeCutter; import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.recipe.RecipeConditions; @@ -280,7 +282,7 @@ public class SawTileEntity extends BlockBreakingKineticTileEntity { * RecipeConditions.isOfType(IRecipeType.STONECUTTING, * AllRecipeTypes.CUTTING.getType()) : * RecipeConditions.isOfType(AllRecipeTypes.CUTTING.getType()); - * + * */ Predicate> types = RecipeConditions.isOfType(AllRecipeTypes.CUTTING.getType(), @@ -366,6 +368,12 @@ public class SawTileEntity extends BlockBreakingKineticTileEntity { @Override public void onBlockBroken(BlockState stateToBreak) { + Optional dynamicTree = TreeCutter.findDynamicTree(stateToBreak.getBlock(), breakingPos); + if (dynamicTree.isPresent()) { + dynamicTree.get().destroyBlocks(world, null, this::dropItemFromCutTree); + return; + } + super.onBlockBroken(stateToBreak); TreeCutter.findTree(world, breakingPos).destroyBlocks(world, null, this::dropItemFromCutTree); } diff --git a/src/main/java/com/simibubi/create/foundation/utility/TreeCutter.java b/src/main/java/com/simibubi/create/foundation/utility/TreeCutter.java index d1191082d..e92c788c3 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/TreeCutter.java +++ b/src/main/java/com/simibubi/create/foundation/utility/TreeCutter.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Predicate; @@ -14,6 +15,10 @@ import javax.annotation.Nullable; import com.simibubi.create.AllTags; +import com.simibubi.create.compat.Mods; + +import com.simibubi.create.compat.dynamictrees.DynamicTree; + import net.minecraft.block.BambooBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -36,30 +41,44 @@ import net.minecraft.world.World; public class TreeCutter { public static final Tree NO_TREE = new Tree(Collections.emptyList(), Collections.emptyList()); + public static boolean canDynamicTreeCutFrom(Block startBlock) { + return Mods.DYNAMICTREES.runIfInstalled(() -> () -> DynamicTree.isDynamicBranch(startBlock)).orElse(false); + } + + @Nonnull + public static Optional findDynamicTree(Block startBlock, BlockPos pos) { + if (canDynamicTreeCutFrom(startBlock)) { + return Mods.DYNAMICTREES.runIfInstalled(() -> () -> new DynamicTree(pos)); + } + return Optional.empty(); + } + /** * Finds a tree at the given pos. Block at the position should be air * - * @param reader + * @param world * @param pos * @return null if not found or not fully cut */ @Nonnull - public static Tree findTree(@Nullable IBlockReader reader, BlockPos pos) { - if (reader == null) + public static AbstractBlockBreakQueue findTree(@Nullable IBlockReader world, BlockPos pos) { + if (world == null) return NO_TREE; List logs = new ArrayList<>(); + logs.add(pos); + List leaves = new ArrayList<>(); Set visited = new HashSet<>(); List frontier = new LinkedList<>(); // Bamboo, Sugar Cane, Cactus - BlockState stateAbove = reader.getBlockState(pos.up()); + BlockState stateAbove = world.getBlockState(pos.up()); if (isVerticalPlant(stateAbove)) { logs.add(pos.up()); for (int i = 1; i < 256; i++) { BlockPos current = pos.up(i); - if (!isVerticalPlant(reader.getBlockState(current))) + if (!isVerticalPlant(world.getBlockState(current))) break; logs.add(current); } @@ -78,7 +97,7 @@ public class TreeCutter { BlockPos offset = current.offset(direction); if (visited.contains(offset)) continue; - if (!isChorus(reader.getBlockState(offset))) + if (!isChorus(world.getBlockState(offset))) continue; frontier.add(offset); } @@ -88,7 +107,7 @@ public class TreeCutter { } // Regular Tree - if (!validateCut(reader, pos)) + if (!validateCut(world, pos)) return NO_TREE; visited.add(pos); @@ -102,7 +121,7 @@ public class TreeCutter { continue; visited.add(currentPos); - if (!isLog(reader.getBlockState(currentPos))) + if (!isLog(world.getBlockState(currentPos))) continue; logs.add(currentPos); addNeighbours(currentPos, frontier, visited); @@ -118,7 +137,7 @@ public class TreeCutter { continue; visited.add(currentPos); - BlockState blockState = reader.getBlockState(currentPos); + BlockState blockState = world.getBlockState(currentPos); boolean isLog = isLog(blockState); boolean isLeaf = isLeaf(blockState); boolean isGenericLeaf = isLeaf || isNonDecayingLeaf(blockState); @@ -133,7 +152,7 @@ public class TreeCutter { BlockPos offset = currentPos.offset(direction); if (visited.contains(offset)) continue; - BlockState state = reader.getBlockState(offset); + BlockState state = world.getBlockState(offset); BlockPos subtract = offset.subtract(pos); int horizontalDistance = Math.max(Math.abs(subtract.getX()), Math.abs(subtract.getZ())); if (isLeaf(state) && state.get(LeavesBlock.DISTANCE) > distance