package codechicken.lib.inventory; import codechicken.lib.vec.Vector3; import com.google.common.base.Objects; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Items; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.InventoryLargeChest; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityChest; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; public class InventoryUtils { /** * Constructor for ItemStack with tag */ public static ItemStack newItemStack(Item item, int size, int damage, NBTTagCompound tag) { ItemStack stack = new ItemStack(item, size, damage); stack.stackTagCompound = tag; return stack; } /** * Gets the actual damage of an item without asking the Item */ public static int actualDamage(ItemStack stack) { return Items.diamond.getDamage(stack); } /** * Static default implementation for IInventory method */ public static ItemStack decrStackSize(IInventory inv, int slot, int size) { ItemStack item = inv.getStackInSlot(slot); if (item != null) { if (item.stackSize <= size) { inv.setInventorySlotContents(slot, null); inv.markDirty(); return item; } ItemStack itemstack1 = item.splitStack(size); if (item.stackSize == 0) inv.setInventorySlotContents(slot, null); inv.markDirty(); return itemstack1; } return null; } /** * Static default implementation for IInventory method */ public static ItemStack getStackInSlotOnClosing(IInventory inv, int slot) { ItemStack stack = inv.getStackInSlot(slot); inv.setInventorySlotContents(slot, null); return stack; } /** * @return The quantity of items from addition that can be added to base */ public static int incrStackSize(ItemStack base, ItemStack addition) { if (canStack(base, addition)) return incrStackSize(base, addition.stackSize); return 0; } /** * @return The quantity of items from addition that can be added to base */ public static int incrStackSize(ItemStack base, int addition) { int totalSize = base.stackSize + addition; if (totalSize <= base.getMaxStackSize()) return addition; else if (base.stackSize < base.getMaxStackSize()) return base.getMaxStackSize() - base.stackSize; return 0; } /** * NBT item saving function */ public static NBTTagList writeItemStacksToTag(ItemStack[] items) { return writeItemStacksToTag(items, 64); } /** * NBT item saving function with support for stack sizes > 32K */ public static NBTTagList writeItemStacksToTag(ItemStack[] items, int maxQuantity) { NBTTagList tagList = new NBTTagList(); for (int i = 0; i < items.length; i++) { if (items[i] != null) { NBTTagCompound tag = new NBTTagCompound(); tag.setShort("Slot", (short) i); items[i].writeToNBT(tag); if (maxQuantity > Short.MAX_VALUE) tag.setInteger("Quantity", items[i].stackSize); else if (maxQuantity > Byte.MAX_VALUE) tag.setShort("Quantity", (short) items[i].stackSize); tagList.appendTag(tag); } } return tagList; } /** * NBT item loading function with support for stack sizes > 32K */ public static void readItemStacksFromTag(ItemStack[] items, NBTTagList tagList) { for (int i = 0; i < tagList.tagCount(); i++) { NBTTagCompound tag = tagList.getCompoundTagAt(i); int b = tag.getShort("Slot"); items[b] = ItemStack.loadItemStackFromNBT(tag); if (tag.hasKey("Quantity")) items[b].stackSize = ((NBTBase.NBTPrimitive) tag.getTag("Quantity")).func_150287_d(); } } /** * Spawns an itemstack in the world at a location */ public static void dropItem(ItemStack stack, World world, Vector3 dropLocation) { EntityItem item = new EntityItem(world, dropLocation.x, dropLocation.y, dropLocation.z, stack); item.motionX = world.rand.nextGaussian() * 0.05; item.motionY = world.rand.nextGaussian() * 0.05 + 0.2F; item.motionZ = world.rand.nextGaussian() * 0.05; world.spawnEntityInWorld(item); } /** * Copies an itemstack with a new quantity */ public static ItemStack copyStack(ItemStack stack, int quantity) { if (stack == null) return null; stack = stack.copy(); stack.stackSize = quantity; return stack; } /** * Gets the maximum quantity of an item that can be inserted into inv */ public static int getInsertibleQuantity(InventoryRange inv, ItemStack stack) { int quantity = 0; stack = copyStack(stack, Integer.MAX_VALUE); for (int slot : inv.slots) quantity += fitStackInSlot(inv, slot, stack); return quantity; } public static int getInsertibleQuantity(IInventory inv, ItemStack stack) { return getInsertibleQuantity(new InventoryRange(inv), stack); } public static int fitStackInSlot(InventoryRange inv, int slot, ItemStack stack) { ItemStack base = inv.inv.getStackInSlot(slot); if (!canStack(base, stack) || !inv.canInsertItem(slot, stack)) return 0; int fit = base != null ? incrStackSize(base, inv.inv.getInventoryStackLimit() - base.stackSize) : inv.inv.getInventoryStackLimit(); return Math.min(fit, stack.stackSize); } public static int fitStackInSlot(IInventory inv, int slot, ItemStack stack) { return fitStackInSlot(new InventoryRange(inv), slot, stack); } /** * @param simulate If set to true, no items will actually be inserted * @return The number of items unable to be inserted */ public static int insertItem(InventoryRange inv, ItemStack stack, boolean simulate) { stack = stack.copy(); for (int pass = 0; pass < 2; pass++) { for (int slot : inv.slots) { ItemStack base = inv.inv.getStackInSlot(slot); int fit = fitStackInSlot(inv, slot, stack); if (fit == 0) continue; if (base != null) { stack.stackSize -= fit; if (!simulate) { base.stackSize += fit; inv.inv.setInventorySlotContents(slot, base); } } else if (pass == 1) { if (!simulate) inv.inv.setInventorySlotContents(slot, copyStack(stack, fit)); stack.stackSize -= fit; } if (stack.stackSize == 0) return 0; } } return stack.stackSize; } public static int insertItem(IInventory inv, ItemStack stack, boolean simulate) { return insertItem(new InventoryRange(inv), stack, simulate); } /** * Gets the stack in slot if it can be extracted */ public static ItemStack getExtractableStack(InventoryRange inv, int slot) { ItemStack stack = inv.inv.getStackInSlot(slot); if (stack == null || !inv.canExtractItem(slot, stack)) return null; return stack; } public static ItemStack getExtractableStack(IInventory inv, int slot) { return getExtractableStack(new InventoryRange(inv), slot); } public static boolean areStacksIdentical(ItemStack stack1, ItemStack stack2) { if (stack1 == null || stack2 == null) return stack1 == stack2; return stack1.getItem() == stack2.getItem() && stack1.getItemDamage() == stack2.getItemDamage() && stack1.stackSize == stack2.stackSize && Objects.equal(stack1.getTagCompound(), stack2.getTagCompound()); } /** * Gets an IInventory from a coordinate with support for double chests */ public static IInventory getInventory(World world, int x, int y, int z) { TileEntity tile = world.getTileEntity(x, y, z); if (!(tile instanceof IInventory)) return null; if (tile instanceof TileEntityChest) return getChest((TileEntityChest) tile); return (IInventory) tile; } public static final ForgeDirection[] chestSides = new ForgeDirection[]{ForgeDirection.WEST, ForgeDirection.EAST, ForgeDirection.NORTH, ForgeDirection.SOUTH}; public static IInventory getChest(TileEntityChest chest) { for (ForgeDirection fside : chestSides) { if (chest.getWorldObj().getBlock(chest.xCoord + fside.offsetX, chest.yCoord + fside.offsetY, chest.zCoord + fside.offsetZ) == chest.getBlockType()) return new InventoryLargeChest("container.chestDouble", (TileEntityChest) chest.getWorldObj().getTileEntity(chest.xCoord + fside.offsetX, chest.yCoord + fside.offsetY, chest.zCoord + fside.offsetZ), chest); } return chest; } public static boolean canStack(ItemStack stack1, ItemStack stack2) { return stack1 == null || stack2 == null || (stack1.getItem() == stack2.getItem() && (!stack2.getHasSubtypes() || stack2.getItemDamage() == stack1.getItemDamage()) && ItemStack.areItemStackTagsEqual(stack2, stack1)) && stack1.isStackable(); } /** * Consumes one item from slot in inv with support for containers. */ public static void consumeItem(IInventory inv, int slot) { ItemStack stack = inv.getStackInSlot(slot); Item item = stack.getItem(); if (item.hasContainerItem(stack)) { ItemStack container = item.getContainerItem(stack); inv.setInventorySlotContents(slot, container); } else { inv.decrStackSize(slot, 1); } } /** * Gets the size of the stack in a slot. Returns 0 on null stacks */ public static int stackSize(IInventory inv, int slot) { ItemStack stack = inv.getStackInSlot(slot); return stack == null ? 0 : stack.stackSize; } /** * Drops all items from inv using getStackInSlotOnClosing */ public static void dropOnClose(EntityPlayer player, IInventory inv) { for (int i = 0; i < inv.getSizeInventory(); i++) { ItemStack stack = inv.getStackInSlotOnClosing(i); if (stack != null) player.dropPlayerItemWithRandomChoice(stack, false); } } public static NBTTagCompound savePersistant(ItemStack stack, NBTTagCompound tag) { stack.writeToNBT(tag); tag.removeTag("id"); tag.setString("name", Item.itemRegistry.getNameForObject(stack.getItem())); return tag; } public static ItemStack loadPersistant(NBTTagCompound tag) { String name = tag.getString("name"); Item item = (Item) Item.itemRegistry.getObject(name); if(item == null) return null; int count = tag.hasKey("Count") ? tag.getByte("Count") : 1; int damage = tag.hasKey("Damage") ? tag.getShort("Damage") : 0; ItemStack stack = new ItemStack(item, count, damage); if(tag.hasKey("tag", 10)) stack.stackTagCompound = tag.getCompoundTag("tag"); return stack; } }