From f19c6ab7635d55f891e5f37899b78d26fb1ee5b3 Mon Sep 17 00:00:00 2001 From: grimmauld Date: Wed, 7 Apr 2021 14:07:06 +0200 Subject: [PATCH] - Deforester now hits forge hooks for tool break, block break, times used. -Moved deforester not loosing durability from leaves to DeforesterItem instead of Tree mining - added destroyBlocks method in TreeCutter$Tree for less doubled code - Mechanical Saws currently break the tree with an empty "hand" as they did before - Silktouch and Fortune enchants are now being applied from deforesters --- .../actors/SawMovementBehaviour.java | 11 +-- .../components/saw/SawTileEntity.java | 15 +--- .../curiosities/tools/DeforesterItem.java | 47 +++++----- .../foundation/utility/BlockHelper.java | 24 ++++- .../create/foundation/utility/TreeCutter.java | 88 ++++++++++--------- 5 files changed, 96 insertions(+), 89 deletions(-) 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 af904fed5..e8ec8a692 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,9 +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.BlockHelper; import com.simibubi.create.foundation.utility.TreeCutter; -import com.simibubi.create.foundation.utility.TreeCutter.Tree; import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.block.BlockState; @@ -59,14 +57,7 @@ public class SawMovementBehaviour extends BlockBreakingMovementBehaviour { protected void onBlockBroken(MovementContext context, BlockPos pos, BlockState brokenState) { if (brokenState.isIn(BlockTags.LEAVES)) return; - Tree tree = TreeCutter.cutTree(context.world, pos); - if (tree != null) { - for (BlockPos log : tree.logs) - BlockHelper.destroyBlock(context.world, log, 1 / 2f, stack -> dropItemFromCutTree(context, log, stack)); - for (BlockPos leaf : tree.leaves) - BlockHelper.destroyBlock(context.world, leaf, 1 / 8f, - stack -> dropItemFromCutTree(context, leaf, stack)); - } + TreeCutter.findTree(context.world, pos).destroyBlocks(context.world, null, (stack, dropPos) -> dropItemFromCutTree(context, stack, dropPos)); } public void dropItemFromCutTree(MovementContext context, BlockPos pos, ItemStack stack) { 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 c6c232b17..8f82fb1d9 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 @@ -1,9 +1,6 @@ package com.simibubi.create.content.contraptions.components.saw; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Random; +import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -18,9 +15,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.BlockHelper; import com.simibubi.create.foundation.utility.TreeCutter; -import com.simibubi.create.foundation.utility.TreeCutter.Tree; import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.recipe.RecipeConditions; import com.simibubi.create.foundation.utility.recipe.RecipeFinder; @@ -360,13 +355,7 @@ public class SawTileEntity extends BlockBreakingKineticTileEntity { @Override public void onBlockBroken(BlockState stateToBreak) { super.onBlockBroken(stateToBreak); - Tree tree = TreeCutter.cutTree(world, breakingPos); - if (tree != null) { - for (BlockPos log : tree.logs) - BlockHelper.destroyBlock(world, log, 1 / 2f, stack -> dropItemFromCutTree(log, stack)); - for (BlockPos leaf : tree.leaves) - BlockHelper.destroyBlock(world, leaf, 1 / 8f, stack -> dropItemFromCutTree(leaf, stack)); - } + TreeCutter.findTree(world, breakingPos).destroyBlocks(world, null, this::dropItemFromCutTree); } public void dropItemFromCutTree(BlockPos pos, ItemStack stack) { diff --git a/src/main/java/com/simibubi/create/content/curiosities/tools/DeforesterItem.java b/src/main/java/com/simibubi/create/content/curiosities/tools/DeforesterItem.java index bcd657bd5..24dbc12bb 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/tools/DeforesterItem.java +++ b/src/main/java/com/simibubi/create/content/curiosities/tools/DeforesterItem.java @@ -2,18 +2,17 @@ package com.simibubi.create.content.curiosities.tools; import com.simibubi.create.AllItems; import com.simibubi.create.AllTags; -import com.simibubi.create.foundation.utility.BlockHelper; import com.simibubi.create.foundation.utility.TreeCutter; -import com.simibubi.create.foundation.utility.TreeCutter.Tree; import com.simibubi.create.foundation.utility.VecHelper; +import mcp.MethodsReturnNonnullByDefault; import net.minecraft.block.BlockState; +import net.minecraft.entity.LivingEntity; import net.minecraft.entity.item.ItemEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.AxeItem; import net.minecraft.item.ItemStack; import net.minecraft.tags.BlockTags; -import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.vector.Vector3d; import net.minecraft.world.IWorld; @@ -23,37 +22,31 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod.EventBusSubscriber; import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; +import javax.annotation.ParametersAreNonnullByDefault; + + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault @EventBusSubscriber(bus = Bus.FORGE) public class DeforesterItem extends AxeItem { + private static boolean deforesting = false; // required as to not run into "recursions" over forge events on tree cutting public DeforesterItem(Properties builder) { super(AllToolTiers.RADIANT, 5.0F, -3.1F, builder); } // Moved away from Item#onBlockDestroyed as it does not get called in Creative - public static void destroyTree(ItemStack stack, IWorld iWorld, BlockState state, BlockPos pos, - PlayerEntity player) { - if (!(state.isIn(BlockTags.LOGS) || AllTags.AllBlockTags.SLIMY_LOGS.matches(state)) || player.isSneaking() || !(iWorld instanceof World)) + public static void destroyTree(IWorld iWorld, BlockState state, BlockPos pos, + PlayerEntity player) { + + if (deforesting ||!(state.isIn(BlockTags.LOGS) || AllTags.AllBlockTags.SLIMY_LOGS.matches(state)) || player.isSneaking() || !(iWorld instanceof World)) return; World worldIn = (World) iWorld; - Tree tree = TreeCutter.cutTree(worldIn, pos); - if (tree == null) - return; - boolean dropBlock = !player.isCreative(); - Vector3d vec = player.getLookVec(); - for (BlockPos log : tree.logs) - BlockHelper.destroyBlock(worldIn, log, 1 / 2f, item -> { - if (dropBlock) { - dropItemFromCutTree(worldIn, pos, vec, log, item); - stack.damageItem(1, player, p -> p.sendBreakAnimation(Hand.MAIN_HAND)); - } - }); - for (BlockPos leaf : tree.leaves) - BlockHelper.destroyBlock(worldIn, leaf, 1 / 8f, item -> { - if (dropBlock) - dropItemFromCutTree(worldIn, pos, vec, leaf, item); - }); + + deforesting = true; + TreeCutter.findTree(worldIn, pos).destroyBlocks(worldIn, player, (dropPos, item) -> dropItemFromCutTree(worldIn, pos, vec, dropPos, item)); + deforesting = false; } @SubscribeEvent @@ -61,7 +54,7 @@ public class DeforesterItem extends AxeItem { ItemStack heldItemMainhand = event.getPlayer().getHeldItemMainhand(); if (!AllItems.DEFORESTER.isIn(heldItemMainhand)) return; - destroyTree(heldItemMainhand, event.getWorld(), event.getState(), event.getPos(), event.getPlayer()); + destroyTree(event.getWorld(), event.getState(), event.getPos(), event.getPlayer()); } public static void dropItemFromCutTree(World world, BlockPos breakingPos, Vector3d fallDirection, BlockPos pos, @@ -73,4 +66,10 @@ public class DeforesterItem extends AxeItem { world.addEntity(entity); } + @Override + public boolean onBlockDestroyed(ItemStack stack, World world, BlockState state, BlockPos pos, LivingEntity entity) { + if (!state.isIn(BlockTags.LEAVES)) + super.onBlockDestroyed(stack, world, state, pos, entity); + return true; + } } diff --git a/src/main/java/com/simibubi/create/foundation/utility/BlockHelper.java b/src/main/java/com/simibubi/create/foundation/utility/BlockHelper.java index badfc7ace..035828332 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/BlockHelper.java +++ b/src/main/java/com/simibubi/create/foundation/utility/BlockHelper.java @@ -4,6 +4,9 @@ import java.util.function.Consumer; import javax.annotation.Nullable; +import net.minecraft.stats.Stats; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.world.BlockEvent; import org.apache.commons.lang3.mutable.MutableInt; import com.simibubi.create.AllBlocks; @@ -184,16 +187,33 @@ public class BlockHelper { public static void destroyBlock(World world, BlockPos pos, float effectChance, Consumer droppedItemCallback) { + destroyBlockAs(world, pos, null, ItemStack.EMPTY, effectChance, droppedItemCallback); + } + public static void destroyBlockAs(World world, BlockPos pos, @Nullable PlayerEntity player, ItemStack usedTool, float effectChance, + Consumer droppedItemCallback) { FluidState fluidState = world.getFluidState(pos); BlockState state = world.getBlockState(pos); if (world.rand.nextFloat() < effectChance) world.playEvent(2001, pos, Block.getStateId(state)); TileEntity tileentity = state.hasTileEntity() ? world.getTileEntity(pos) : null; + if (player != null) { + BlockEvent.BreakEvent event = new BlockEvent.BreakEvent(world, pos, state, player); + MinecraftForge.EVENT_BUS.post(event); + if (event.isCanceled()) + return; + + if (event.getExpToDrop() > 0 && world instanceof ServerWorld) + state.getBlock().dropXpOnBlockBreak((ServerWorld) world, pos, event.getExpToDrop()); + + usedTool.onBlockDestroyed(world, state, pos, player); + player.addStat(Stats.BLOCK_MINED.get(state.getBlock())); + } if (world instanceof ServerWorld && world.getGameRules() - .getBoolean(GameRules.DO_TILE_DROPS) && !world.restoringBlockSnapshots) { - for (ItemStack itemStack : Block.getDrops(state, (ServerWorld) world, pos, tileentity)) + .getBoolean(GameRules.DO_TILE_DROPS) && !world.restoringBlockSnapshots && (player == null || !player.isCreative())) { + for (ItemStack itemStack : Block.getDrops(state, (ServerWorld) world, pos, tileentity, + player, usedTool)) droppedItemCallback.accept(itemStack); state.spawnAdditionalDrops((ServerWorld) world, pos, ItemStack.EMPTY); } 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 58c2fe118..5e7ee1294 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/TreeCutter.java +++ b/src/main/java/com/simibubi/create/foundation/utility/TreeCutter.java @@ -1,51 +1,41 @@ package com.simibubi.create.foundation.utility; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import com.google.common.base.Predicates; +import com.google.common.collect.Iterators; import com.simibubi.create.AllTags; - -import net.minecraft.block.BambooBlock; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; -import net.minecraft.block.CactusBlock; -import net.minecraft.block.ChorusFlowerBlock; -import net.minecraft.block.ChorusPlantBlock; -import net.minecraft.block.KelpBlock; -import net.minecraft.block.KelpTopBlock; -import net.minecraft.block.LeavesBlock; -import net.minecraft.block.SugarCaneBlock; +import net.minecraft.block.*; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; import net.minecraft.tags.BlockTags; import net.minecraft.util.Direction; +import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockReader; +import net.minecraft.world.World; +import net.minecraftforge.event.ForgeEventFactory; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Predicate; public class TreeCutter { + public static final Tree NO_TREE = new Tree(Collections.emptyList(), Collections.emptyList()); - public static class Tree { - public List logs; - public List leaves; - - public Tree(List logs, List leaves) { - this.logs = logs; - this.leaves = leaves; - } - } /** * Finds a tree at the given pos. Block at the position should be air - * + * * @param reader * @param pos * @return null if not found or not fully cut */ - public static Tree cutTree(IBlockReader reader, BlockPos pos) { + @Nonnull + public static Tree findTree(@Nullable IBlockReader reader, BlockPos pos) { + if (reader == null) + return NO_TREE; + List logs = new ArrayList<>(); List leaves = new ArrayList<>(); Set visited = new HashSet<>(); @@ -87,7 +77,7 @@ public class TreeCutter { // Regular Tree if (!validateCut(reader, pos)) - return null; + return NO_TREE; visited.add(pos); BlockPos.getAllInBox(pos.add(-1, 0, -1), pos.add(1, 1, 1)) @@ -112,9 +102,8 @@ public class TreeCutter { frontier.addAll(logs); while (!frontier.isEmpty()) { BlockPos currentPos = frontier.remove(0); - if (!logs.contains(currentPos)) - if (visited.contains(currentPos)) - continue; + if (!logs.contains(currentPos) && visited.contains(currentPos)) + continue; visited.add(currentPos); BlockState blockState = reader.getBlockState(currentPos); @@ -158,15 +147,13 @@ public class TreeCutter { return true; if (block instanceof KelpBlock) return true; - if (block instanceof KelpTopBlock) - return true; - return false; + return block instanceof KelpTopBlock; } /** * Checks whether a tree was fully cut by seeing whether the layer above the cut * is not supported by any more logs. - * + * * @param reader * @param pos * @return @@ -206,7 +193,7 @@ public class TreeCutter { private static void addNeighbours(BlockPos pos, List frontier, Set visited) { BlockPos.getAllInBox(pos.add(-1, -1, -1), pos.add(1, 1, 1)) - .filter(Predicates.not(visited::contains)) + .filter(((Predicate) visited::contains).negate()) .forEach(p -> frontier.add(new BlockPos(p))); } @@ -222,4 +209,25 @@ public class TreeCutter { return state.contains(LeavesBlock.DISTANCE); } + public static class Tree { + private final List logs; + private final List leaves; + + public Tree(List logs, List leaves) { + this.logs = logs; + this.leaves = leaves; + } + + public void destroyBlocks(World world, @Nullable LivingEntity entity, BiConsumer drop) { + PlayerEntity playerEntity = entity instanceof PlayerEntity ? ((PlayerEntity) entity) : null; + ItemStack toDamage = playerEntity != null && !playerEntity.isCreative() ? playerEntity.getHeldItemMainhand() : ItemStack.EMPTY; + Iterators.concat(logs.iterator(), leaves.iterator()).forEachRemaining(pos -> { + ItemStack usedTool = toDamage.copy(); + BlockHelper.destroyBlockAs(world, pos, playerEntity, toDamage, 1 / 2f, stack -> drop.accept(pos, stack)); + if (toDamage.isEmpty() && !usedTool.isEmpty()) + ForgeEventFactory.onPlayerDestroyItem(playerEntity, usedTool, Hand.MAIN_HAND); + }); + } + } + }