package de.take_weiland.mods.commons.inv;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterators;
import com.google.common.collect.UnmodifiableIterator;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import de.take_weiland.mods.commons.client.I18n;
import de.take_weiland.mods.commons.util.Sides;
import net.minecraft.block.Block;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Random;
/**
* A collection of static utility methods regarding implementors of {@link IInventory}
*
* @author diesieben07
*/
public final class Inventories {
/**
* <p>Generic implementation for {@link net.minecraft.inventory.IInventory#decrStackSize}.</p>
*
* @param inventory the inventory
* @param slot the slot to be decreased in size
* @param count the number of items to be depleted
* @return the stack being depleted from the inventory
*/
public static ItemStack decreaseStackSize(IInventory inventory, @Nonnegative int slot, @Nonnegative int count) {
ItemStack stack = inventory.getStackInSlot(slot);
if (stack != null) {
ItemStack returnStack;
if (stack.stackSize <= count) {
returnStack = stack;
inventory.setInventorySlotContents(slot, null);
return returnStack;
} else {
returnStack = stack.splitStack(count);
if (stack.stackSize == 0) {
inventory.setInventorySlotContents(slot, null);
} else {
inventory.onInventoryChanged();
}
return returnStack;
}
} else {
return null;
}
}
/**
* <p>Get the display name for the given Inventory. This method may only be called from client code.</p>
* @param inv the inventory
* @return the display name
*/
@SideOnly(Side.CLIENT)
public static String getDisplayName(IInventory inv) {
return inv.isInvNameLocalized() ? inv.getInvName() : I18n.translate(inv.getInvName());
}
/**
* <p>Generic implementation for {@link net.minecraft.inventory.IInventory#getStackInSlotOnClosing}.
* Gets the contents from the given slot and then empties the slot.</p>
*
* @param inventory the inventory
* @param slot the slot to get the contents from
* @return the slots contents
*/
public static ItemStack getAndRemove(IInventory inventory, @Nonnegative int slot) {
ItemStack item = inventory.getStackInSlot(slot);
inventory.setInventorySlotContents(slot, null);
return item;
}
/**
* <p>Calls {@link #spill(net.minecraft.tileentity.TileEntity)} only if the given TileEntity implements {@link IInventory}.</p>
*
* @param te the TileEntity
*/
public static void spillIfInventory(TileEntity te) {
if (te instanceof IInventory) {
spill(te.worldObj, te.xCoord, te.yCoord, te.zCoord, (IInventory) te);
}
}
/**
* <p>Spill the contents of a {@link TileEntity} that also implements {@link IInventory} into the world. Usually used in {@link Block#breakBlock}.</p>
*
* @param te the TileEntity
*/
public static <T extends TileEntity & IInventory> void spill(T te) {
spill(te.worldObj, te.xCoord, te.yCoord, te.zCoord, te);
}
/**
* <p>Spill the contents of the given Inventory at the given coordinates.</p>
*
* @param world the world
* @param x x coordinate
* @param y y coordinate
* @param z z coordinate
* @param inventory the inventory
*/
public static void spill(World world, int x, int y, int z, IInventory inventory) {
if (Sides.logical(world).isServer()) {
Random rand = world.rand;
for (ItemStack stack : iterate(inventory, false)) {
float xRand = rand.nextFloat() * 0.8F + 0.1F;
float yRand = rand.nextFloat() * 0.8F + 0.1F;
float zRand = rand.nextFloat() * 0.8F + 0.1F;
while (stack.stackSize > 0) {
int partialStackSize = Math.min(stack.stackSize, rand.nextInt(21) + 10);
ItemStack stackToSpawn = stack.splitStack(partialStackSize);
EntityItem itemEntity = new EntityItem(world, x + xRand, y + yRand, z + zRand, stackToSpawn);
itemEntity.motionX = rand.nextGaussian() * 0.05F;
itemEntity.motionY = rand.nextGaussian() * 0.05F + 0.2F;
itemEntity.motionZ = rand.nextGaussian() * 0.05F;
world.spawnEntityInWorld(itemEntity);
}
}
}
}
/**
* <p>Write the given inventory to the given key in the NBTTagCompound. The contents can be read with either
* {@link #readInventory(net.minecraft.item.ItemStack[], net.minecraft.nbt.NBTTagCompound, String)} or
* {@link #readInventory(net.minecraft.item.ItemStack[], net.minecraft.nbt.NBTTagList)}.</p>
* @param stacks the inventory
* @param nbt the NBTTagCompound
* @param key the key to write to
*/
public static void writeInventory(ItemStack[] stacks, NBTTagCompound nbt, String key) {
nbt.setTag(key, writeInventory(stacks));
}
/**
* <p>Write the given inventory to an NBTTagList. The contents can be read with either
* {@link #readInventory(net.minecraft.item.ItemStack[], net.minecraft.nbt.NBTTagCompound, String)} or
* {@link #readInventory(net.minecraft.item.ItemStack[], net.minecraft.nbt.NBTTagList)}.</p>
*
* @param stacks the inventory
* @return an NBTTagList
*/
public static NBTTagList writeInventory(ItemStack[] stacks) {
NBTTagList nbt = new NBTTagList();
int len = stacks.length;
for (int i = 0; i < len; i++) {
ItemStack item = stacks[i];
if (item != null) {
NBTTagCompound itemCompound = item.writeToNBT(new NBTTagCompound());
itemCompound.setInteger("slot", i);
nbt.appendTag(itemCompound);
}
}
return nbt;
}
/**
* <p>Read the given inventory from the given key in the NBTTagCompound. The contents must be in the format produced by
* {@link #writeInventory(net.minecraft.item.ItemStack[])} or {@link #writeInventory(net.minecraft.item.ItemStack[], net.minecraft.nbt.NBTTagCompound, String)}.</p>
*
* @param stacks the inventory
* @param nbt the NBTTagCompound
* @param key the key to read from
*/
public static void readInventory(ItemStack[] stacks, NBTTagCompound nbt, String key) {
readInventory(stacks, nbt.getTagList(key));
}
/**
* <p>Read the given inventory from the given NBTTagList. The contents must be in the format produced by {@link #writeInventory(net.minecraft.item.ItemStack[])}
* or {@link #writeInventory(net.minecraft.item.ItemStack[], net.minecraft.nbt.NBTTagCompound, String)}.</p>
*
* @param stacks the inventory
* @param nbtList the NBTTagList
*/
public static void readInventory(ItemStack[] stacks, NBTTagList nbtList) {
int invSize = stacks.length;
int listLen = nbtList.tagCount();
for (int i = 0; i < listLen; i++) {
NBTTagCompound itemCompound = (NBTTagCompound) nbtList.tagAt(i);
ItemStack item = ItemStack.loadItemStackFromNBT(itemCompound);
int idx = itemCompound.getInteger("slot");
if (idx < invSize && idx > 0) {
stacks[idx] = item;
}
}
}
/**
* <p>Create an {@code Iterator} that iterates all slots in the given inventory, in order.</p>
* @param inventory the inventory
* @return an Iterator
*/
public static UnmodifiableIterator<ItemStack> iterator(final IInventory inventory) {
return iterator(inventory, true);
}
/**
* <p>Create an {@code Iterator} that iterates over the given inventory, in order.</p>
*
* @param inventory the inventory
* @param includeNulls whether null (empty slots) should be included in the iterator
* @return an Iterator
*/
@SuppressWarnings("unchecked")
public static UnmodifiableIterator<ItemStack> iterator(IInventory inventory, boolean includeNulls) {
UnmodifiableIterator<ItemStack> it = new InventoryIterator(inventory);
return includeNulls ? it : Iterators.filter(it, Predicates.notNull());
}
/**
* <p>Create an {@code Iterable} that represents the given inventory. The created Iterable uses {@link #iterator(net.minecraft.inventory.IInventory)}
* to create new Iterators.</p>
* @param inventory the inventory
* @return an Iterable
*/
public static FluentIterable<ItemStack> iterate(IInventory inventory) {
return iterate(inventory, true);
}
/**
* <p>Create an {@code Iterable} that represents the given inventory. The created Iterable uses {@link #iterator(net.minecraft.inventory.IInventory, boolean)}
* to create new Iterators.</p>
* @param inventory the inventory
* @param includeNulls whether null (empty slots) should be included in the iterator
* @return an Iterable
*/
public static FluentIterable<ItemStack> iterate(final IInventory inventory, final boolean includeNulls) {
return new FluentIterable<ItemStack>() {
@Nonnull
@Override
public Iterator<ItemStack> iterator() {
return Inventories.iterator(inventory, includeNulls);
}
};
}
private static final class InventoryIterator extends UnmodifiableIterator<ItemStack> {
private final IInventory inv;
private int next;
InventoryIterator(IInventory inv) {
this.inv = inv;
}
@Override
public boolean hasNext() {
return next < inv.getSizeInventory();
}
@Override
public ItemStack next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return inv.getStackInSlot(next++);
}
}
private Inventories() { }
}