- 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
This commit is contained in:
grimmauld 2021-04-07 14:07:06 +02:00
parent c46e286844
commit f19c6ab763
5 changed files with 96 additions and 89 deletions

View file

@ -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) {

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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<ItemStack> 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<ItemStack> 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);
}

View file

@ -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<BlockPos> logs;
public List<BlockPos> leaves;
public Tree(List<BlockPos> logs, List<BlockPos> 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<BlockPos> logs = new ArrayList<>();
List<BlockPos> leaves = new ArrayList<>();
Set<BlockPos> 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<BlockPos> frontier, Set<BlockPos> visited) {
BlockPos.getAllInBox(pos.add(-1, -1, -1), pos.add(1, 1, 1))
.filter(Predicates.not(visited::contains))
.filter(((Predicate<BlockPos>) 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<BlockPos> logs;
private final List<BlockPos> leaves;
public Tree(List<BlockPos> logs, List<BlockPos> leaves) {
this.logs = logs;
this.leaves = leaves;
}
public void destroyBlocks(World world, @Nullable LivingEntity entity, BiConsumer<BlockPos, ItemStack> 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);
});
}
}
}