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
This commit is contained in:
simibubi 2020-01-25 17:55:42 +01:00
parent b8490038fd
commit 39cbf77457
10 changed files with 380 additions and 42 deletions

View file

@ -7,12 +7,15 @@ import com.simibubi.create.AllPackets;
import com.simibubi.create.foundation.behaviour.base.IBehaviourType; import com.simibubi.create.foundation.behaviour.base.IBehaviourType;
import com.simibubi.create.foundation.behaviour.base.SmartTileEntity; import com.simibubi.create.foundation.behaviour.base.SmartTileEntity;
import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour; 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 com.simibubi.create.modules.logistics.item.filter.FilterItem;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class FilteringBehaviour extends TileEntityBehaviour { public class FilteringBehaviour extends TileEntityBehaviour {
@ -127,6 +130,17 @@ public class FilteringBehaviour extends TileEntityBehaviour {
tileEntity.sendData(); 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() { public ItemStack getFilter() {
return filter.copy(); return filter.copy();
} }

View file

@ -1,5 +1,13 @@
package com.simibubi.create.foundation.utility; 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 class NBTHelper {
public static <T extends Enum<?>> T readEnum(String name, Class<T> enumClass) { public static <T extends Enum<?>> T readEnum(String name, Class<T> enumClass) {
@ -17,4 +25,24 @@ public class NBTHelper {
return enumConstant.name(); return enumConstant.name();
} }
public static <T> ListNBT writeCompoundList(List<T> list, Function<T, CompoundNBT> serializer) {
ListNBT listNBT = new ListNBT();
list.forEach(t -> listNBT.add(serializer.apply(t)));
return listNBT;
}
public static <T> List<T> readCompoundList(ListNBT listNBT, Function<CompoundNBT, T> deserializer) {
List<T> list = new ArrayList<>(listNBT.size());
listNBT.forEach(inbt -> list.add(deserializer.apply((CompoundNBT) inbt)));
return list;
}
public static ListNBT writeItemList(List<ItemStack> stacks) {
return writeCompoundList(stacks, ItemStack::serializeNBT);
}
public static List<ItemStack> readItemList(ListNBT stacks) {
return readCompoundList(stacks, ItemStack::read);
}
} }

View file

@ -1,5 +1,6 @@
package com.simibubi.create.modules.contraptions.components.deployer; 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.block.IWithTileEntity;
import com.simibubi.create.foundation.utility.AllShapes; import com.simibubi.create.foundation.utility.AllShapes;
import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.AngleHelper;
@ -54,10 +55,26 @@ public class DeployerBlock extends DirectionalAxisKineticBlock implements IWithT
} }
return super.onWrenched(state, context); 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 @Override
public boolean onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn, public boolean onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn,
BlockRayTraceResult hit) { BlockRayTraceResult hit) {
ItemStack held = player.getHeldItem(handIn);
if (AllItems.WRENCH.typeOf(held))
return false;
if (hit.getFace() == state.get(FACING)) { if (hit.getFace() == state.get(FACING)) {
if (!worldIn.isRemote) if (!worldIn.isRemote)
withTileEntityDo(worldIn, pos, te -> { withTileEntityDo(worldIn, pos, te -> {
@ -66,6 +83,7 @@ public class DeployerBlock extends DirectionalAxisKineticBlock implements IWithT
return; return;
player.inventory.placeItemBackInInventory(worldIn, heldItemMainhand); player.inventory.placeItemBackInInventory(worldIn, heldItemMainhand);
te.player.setHeldItem(Hand.MAIN_HAND, ItemStack.EMPTY); te.player.setHeldItem(Hand.MAIN_HAND, ItemStack.EMPTY);
te.sendData();
}); });
return true; return true;
} }

View file

@ -4,9 +4,12 @@ import java.util.OptionalInt;
import java.util.UUID; import java.util.UUID;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import com.simibubi.create.foundation.utility.Lang;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener; 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.entity.player.ServerPlayerEntity;
import net.minecraft.inventory.container.INamedContainerProvider; import net.minecraft.inventory.container.INamedContainerProvider;
import net.minecraft.network.IPacket; import net.minecraft.network.IPacket;
@ -14,9 +17,21 @@ import net.minecraft.network.NetworkManager;
import net.minecraft.network.PacketDirection; import net.minecraft.network.PacketDirection;
import net.minecraft.network.play.ServerPlayNetHandler; import net.minecraft.network.play.ServerPlayNetHandler;
import net.minecraft.server.MinecraftServer; 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.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.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 { public class DeployerFakePlayer extends FakePlayer {
private static final NetworkManager NETWORK_MANAGER = new NetworkManager(PacketDirection.CLIENTBOUND); private static final NetworkManager NETWORK_MANAGER = new NetworkManager(PacketDirection.CLIENTBOUND);
@ -33,6 +48,52 @@ public class DeployerFakePlayer extends FakePlayer {
return OptionalInt.empty(); 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 { private static class FakePlayNetHandler extends ServerPlayNetHandler {
public FakePlayNetHandler(MinecraftServer server, ServerPlayerEntity playerIn) { public FakePlayNetHandler(MinecraftServer server, ServerPlayerEntity playerIn) {
super(server, NETWORK_MANAGER, playerIn); super(server, NETWORK_MANAGER, playerIn);

View file

@ -11,6 +11,7 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.Multimap;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTileEntities; import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour; 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.AngleHelper;
import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.WrappedWorld;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity; import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState; 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.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.entity.player.PlayerInventory; 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.BlockItem;
import net.minecraft.item.BlockItemUseContext; 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.ItemStack;
import net.minecraft.item.ItemUseContext; import net.minecraft.item.ItemUseContext;
import net.minecraft.item.Items;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.util.ActionResult;
import net.minecraft.util.ActionResultType; import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.Hand; 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.BlockMode;
import net.minecraft.util.math.RayTraceContext.FluidMode; import net.minecraft.util.math.RayTraceContext.FluidMode;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.ITickList;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld; import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.ForgeHooks; 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.event.entity.player.PlayerInteractEvent.RightClickBlock;
import net.minecraftforge.eventbus.api.Event.Result;
import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper; import net.minecraftforge.items.ItemHandlerHelper;
@ -61,8 +82,10 @@ public class DeployerTileEntity extends KineticTileEntity {
protected DeployerFakePlayer player; protected DeployerFakePlayer player;
protected int timer; protected int timer;
protected float reach; protected float reach;
protected List<ItemStack> overflowItems = new ArrayList<>();
protected Pair<BlockPos, Float> blockBreakingProgress;
private List<ItemStack> overflowItems = new ArrayList<>(); private ListNBT deferredInventoryList;
enum State { enum State {
WAITING, EXPANDING, RETRACTING, DUMPING; WAITING, EXPANDING, RETRACTING, DUMPING;
@ -94,8 +117,15 @@ public class DeployerTileEntity extends KineticTileEntity {
@Override @Override
public void initialize() { public void initialize() {
super.initialize(); super.initialize();
if (!world.isRemote) if (!world.isRemote) {
player = new DeployerFakePlayer((ServerWorld) world); player = new DeployerFakePlayer((ServerWorld) world);
if (deferredInventoryList != null) {
player.inventory.read(deferredInventoryList);
deferredInventoryList = null;
heldItem = player.getHeldItemMainhand();
sendData();
}
}
} }
protected void onExtract(ItemStack stack) { protected void onExtract(ItemStack stack) {
@ -109,7 +139,7 @@ public class DeployerTileEntity extends KineticTileEntity {
} }
protected int getTimerSpeed() { 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 @Override
@ -118,6 +148,12 @@ public class DeployerTileEntity extends KineticTileEntity {
if (getSpeed() == 0) if (getSpeed() == 0)
return; return;
if (!world.isRemote && blockBreakingProgress != null) {
if (world.isAirBlock(blockBreakingProgress.getKey())) {
world.sendBlockBreakProgress(player.getEntityId(), blockBreakingProgress.getKey(), -1);
blockBreakingProgress = null;
}
}
if (timer > 0) { if (timer > 0) {
timer -= getTimerSpeed(); timer -= getTimerSpeed();
return; return;
@ -125,6 +161,7 @@ public class DeployerTileEntity extends KineticTileEntity {
if (world.isRemote) if (world.isRemote)
return; return;
ItemStack stack = player.getHeldItemMainhand();
if (state == State.WAITING) { if (state == State.WAITING) {
if (!overflowItems.isEmpty()) { if (!overflowItems.isEmpty()) {
tryDisposeOfItems(); tryDisposeOfItems();
@ -133,24 +170,34 @@ public class DeployerTileEntity extends KineticTileEntity {
return; return;
} }
if (!filtering.test(player.getHeldItemMainhand())) { if (!filtering.test(stack)) {
if (!player.getHeldItemMainhand().isEmpty()) { if (!stack.isEmpty()) {
overflowItems.add(player.getHeldItemMainhand()); overflowItems.add(stack);
player.setHeldItem(Hand.MAIN_HAND, ItemStack.EMPTY); player.setHeldItem(Hand.MAIN_HAND, ItemStack.EMPTY);
sendData(); sendData();
return; return;
} }
extracting.extract(1); extracting.extract(1);
if (!filtering.test(player.getHeldItemMainhand())) if (!filtering.test(stack))
timer = getTimerSpeed() * 10; timer = getTimerSpeed() * 10;
return; return;
} }
if (filtering.getFilter().isEmpty() && player.getHeldItemMainhand().isEmpty()) if (filtering.getFilter().isEmpty() && stack.isEmpty())
extracting.extract(1); extracting.extract(1);
if (player.getHeldItemMainhand().getItem() instanceof BlockItem) { Direction facing = getBlockState().get(FACING);
if (!world.getBlockState(pos.offset(getBlockState().get(FACING), 2)).getMaterial().isReplaceable()) { 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; timer = getTimerSpeed() * 10;
return; return;
} }
@ -161,7 +208,7 @@ public class DeployerTileEntity extends KineticTileEntity {
Vec3d rayOrigin = VecHelper.getCenterOf(pos).add(movementVector.scale(3 / 2f)); Vec3d rayOrigin = VecHelper.getCenterOf(pos).add(movementVector.scale(3 / 2f));
Vec3d rayTarget = VecHelper.getCenterOf(pos).add(movementVector.scale(5 / 2f)); Vec3d rayTarget = VecHelper.getCenterOf(pos).add(movementVector.scale(5 / 2f));
RayTraceContext rayTraceContext = new RayTraceContext(rayOrigin, rayTarget, BlockMode.OUTLINE, RayTraceContext rayTraceContext = new RayTraceContext(rayOrigin, rayTarget, BlockMode.OUTLINE,
FluidMode.SOURCE_ONLY, player); FluidMode.NONE, player);
BlockRayTraceResult result = world.rayTraceBlocks(rayTraceContext); BlockRayTraceResult result = world.rayTraceBlocks(rayTraceContext);
reach = (float) (.5f + Math.min(result.getHitVec().subtract(rayOrigin).length(), .75f)); reach = (float) (.5f + Math.min(result.getHitVec().subtract(rayOrigin).length(), .75f));
@ -171,7 +218,12 @@ public class DeployerTileEntity extends KineticTileEntity {
} }
if (state == State.EXPANDING) { if (state == State.EXPANDING) {
Multimap<String, AttributeModifier> attributeModifiers = stack
.getAttributeModifiers(EquipmentSlotType.MAINHAND);
player.getAttributes().applyAttributeModifiers(attributeModifiers);
activate(); activate();
player.getAttributes().removeAttributeModifiers(attributeModifiers);
state = State.RETRACTING; state = State.RETRACTING;
timer = 1000; timer = 1000;
sendData(); sendData();
@ -189,57 +241,180 @@ public class DeployerTileEntity extends KineticTileEntity {
} }
protected void activate() { protected void activate() {
// Update player position and angle
Vec3d movementVector = getMovementVector(); Vec3d movementVector = getMovementVector();
Direction direction = getBlockState().get(FACING); 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.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(); ItemStack stack = player.getHeldItemMainhand();
Item item = stack.getItem();
List<LivingEntity> entities = world.getEntitiesWithinAABB(LivingEntity.class, new AxisAlignedBB(clicked)); // Check for entities
World world = this.world;
List<LivingEntity> entities = world.getEntitiesWithinAABB(LivingEntity.class, new AxisAlignedBB(clickedPos));
Hand hand = Hand.MAIN_HAND;
if (!entities.isEmpty()) { 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<ItemEntity> 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; return;
} }
Vec3d rayOrigin = VecHelper.getCenterOf(pos).add(movementVector.scale(3 / 2f + 1 / 64f)); // Right click
Vec3d rayTarget = VecHelper.getCenterOf(pos).add(movementVector.scale(5 / 2f - 1 / 64f)); ItemUseContext itemusecontext = new ItemUseContext(player, hand, result);
RayTraceContext rayTraceContext = new RayTraceContext(rayOrigin, rayTarget, BlockMode.OUTLINE, RightClickBlock event = ForgeHooks.onRightClickBlock(player, hand, clickedPos, direction.getOpposite());
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());
// Item has active use (food for instance)
if (event.getUseItem() != DENY) { if (event.getUseItem() != DENY) {
ActionResultType actionresult = stack.onItemUseFirst(itemusecontext); ActionResultType actionresult = stack.onItemUseFirst(itemusecontext);
if (actionresult != ActionResultType.PASS) if (actionresult != ActionResultType.PASS)
return; 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 holdingSomething = !player.getHeldItemMainhand().isEmpty();
boolean flag1 = !(player.isSneaking() && holdingSomething) boolean flag1 = !(player.isSneaking() && holdingSomething)
|| (stack.doesSneakBypassUse(world, clicked, player)); || (stack.doesSneakBypassUse(world, clickedPos, player));
if (event.getUseBlock() != DENY && flag1 // Use on block
&& clickedState.onBlockActivated(world, player, Hand.MAIN_HAND, result)) if (event.getUseBlock() != DENY && flag1 && clickedState.onBlockActivated(world, player, hand, result))
return; return;
if (stack.isEmpty()) if (stack.isEmpty())
return; return;
if (event.getUseItem() == DENY) if (event.getUseItem() == DENY)
return; return;
if (stack.getItem() instanceof BlockItem if (item instanceof BlockItem && !clickedState.isReplaceable(new BlockItemUseContext(itemusecontext)))
&& !clickedState.isReplaceable(new BlockItemUseContext(itemusecontext)))
return; 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); ActionResultType onItemUse = stack.onItemUse(itemusecontext);
if (onItemUse == ActionResultType.SUCCESS) if (onItemUse == ActionResultType.SUCCESS)
return; 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<Block> getPendingBlockTicks() {
return world.getPendingBlockTicks();
}
@Override
public ITickList<Fluid> getPendingFluidTicks() {
return world.getPendingFluidTicks();
}
};
}
ActionResult<ItemStack> onItemRightClick = item.onItemRightClick(world, player, hand);
player.setHeldItem(hand, onItemRightClick.getResult());
} }
protected void returnAndDeposit() { protected void returnAndDeposit() {
@ -264,8 +439,21 @@ public class DeployerTileEntity extends KineticTileEntity {
} }
protected void tryDisposeOfItems() { protected void tryDisposeOfItems() {
boolean noInv = extracting.getInventories().isEmpty();
for (Iterator<ItemStack> iterator = overflowItems.iterator(); iterator.hasNext();) { for (Iterator<ItemStack> iterator = overflowItems.iterator(); iterator.hasNext();) {
ItemStack itemStack = iterator.next(); 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); itemStack = insert(itemStack, false);
if (itemStack.isEmpty()) if (itemStack.isEmpty())
iterator.remove(); iterator.remove();
@ -292,6 +480,8 @@ public class DeployerTileEntity extends KineticTileEntity {
state = NBTHelper.readEnum(compound.getString("State"), State.class); state = NBTHelper.readEnum(compound.getString("State"), State.class);
mode = NBTHelper.readEnum(compound.getString("Mode"), Mode.class); mode = NBTHelper.readEnum(compound.getString("Mode"), Mode.class);
timer = compound.getInt("Timer"); timer = compound.getInt("Timer");
deferredInventoryList = compound.getList("Inventory", NBT.TAG_COMPOUND);
overflowItems = NBTHelper.readItemList(compound.getList("Overflow", NBT.TAG_COMPOUND));
super.read(compound); super.read(compound);
} }
@ -300,6 +490,12 @@ public class DeployerTileEntity extends KineticTileEntity {
compound.putString("Mode", NBTHelper.writeEnum(mode)); compound.putString("Mode", NBTHelper.writeEnum(mode));
compound.putString("State", NBTHelper.writeEnum(state)); compound.putString("State", NBTHelper.writeEnum(state));
compound.putInt("Timer", timer); 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); return super.write(compound);
} }
@ -326,6 +522,8 @@ public class DeployerTileEntity extends KineticTileEntity {
@Override @Override
public void remove() { public void remove() {
if (!world.isRemote && blockBreakingProgress != null)
world.sendBlockBreakProgress(player.getEntityId(), blockBreakingProgress.getKey(), -1);
super.remove(); super.remove();
player = null; player = null;
} }

View file

@ -52,15 +52,23 @@ public class DeployerTileEntityRenderer extends TileEntityRenderer<DeployerTileE
GlStateManager.translated(offset.x + x, offset.y + y, offset.z + z); GlStateManager.translated(offset.x + x, offset.y + y, offset.z + z);
Direction facing = deployerState.get(FACING); Direction facing = deployerState.get(FACING);
boolean punching = te.mode == Mode.PUNCH;
float yRot = AngleHelper.horizontalAngle(facing) + 180; float yRot = AngleHelper.horizontalAngle(facing) + 180;
float zRot = facing == Direction.UP ? 90 : facing == Direction.DOWN ? 270 : 0; float zRot = facing == Direction.UP ? 90 : facing == Direction.DOWN ? 270 : 0;
GlStateManager.rotatef(yRot, 0, 1, 0); GlStateManager.rotatef(yRot, 0, 1, 0);
GlStateManager.rotatef(zRot, 1, 0, 0); GlStateManager.rotatef(zRot, 1, 0, 0);
GlStateManager.translated(0, 0, -11 / 16f); GlStateManager.translated(0, 0, -11 / 16f);
float scale = .5f;
if (punching) {
GlStateManager.translatef(0, 1/8f, -1/16f);
// GlStateManager.rotatef(punching ? -45 : 0, 1, 0, 0);
}
float scale = punching ? .75f : .5f;
GlStateManager.scaled(scale, scale, scale); GlStateManager.scaled(scale, scale, scale);
TransformType transform = punching ? TransformType.THIRD_PERSON_RIGHT_HAND : TransformType.FIXED;
TransformType transform = te.mode == Mode.PUNCH ? TransformType.FIRST_PERSON_RIGHT_HAND : TransformType.FIXED;
Minecraft.getInstance().getItemRenderer().renderItem(te.heldItem, transform); Minecraft.getInstance().getItemRenderer().renderItem(te.heldItem, transform);
GlStateManager.popMatrix(); GlStateManager.popMatrix();

View file

@ -22,6 +22,7 @@ import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer.Builder; import net.minecraft.state.StateContainer.Builder;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.util.math.shapes.VoxelShape;
@ -180,6 +181,11 @@ public class FunnelBlock extends AttachedLogisticalBlock implements IBeltAttachm
protected boolean isVertical() { protected boolean isVertical() {
return true; return true;
} }
@Override
public ResourceLocation getLootTable() {
return AllBlocks.BELT_FUNNEL.get().getLootTable();
}
} }
} }

View file

@ -67,11 +67,12 @@ public class FlexcrateContainer extends Container {
return ItemStack.EMPTY; return ItemStack.EMPTY;
ItemStack stack = clickedSlot.getStack(); ItemStack stack = clickedSlot.getStack();
if (index < 16) { int crateSize = doubleCrate ? 32 : 16;
mergeItemStack(stack, 16, inventorySlots.size(), false); if (index < crateSize) {
mergeItemStack(stack, crateSize, inventorySlots.size(), false);
te.inventory.onContentsChanged(index); te.inventory.onContentsChanged(index);
} else } else
mergeItemStack(stack, 0, 15, false); mergeItemStack(stack, 0, crateSize - 1, false);
return ItemStack.EMPTY; return ItemStack.EMPTY;
} }

View file

@ -107,11 +107,11 @@ public class FlexcrateTileEntity extends SyncedTileEntity implements INamedConta
public void onDestroyed() { public void onDestroyed() {
FlexcrateTileEntity other = getOtherCrate(); FlexcrateTileEntity other = getOtherCrate();
if (other == null) { if (other == null) {
for (int slot = 0; slot < inventory.getSlots(); slot++) for (int slot = 0; slot < inventory.getSlots(); slot++)
drop(slot); drop(slot);
return; return;
} }
FlexcrateTileEntity main = getMainCrate(); FlexcrateTileEntity main = getMainCrate();
if (this == main) { if (this == main) {
for (int slot = 0; slot < inventory.getSlots(); slot++) { for (int slot = 0; slot < inventory.getSlots(); slot++) {
@ -120,11 +120,14 @@ public class FlexcrateTileEntity extends SyncedTileEntity implements INamedConta
} }
other.allowedAmount = Math.min(1024, allowedAmount); other.allowedAmount = Math.min(1024, allowedAmount);
} }
for (int slot = 16; slot < other.inventory.getSlots(); slot++) for (int slot = 16; slot < other.inventory.getSlots(); slot++)
other.drop(slot); other.drop(slot);
other.invHandler.invalidate();
other.invHandler = LazyOptional.of(() -> other.inventory);
} }
private void drop(int slot) { private void drop(int slot) {
InventoryHelper.spawnItemStack(world, pos.getX(), pos.getY(), pos.getZ(), inventory.getStackInSlot(slot)); InventoryHelper.spawnItemStack(world, pos.getX(), pos.getY(), pos.getZ(), inventory.getStackInSlot(slot));
} }

View file

@ -209,6 +209,7 @@
"death.attack.create.fan_fire": "%1$s was burned to death by hot air", "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.fan_lava": "%1$s was burned to death by lava fan",
"death.attack.create.drill": "%1$s was impaled by Mechanical Drill", "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.crushing": "Crushing",
"create.recipe.splashing": "Bulk Washing", "create.recipe.splashing": "Bulk Washing",