From 39cbf77457d01433be80ccf2030e23c125736a0d Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Sat, 25 Jan 2020 17:55:42 +0100 Subject: [PATCH] Throw, Punch, Fill and Destroy - Deployers can now use buckets properly - Deployers can interact with entities - Deployers can use items to hurt entities and break blocks - Deployers can left-click blocks - Deployers automatically pick up items gathered from interacting with or killing entities - Deployers eject collected items when no Inventory is adjacent - Fixed Deployers throwing projectiles from the wrong height - Fixed blocks not dropping applied filter items - Fixed adjustable crates not invalidating their inventory - Fixed shift-clicking in crate guis - Fixed vertical funnels not dropping when destroyed - Added NBT utility for writing and reading lists --- .../filtering/FilteringBehaviour.java | 14 + .../create/foundation/utility/NBTHelper.java | 28 ++ .../components/deployer/DeployerBlock.java | 18 ++ .../deployer/DeployerFakePlayer.java | 61 ++++ .../deployer/DeployerTileEntity.java | 260 +++++++++++++++--- .../deployer/DeployerTileEntityRenderer.java | 14 +- .../logistics/block/belts/FunnelBlock.java | 6 + .../block/inventories/FlexcrateContainer.java | 7 +- .../inventories/FlexcrateTileEntity.java | 13 +- .../resources/assets/create/lang/en_us.json | 1 + 10 files changed, 380 insertions(+), 42 deletions(-) diff --git a/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringBehaviour.java b/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringBehaviour.java index 1ee10c9f0..d10e3e888 100644 --- a/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringBehaviour.java +++ b/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringBehaviour.java @@ -7,12 +7,15 @@ import com.simibubi.create.AllPackets; import com.simibubi.create.foundation.behaviour.base.IBehaviourType; import com.simibubi.create.foundation.behaviour.base.SmartTileEntity; import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour; +import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.modules.logistics.item.filter.FilterItem; import net.minecraft.block.BlockState; +import net.minecraft.entity.item.ItemEntity; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; public class FilteringBehaviour extends TileEntityBehaviour { @@ -127,6 +130,17 @@ public class FilteringBehaviour extends TileEntityBehaviour { tileEntity.sendData(); } + @Override + public void remove() { + if (filter.getItem() instanceof FilterItem) { + Vec3d pos = VecHelper.getCenterOf(getPos()); + World world = getWorld(); + world.addEntity(new ItemEntity(world, pos.x, pos.y, pos.z, filter.copy())); + } + + super.remove(); + } + public ItemStack getFilter() { return filter.copy(); } diff --git a/src/main/java/com/simibubi/create/foundation/utility/NBTHelper.java b/src/main/java/com/simibubi/create/foundation/utility/NBTHelper.java index caf86265d..4cf167b0f 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/NBTHelper.java +++ b/src/main/java/com/simibubi/create/foundation/utility/NBTHelper.java @@ -1,5 +1,13 @@ package com.simibubi.create.foundation.utility; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; + public class NBTHelper { public static > T readEnum(String name, Class enumClass) { @@ -17,4 +25,24 @@ public class NBTHelper { return enumConstant.name(); } + public static ListNBT writeCompoundList(List list, Function serializer) { + ListNBT listNBT = new ListNBT(); + list.forEach(t -> listNBT.add(serializer.apply(t))); + return listNBT; + } + + public static List readCompoundList(ListNBT listNBT, Function deserializer) { + List list = new ArrayList<>(listNBT.size()); + listNBT.forEach(inbt -> list.add(deserializer.apply((CompoundNBT) inbt))); + return list; + } + + public static ListNBT writeItemList(List stacks) { + return writeCompoundList(stacks, ItemStack::serializeNBT); + } + + public static List readItemList(ListNBT stacks) { + return readCompoundList(stacks, ItemStack::read); + } + } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerBlock.java b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerBlock.java index e0127e46b..14a8f1312 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerBlock.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerBlock.java @@ -1,5 +1,6 @@ package com.simibubi.create.modules.contraptions.components.deployer; +import com.simibubi.create.AllItems; import com.simibubi.create.foundation.block.IWithTileEntity; import com.simibubi.create.foundation.utility.AllShapes; import com.simibubi.create.foundation.utility.AngleHelper; @@ -54,10 +55,26 @@ public class DeployerBlock extends DirectionalAxisKineticBlock implements IWithT } return super.onWrenched(state, context); } + + @Override + public void onReplaced(BlockState state, World worldIn, BlockPos pos, BlockState newState, boolean isMoving) { + if (state.hasTileEntity() && state.getBlock() != newState.getBlock()) { + withTileEntityDo(worldIn, pos, te -> { + te.player.inventory.dropAllItems(); + te.overflowItems.forEach(itemstack -> te.player.dropItem(itemstack, true, false)); + }); + + worldIn.removeTileEntity(pos); + } + } @Override public boolean onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult hit) { + ItemStack held = player.getHeldItem(handIn); + if (AllItems.WRENCH.typeOf(held)) + return false; + if (hit.getFace() == state.get(FACING)) { if (!worldIn.isRemote) withTileEntityDo(worldIn, pos, te -> { @@ -66,6 +83,7 @@ public class DeployerBlock extends DirectionalAxisKineticBlock implements IWithT return; player.inventory.placeItemBackInInventory(worldIn, heldItemMainhand); te.player.setHeldItem(Hand.MAIN_HAND, ItemStack.EMPTY); + te.sendData(); }); return true; } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerFakePlayer.java b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerFakePlayer.java index 45a2cb75c..4fa0985f1 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerFakePlayer.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerFakePlayer.java @@ -4,9 +4,12 @@ import java.util.OptionalInt; import java.util.UUID; import com.mojang.authlib.GameProfile; +import com.simibubi.create.foundation.utility.Lang; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; +import net.minecraft.entity.Entity; +import net.minecraft.entity.Pose; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.container.INamedContainerProvider; import net.minecraft.network.IPacket; @@ -14,9 +17,21 @@ import net.minecraft.network.NetworkManager; import net.minecraft.network.PacketDirection; import net.minecraft.network.play.ServerPlayNetHandler; import net.minecraft.server.MinecraftServer; +import net.minecraft.util.EntityDamageSource; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.event.entity.EntityEvent; +import net.minecraftforge.event.entity.living.LivingDropsEvent; +import net.minecraftforge.event.entity.living.LivingExperienceDropEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +@EventBusSubscriber public class DeployerFakePlayer extends FakePlayer { private static final NetworkManager NETWORK_MANAGER = new NetworkManager(PacketDirection.CLIENTBOUND); @@ -33,6 +48,52 @@ public class DeployerFakePlayer extends FakePlayer { return OptionalInt.empty(); } + @Override + public ITextComponent getDisplayName() { + return new StringTextComponent(Lang.translate("block.deployer.damage_source_name")); + } + + @OnlyIn(Dist.CLIENT) + public float getEyeHeight(Pose poseIn) { + return 0; + } + + @Override + public Vec3d getPositionVector() { + return new Vec3d(posX, posY, posZ); + } + + @Override + public float getCooldownPeriod() { + return 1 / 64f; + } + + @SubscribeEvent + public static void deployerHasEyesOnHisFeet(EntityEvent.EyeHeight event) { + if (event.getEntity() instanceof DeployerFakePlayer) + event.setNewHeight(0); + } + + @SubscribeEvent + public static void deployerCollectsDropsFromKilledEntities(LivingDropsEvent event) { + if (!(event.getSource() instanceof EntityDamageSource)) + return; + EntityDamageSource source = (EntityDamageSource) event.getSource(); + Entity trueSource = source.getTrueSource(); + if (trueSource != null && trueSource instanceof DeployerFakePlayer) { + DeployerFakePlayer fakePlayer = (DeployerFakePlayer) trueSource; + event.getDrops() + .forEach(stack -> fakePlayer.inventory.placeItemBackInInventory(trueSource.world, stack.getItem())); + event.setCanceled(true); + } + } + + @SubscribeEvent + public static void deployerKillsDoNotSpawnXP(LivingExperienceDropEvent event) { + if (event.getAttackingPlayer() instanceof DeployerFakePlayer) + event.setCanceled(true); + } + private static class FakePlayNetHandler extends ServerPlayNetHandler { public FakePlayNetHandler(MinecraftServer server, ServerPlayerEntity playerIn) { super(server, NETWORK_MANAGER, playerIn); diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntity.java index 8d94ece5a..3c164c036 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntity.java @@ -11,6 +11,7 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.Pair; +import com.google.common.collect.Multimap; import com.simibubi.create.AllBlocks; import com.simibubi.create.AllTileEntities; import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour; @@ -21,16 +22,31 @@ import com.simibubi.create.foundation.item.ItemHelper; import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.VecHelper; +import com.simibubi.create.foundation.utility.WrappedWorld; import com.simibubi.create.modules.contraptions.base.KineticTileEntity; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.material.Material; import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.attributes.AttributeModifier; +import net.minecraft.entity.item.ItemEntity; import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.fluid.Fluid; +import net.minecraft.fluid.Fluids; +import net.minecraft.inventory.EquipmentSlotType; import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItemUseContext; +import net.minecraft.item.BucketItem; +import net.minecraft.item.FlintAndSteelItem; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemUseContext; +import net.minecraft.item.Items; import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResultType; import net.minecraft.util.Direction; import net.minecraft.util.Hand; @@ -42,9 +58,14 @@ import net.minecraft.util.math.RayTraceContext; import net.minecraft.util.math.RayTraceContext.BlockMode; import net.minecraft.util.math.RayTraceContext.FluidMode; import net.minecraft.util.math.Vec3d; +import net.minecraft.world.ITickList; +import net.minecraft.world.World; import net.minecraft.world.server.ServerWorld; import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.util.Constants.NBT; +import net.minecraftforge.event.entity.player.PlayerInteractEvent.LeftClickBlock; import net.minecraftforge.event.entity.player.PlayerInteractEvent.RightClickBlock; +import net.minecraftforge.eventbus.api.Event.Result; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemHandlerHelper; @@ -61,8 +82,10 @@ public class DeployerTileEntity extends KineticTileEntity { protected DeployerFakePlayer player; protected int timer; protected float reach; + protected List overflowItems = new ArrayList<>(); + protected Pair blockBreakingProgress; - private List overflowItems = new ArrayList<>(); + private ListNBT deferredInventoryList; enum State { WAITING, EXPANDING, RETRACTING, DUMPING; @@ -94,8 +117,15 @@ public class DeployerTileEntity extends KineticTileEntity { @Override public void initialize() { super.initialize(); - if (!world.isRemote) + if (!world.isRemote) { player = new DeployerFakePlayer((ServerWorld) world); + if (deferredInventoryList != null) { + player.inventory.read(deferredInventoryList); + deferredInventoryList = null; + heldItem = player.getHeldItemMainhand(); + sendData(); + } + } } protected void onExtract(ItemStack stack) { @@ -109,7 +139,7 @@ public class DeployerTileEntity extends KineticTileEntity { } protected int getTimerSpeed() { - return (int) (getSpeed() == 0 ? 0 : MathHelper.clamp(Math.abs(getSpeed()) / 4, 1, 512)); + return (int) (getSpeed() == 0 ? 0 : MathHelper.clamp(Math.abs(getSpeed()) / 2, 8, 512)); } @Override @@ -118,6 +148,12 @@ public class DeployerTileEntity extends KineticTileEntity { if (getSpeed() == 0) return; + if (!world.isRemote && blockBreakingProgress != null) { + if (world.isAirBlock(blockBreakingProgress.getKey())) { + world.sendBlockBreakProgress(player.getEntityId(), blockBreakingProgress.getKey(), -1); + blockBreakingProgress = null; + } + } if (timer > 0) { timer -= getTimerSpeed(); return; @@ -125,6 +161,7 @@ public class DeployerTileEntity extends KineticTileEntity { if (world.isRemote) return; + ItemStack stack = player.getHeldItemMainhand(); if (state == State.WAITING) { if (!overflowItems.isEmpty()) { tryDisposeOfItems(); @@ -133,24 +170,34 @@ public class DeployerTileEntity extends KineticTileEntity { return; } - if (!filtering.test(player.getHeldItemMainhand())) { - if (!player.getHeldItemMainhand().isEmpty()) { - overflowItems.add(player.getHeldItemMainhand()); + if (!filtering.test(stack)) { + if (!stack.isEmpty()) { + overflowItems.add(stack); player.setHeldItem(Hand.MAIN_HAND, ItemStack.EMPTY); sendData(); return; } extracting.extract(1); - if (!filtering.test(player.getHeldItemMainhand())) + if (!filtering.test(stack)) timer = getTimerSpeed() * 10; return; } - if (filtering.getFilter().isEmpty() && player.getHeldItemMainhand().isEmpty()) + if (filtering.getFilter().isEmpty() && stack.isEmpty()) extracting.extract(1); - if (player.getHeldItemMainhand().getItem() instanceof BlockItem) { - if (!world.getBlockState(pos.offset(getBlockState().get(FACING), 2)).getMaterial().isReplaceable()) { + Direction facing = getBlockState().get(FACING); + if (stack.getItem() instanceof BlockItem) { + if (!world.getBlockState(pos.offset(facing, 2)).getMaterial().isReplaceable()) { + timer = getTimerSpeed() * 10; + return; + } + } + + if (stack.getItem() instanceof BucketItem) { + BucketItem bucketItem = (BucketItem) stack.getItem(); + Fluid fluid = bucketItem.getFluid(); + if (fluid != Fluids.EMPTY && world.getFluidState(pos.offset(facing, 2)).getFluid() == fluid) { timer = getTimerSpeed() * 10; return; } @@ -161,7 +208,7 @@ public class DeployerTileEntity extends KineticTileEntity { Vec3d rayOrigin = VecHelper.getCenterOf(pos).add(movementVector.scale(3 / 2f)); Vec3d rayTarget = VecHelper.getCenterOf(pos).add(movementVector.scale(5 / 2f)); RayTraceContext rayTraceContext = new RayTraceContext(rayOrigin, rayTarget, BlockMode.OUTLINE, - FluidMode.SOURCE_ONLY, player); + FluidMode.NONE, player); BlockRayTraceResult result = world.rayTraceBlocks(rayTraceContext); reach = (float) (.5f + Math.min(result.getHitVec().subtract(rayOrigin).length(), .75f)); @@ -171,7 +218,12 @@ public class DeployerTileEntity extends KineticTileEntity { } if (state == State.EXPANDING) { + Multimap attributeModifiers = stack + .getAttributeModifiers(EquipmentSlotType.MAINHAND); + player.getAttributes().applyAttributeModifiers(attributeModifiers); activate(); + player.getAttributes().removeAttributeModifiers(attributeModifiers); + state = State.RETRACTING; timer = 1000; sendData(); @@ -189,57 +241,180 @@ public class DeployerTileEntity extends KineticTileEntity { } protected void activate() { + // Update player position and angle Vec3d movementVector = getMovementVector(); Direction direction = getBlockState().get(FACING); + Vec3d center = VecHelper.getCenterOf(pos); + Vec3d rayOrigin = center.add(movementVector.scale(3 / 2f + 1 / 64f)); + Vec3d rayTarget = center.add(movementVector.scale(5 / 2f - 1 / 64f)); + BlockPos clickedPos = pos.offset(direction, 2); - player.rotationYaw = AngleHelper.horizontalAngle(direction); + player.rotationYaw = AngleHelper.horizontalAngle(direction) + 180; player.rotationPitch = direction == Direction.UP ? -90 : direction == Direction.DOWN ? 90 : 0; + player.setPosition(rayOrigin.x, rayOrigin.y, rayOrigin.z); - BlockPos clicked = pos.offset(direction, 2); ItemStack stack = player.getHeldItemMainhand(); + Item item = stack.getItem(); - List entities = world.getEntitiesWithinAABB(LivingEntity.class, new AxisAlignedBB(clicked)); + // Check for entities + World world = this.world; + List entities = world.getEntitiesWithinAABB(LivingEntity.class, new AxisAlignedBB(clickedPos)); + Hand hand = Hand.MAIN_HAND; if (!entities.isEmpty()) { - stack.interactWithEntity(player, entities.get(world.rand.nextInt(entities.size())), Hand.MAIN_HAND); + LivingEntity entity = entities.get(world.rand.nextInt(entities.size())); + List capturedDrops = new ArrayList<>(); + boolean success = false; + entity.captureDrops(capturedDrops); + + // Use on entity + if (mode == Mode.USE) { + ActionResultType cancelResult = ForgeHooks.onInteractEntity(player, entity, hand); + if (cancelResult == ActionResultType.FAIL) { + entity.captureDrops(null); + return; + } + if (cancelResult == null) { + if (entity.processInitialInteract(player, hand)) + success = true; + else if (stack.interactWithEntity(player, entity, hand)) + success = true; + } + } + + // Punch entity + if (mode == Mode.PUNCH) { + player.resetCooldown(); + player.attackTargetEntityWithCurrentItem(entity); + success = true; + } + + entity.captureDrops(null); + capturedDrops.forEach(e -> player.inventory.placeItemBackInInventory(this.world, e.getItem())); + if (success) + return; + } + + // Shoot ray + RayTraceContext rayTraceContext = new RayTraceContext(rayOrigin, rayTarget, BlockMode.OUTLINE, FluidMode.NONE, + player); + BlockRayTraceResult result = world.rayTraceBlocks(rayTraceContext); + BlockState clickedState = world.getBlockState(clickedPos); + + // Left click + if (mode == Mode.PUNCH) { + LeftClickBlock event = ForgeHooks.onLeftClickBlock(player, clickedPos, direction.getOpposite()); + if (event.isCanceled()) + return; + if (!world.isBlockModifiable(player, clickedPos)) + return; + if (world.extinguishFire(player, clickedPos, direction.getOpposite())) + return; + if (clickedState.isAir(world, clickedPos)) + return; + if (event.getUseBlock() != Result.DENY) + clickedState.onBlockClicked(world, clickedPos, player); + if (stack.isEmpty()) + return; + + float progress = clickedState.getPlayerRelativeBlockHardness(player, world, clickedPos) * 16; + float before = 0; + if (blockBreakingProgress != null) + before = blockBreakingProgress.getValue(); + progress += before; + + if (progress >= 1) { + player.interactionManager.tryHarvestBlock(clickedPos); + world.sendBlockBreakProgress(player.getEntityId(), clickedPos, -1); + blockBreakingProgress = null; + return; + } + + if ((int) (before * 10) != (int) (progress * 10)) + world.sendBlockBreakProgress(player.getEntityId(), clickedPos, (int) (progress * 10)); + blockBreakingProgress = Pair.of(clickedPos, progress); return; } - Vec3d rayOrigin = VecHelper.getCenterOf(pos).add(movementVector.scale(3 / 2f + 1 / 64f)); - Vec3d rayTarget = VecHelper.getCenterOf(pos).add(movementVector.scale(5 / 2f - 1 / 64f)); - RayTraceContext rayTraceContext = new RayTraceContext(rayOrigin, rayTarget, BlockMode.OUTLINE, - FluidMode.SOURCE_ONLY, player); - BlockRayTraceResult result = world.rayTraceBlocks(rayTraceContext); - ItemUseContext itemusecontext = new ItemUseContext(player, Hand.MAIN_HAND, result); - - RightClickBlock event = ForgeHooks.onRightClickBlock(player, Hand.MAIN_HAND, clicked, direction.getOpposite()); + // Right click + ItemUseContext itemusecontext = new ItemUseContext(player, hand, result); + RightClickBlock event = ForgeHooks.onRightClickBlock(player, hand, clickedPos, direction.getOpposite()); + // Item has active use (food for instance) if (event.getUseItem() != DENY) { ActionResultType actionresult = stack.onItemUseFirst(itemusecontext); if (actionresult != ActionResultType.PASS) return; - player.setHeldItem(Hand.MAIN_HAND, stack.onItemUseFinish(world, player)); + player.setHeldItem(hand, stack.onItemUseFinish(world, player)); } - BlockState clickedState = world.getBlockState(clicked); boolean holdingSomething = !player.getHeldItemMainhand().isEmpty(); boolean flag1 = !(player.isSneaking() && holdingSomething) - || (stack.doesSneakBypassUse(world, clicked, player)); + || (stack.doesSneakBypassUse(world, clickedPos, player)); - if (event.getUseBlock() != DENY && flag1 - && clickedState.onBlockActivated(world, player, Hand.MAIN_HAND, result)) + // Use on block + if (event.getUseBlock() != DENY && flag1 && clickedState.onBlockActivated(world, player, hand, result)) return; if (stack.isEmpty()) return; if (event.getUseItem() == DENY) return; - if (stack.getItem() instanceof BlockItem - && !clickedState.isReplaceable(new BlockItemUseContext(itemusecontext))) + if (item instanceof BlockItem && !clickedState.isReplaceable(new BlockItemUseContext(itemusecontext))) return; + // Reposition fire placement for convenience + if (item == Items.FLINT_AND_STEEL) { + Direction newFace = result.getFace(); + BlockPos newPos = result.getPos(); + if (!FlintAndSteelItem.canSetFire(clickedState, world, clickedPos)) + newFace = Direction.UP; + if (clickedState.getMaterial() == Material.AIR) + newPos = newPos.offset(direction); + result = new BlockRayTraceResult(result.getHitVec(), newFace, newPos, result.isInside()); + itemusecontext = new ItemUseContext(player, hand, result); + } + + // 'Inert' item use behaviour & block placement ActionResultType onItemUse = stack.onItemUse(itemusecontext); if (onItemUse == ActionResultType.SUCCESS) return; - stack.getItem().onItemRightClick(world, player, Hand.MAIN_HAND); + + // some items use hard-coded eye positions + if (item == Items.SNOWBALL || item == Items.EGG) + player.posY -= 1.5f; + if (item == Items.ENDER_PEARL) + return; + + // buckets create their own ray, We use a fake wall to contain the active area + if (item instanceof BucketItem) { + world = new WrappedWorld(world) { + + @Override + public BlockState getBlockState(BlockPos position) { + if (pos.offset(direction, 3).equals(position) || pos.offset(direction, 1).equals(position)) + return Blocks.BEDROCK.getDefaultState(); + return world.getBlockState(position); + } + + @Override + public void notifyBlockUpdate(BlockPos pos, BlockState oldState, BlockState newState, int flags) { + world.notifyBlockUpdate(pos, oldState, newState, flags); + } + + @Override + public ITickList getPendingBlockTicks() { + return world.getPendingBlockTicks(); + } + + @Override + public ITickList getPendingFluidTicks() { + return world.getPendingFluidTicks(); + } + + }; + } + + ActionResult onItemRightClick = item.onItemRightClick(world, player, hand); + player.setHeldItem(hand, onItemRightClick.getResult()); } protected void returnAndDeposit() { @@ -264,8 +439,21 @@ public class DeployerTileEntity extends KineticTileEntity { } protected void tryDisposeOfItems() { + boolean noInv = extracting.getInventories().isEmpty(); for (Iterator iterator = overflowItems.iterator(); iterator.hasNext();) { ItemStack itemStack = iterator.next(); + + if (noInv) { + Vec3d offset = getMovementVector(); + Vec3d outPos = VecHelper.getCenterOf(pos).add(offset.scale(-.65f)); + Vec3d motion = offset.scale(-.25f); + ItemEntity e = new ItemEntity(world, outPos.x, outPos.y, outPos.z, itemStack.copy()); + e.setMotion(motion); + world.addEntity(e); + iterator.remove(); + continue; + } + itemStack = insert(itemStack, false); if (itemStack.isEmpty()) iterator.remove(); @@ -292,6 +480,8 @@ public class DeployerTileEntity extends KineticTileEntity { state = NBTHelper.readEnum(compound.getString("State"), State.class); mode = NBTHelper.readEnum(compound.getString("Mode"), Mode.class); timer = compound.getInt("Timer"); + deferredInventoryList = compound.getList("Inventory", NBT.TAG_COMPOUND); + overflowItems = NBTHelper.readItemList(compound.getList("Overflow", NBT.TAG_COMPOUND)); super.read(compound); } @@ -300,6 +490,12 @@ public class DeployerTileEntity extends KineticTileEntity { compound.putString("Mode", NBTHelper.writeEnum(mode)); compound.putString("State", NBTHelper.writeEnum(state)); compound.putInt("Timer", timer); + if (player != null) { + ListNBT invNBT = new ListNBT(); + player.inventory.write(invNBT); + compound.put("Inventory", invNBT); + compound.put("Overflow", NBTHelper.writeItemList(overflowItems)); + } return super.write(compound); } @@ -326,6 +522,8 @@ public class DeployerTileEntity extends KineticTileEntity { @Override public void remove() { + if (!world.isRemote && blockBreakingProgress != null) + world.sendBlockBreakProgress(player.getEntityId(), blockBreakingProgress.getKey(), -1); super.remove(); player = null; } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntityRenderer.java b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntityRenderer.java index a1894caf9..98ac63c5f 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntityRenderer.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntityRenderer.java @@ -52,15 +52,23 @@ public class DeployerTileEntityRenderer extends TileEntityRenderer other.inventory); } - + private void drop(int slot) { InventoryHelper.spawnItemStack(world, pos.getX(), pos.getY(), pos.getZ(), inventory.getStackInSlot(slot)); } diff --git a/src/main/resources/assets/create/lang/en_us.json b/src/main/resources/assets/create/lang/en_us.json index c5c539543..1634b7cc4 100644 --- a/src/main/resources/assets/create/lang/en_us.json +++ b/src/main/resources/assets/create/lang/en_us.json @@ -209,6 +209,7 @@ "death.attack.create.fan_fire": "%1$s was burned to death by hot air", "death.attack.create.fan_lava": "%1$s was burned to death by lava fan", "death.attack.create.drill": "%1$s was impaled by Mechanical Drill", + "create.block.deployer.damage_source_name": "a rogue Deployer", "create.recipe.crushing": "Crushing", "create.recipe.splashing": "Bulk Washing",