package net.minecraft.tileentity;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.block.BlockChest;
import net.minecraft.block.BlockHopper;
import net.minecraft.command.IEntitySelector;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.Facing;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
public class TileEntityHopper extends TileEntity implements Hopper
{
private ItemStack[] hopperItemStacks = new ItemStack[5];
/** The name that is displayed if the hopper was renamed */
private String inventoryName;
private int transferCooldown = -1;
/**
* Reads a tile entity from NBT.
*/
public void readFromNBT(NBTTagCompound par1NBTTagCompound)
{
super.readFromNBT(par1NBTTagCompound);
NBTTagList nbttaglist = par1NBTTagCompound.getTagList("Items");
this.hopperItemStacks = new ItemStack[this.getSizeInventory()];
if (par1NBTTagCompound.hasKey("CustomName"))
{
this.inventoryName = par1NBTTagCompound.getString("CustomName");
}
this.transferCooldown = par1NBTTagCompound.getInteger("TransferCooldown");
for (int i = 0; i < nbttaglist.tagCount(); ++i)
{
NBTTagCompound nbttagcompound1 = (NBTTagCompound)nbttaglist.tagAt(i);
byte b0 = nbttagcompound1.getByte("Slot");
if (b0 >= 0 && b0 < this.hopperItemStacks.length)
{
this.hopperItemStacks[b0] = ItemStack.loadItemStackFromNBT(nbttagcompound1);
}
}
}
/**
* Writes a tile entity to NBT.
*/
public void writeToNBT(NBTTagCompound par1NBTTagCompound)
{
super.writeToNBT(par1NBTTagCompound);
NBTTagList nbttaglist = new NBTTagList();
for (int i = 0; i < this.hopperItemStacks.length; ++i)
{
if (this.hopperItemStacks[i] != null)
{
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
nbttagcompound1.setByte("Slot", (byte)i);
this.hopperItemStacks[i].writeToNBT(nbttagcompound1);
nbttaglist.appendTag(nbttagcompound1);
}
}
par1NBTTagCompound.setTag("Items", nbttaglist);
par1NBTTagCompound.setInteger("TransferCooldown", this.transferCooldown);
if (this.isInvNameLocalized())
{
par1NBTTagCompound.setString("CustomName", this.inventoryName);
}
}
/**
* Called when an the contents of an Inventory change, usually
*/
public void onInventoryChanged()
{
super.onInventoryChanged();
}
/**
* Returns the number of slots in the inventory.
*/
public int getSizeInventory()
{
return this.hopperItemStacks.length;
}
/**
* Returns the stack in slot i
*/
public ItemStack getStackInSlot(int par1)
{
return this.hopperItemStacks[par1];
}
/**
* Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a
* new stack.
*/
public ItemStack decrStackSize(int par1, int par2)
{
if (this.hopperItemStacks[par1] != null)
{
ItemStack itemstack;
if (this.hopperItemStacks[par1].stackSize <= par2)
{
itemstack = this.hopperItemStacks[par1];
this.hopperItemStacks[par1] = null;
return itemstack;
}
else
{
itemstack = this.hopperItemStacks[par1].splitStack(par2);
if (this.hopperItemStacks[par1].stackSize == 0)
{
this.hopperItemStacks[par1] = null;
}
return itemstack;
}
}
else
{
return null;
}
}
/**
* When some containers are closed they call this on each slot, then drop whatever it returns as an EntityItem -
* like when you close a workbench GUI.
*/
public ItemStack getStackInSlotOnClosing(int par1)
{
if (this.hopperItemStacks[par1] != null)
{
ItemStack itemstack = this.hopperItemStacks[par1];
this.hopperItemStacks[par1] = null;
return itemstack;
}
else
{
return null;
}
}
/**
* Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections).
*/
public void setInventorySlotContents(int par1, ItemStack par2ItemStack)
{
this.hopperItemStacks[par1] = par2ItemStack;
if (par2ItemStack != null && par2ItemStack.stackSize > this.getInventoryStackLimit())
{
par2ItemStack.stackSize = this.getInventoryStackLimit();
}
}
/**
* Returns the name of the inventory.
*/
public String getInvName()
{
return this.isInvNameLocalized() ? this.inventoryName : "container.hopper";
}
/**
* If this returns false, the inventory name will be used as an unlocalized name, and translated into the player's
* language. Otherwise it will be used directly.
*/
public boolean isInvNameLocalized()
{
return this.inventoryName != null && this.inventoryName.length() > 0;
}
public void setInventoryName(String par1Str)
{
this.inventoryName = par1Str;
}
/**
* Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't
* this more of a set than a get?*
*/
public int getInventoryStackLimit()
{
return 64;
}
/**
* Do not make give this method the name canInteractWith because it clashes with Container
*/
public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer)
{
return this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord) != this ? false : par1EntityPlayer.getDistanceSq((double)this.xCoord + 0.5D, (double)this.yCoord + 0.5D, (double)this.zCoord + 0.5D) <= 64.0D;
}
public void openChest() {}
public void closeChest() {}
/**
* Returns true if automation is allowed to insert the given stack (ignoring stack size) into the given slot.
*/
public boolean isStackValidForSlot(int par1, ItemStack par2ItemStack)
{
return true;
}
/**
* Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count
* ticks and creates a new spawn inside its implementation.
*/
public void updateEntity()
{
if (this.worldObj != null && !this.worldObj.isRemote)
{
--this.transferCooldown;
if (!this.isCoolingDown())
{
this.setTransferCooldown(0);
this.func_98045_j();
}
}
}
public boolean func_98045_j()
{
if (this.worldObj != null && !this.worldObj.isRemote)
{
if (!this.isCoolingDown() && BlockHopper.getIsBlockNotPoweredFromMetadata(this.getBlockMetadata()))
{
boolean flag = this.insertItemToInventory() | suckItemsIntoHopper(this);
if (flag)
{
this.setTransferCooldown(8);
this.onInventoryChanged();
return true;
}
}
return false;
}
else
{
return false;
}
}
/**
* Inserts one item from the hopper into the inventory the hopper is pointing at.
*/
private boolean insertItemToInventory()
{
IInventory iinventory = this.getOutputInventory();
if (iinventory == null)
{
return false;
}
else
{
for (int i = 0; i < this.getSizeInventory(); ++i)
{
if (this.getStackInSlot(i) != null)
{
ItemStack itemstack = this.getStackInSlot(i).copy();
ItemStack itemstack1 = insertStack(iinventory, this.decrStackSize(i, 1), Facing.oppositeSide[BlockHopper.getDirectionFromMetadata(this.getBlockMetadata())]);
if (itemstack1 == null || itemstack1.stackSize == 0)
{
iinventory.onInventoryChanged();
return true;
}
this.setInventorySlotContents(i, itemstack);
}
}
return false;
}
}
/**
* Sucks one item into the given hopper from an inventory or EntityItem above it.
*/
public static boolean suckItemsIntoHopper(Hopper par0Hopper)
{
IInventory iinventory = getInventoryAboveHopper(par0Hopper);
if (iinventory != null)
{
byte b0 = 0;
if (iinventory instanceof ISidedInventory && b0 > -1)
{
ISidedInventory isidedinventory = (ISidedInventory)iinventory;
int[] aint = isidedinventory.getAccessibleSlotsFromSide(b0);
for (int i = 0; i < aint.length; ++i)
{
if (func_102012_a(par0Hopper, iinventory, aint[i], b0))
{
return true;
}
}
}
else
{
int j = iinventory.getSizeInventory();
for (int k = 0; k < j; ++k)
{
if (func_102012_a(par0Hopper, iinventory, k, b0))
{
return true;
}
}
}
}
else
{
EntityItem entityitem = func_96119_a(par0Hopper.getWorldObj(), par0Hopper.getXPos(), par0Hopper.getYPos() + 1.0D, par0Hopper.getZPos());
if (entityitem != null)
{
return func_96114_a(par0Hopper, entityitem);
}
}
return false;
}
private static boolean func_102012_a(Hopper par0Hopper, IInventory par1IInventory, int par2, int par3)
{
ItemStack itemstack = par1IInventory.getStackInSlot(par2);
if (itemstack != null && canExtractItemFromInventory(par1IInventory, itemstack, par2, par3))
{
ItemStack itemstack1 = itemstack.copy();
ItemStack itemstack2 = insertStack(par0Hopper, par1IInventory.decrStackSize(par2, 1), -1);
if (itemstack2 == null || itemstack2.stackSize == 0)
{
par1IInventory.onInventoryChanged();
return true;
}
par1IInventory.setInventorySlotContents(par2, itemstack1);
}
return false;
}
public static boolean func_96114_a(IInventory par0IInventory, EntityItem par1EntityItem)
{
boolean flag = false;
if (par1EntityItem == null)
{
return false;
}
else
{
ItemStack itemstack = par1EntityItem.getEntityItem().copy();
ItemStack itemstack1 = insertStack(par0IInventory, itemstack, -1);
if (itemstack1 != null && itemstack1.stackSize != 0)
{
par1EntityItem.setEntityItemStack(itemstack1);
}
else
{
flag = true;
par1EntityItem.setDead();
}
return flag;
}
}
/**
* Inserts a stack into an inventory. Args: Inventory, stack, side. Returns leftover items.
*/
public static ItemStack insertStack(IInventory par1IInventory, ItemStack par2ItemStack, int par3)
{
if (par1IInventory instanceof ISidedInventory && par3 > -1)
{
ISidedInventory isidedinventory = (ISidedInventory)par1IInventory;
int[] aint = isidedinventory.getAccessibleSlotsFromSide(par3);
for (int j = 0; j < aint.length && par2ItemStack != null && par2ItemStack.stackSize > 0; ++j)
{
par2ItemStack = func_102014_c(par1IInventory, par2ItemStack, aint[j], par3);
}
}
else
{
int k = par1IInventory.getSizeInventory();
for (int l = 0; l < k && par2ItemStack != null && par2ItemStack.stackSize > 0; ++l)
{
par2ItemStack = func_102014_c(par1IInventory, par2ItemStack, l, par3);
}
}
if (par2ItemStack != null && par2ItemStack.stackSize == 0)
{
par2ItemStack = null;
}
return par2ItemStack;
}
private static boolean func_102015_a(IInventory par0IInventory, ItemStack par1ItemStack, int par2, int par3)
{
return !par0IInventory.isStackValidForSlot(par2, par1ItemStack) ? false : !(par0IInventory instanceof ISidedInventory) || ((ISidedInventory)par0IInventory).canInsertItem(par2, par1ItemStack, par3);
}
private static boolean canExtractItemFromInventory(IInventory par0IInventory, ItemStack par1ItemStack, int par2, int par3)
{
return !(par0IInventory instanceof ISidedInventory) || ((ISidedInventory)par0IInventory).canExtractItem(par2, par1ItemStack, par3);
}
private static ItemStack func_102014_c(IInventory par0IInventory, ItemStack par1ItemStack, int par2, int par3)
{
ItemStack itemstack1 = par0IInventory.getStackInSlot(par2);
if (func_102015_a(par0IInventory, par1ItemStack, par2, par3))
{
boolean flag = false;
if (itemstack1 == null)
{
par0IInventory.setInventorySlotContents(par2, par1ItemStack);
par1ItemStack = null;
flag = true;
}
else if (areItemStacksEqualItem(itemstack1, par1ItemStack))
{
int k = par1ItemStack.getMaxStackSize() - itemstack1.stackSize;
int l = Math.min(par1ItemStack.stackSize, k);
par1ItemStack.stackSize -= l;
itemstack1.stackSize += l;
flag = l > 0;
}
if (flag)
{
if (par0IInventory instanceof TileEntityHopper)
{
((TileEntityHopper)par0IInventory).setTransferCooldown(8);
}
par0IInventory.onInventoryChanged();
}
}
return par1ItemStack;
}
/**
* Gets the inventory the hopper is pointing at.
*/
private IInventory getOutputInventory()
{
int i = BlockHopper.getDirectionFromMetadata(this.getBlockMetadata());
return getInventoryAtLocation(this.getWorldObj(), (double)(this.xCoord + Facing.offsetsXForSide[i]), (double)(this.yCoord + Facing.offsetsYForSide[i]), (double)(this.zCoord + Facing.offsetsZForSide[i]));
}
/**
* Looks for anything, that can hold items (like chests, furnaces, etc.) one block above the given hopper.
*/
public static IInventory getInventoryAboveHopper(Hopper par0Hopper)
{
return getInventoryAtLocation(par0Hopper.getWorldObj(), par0Hopper.getXPos(), par0Hopper.getYPos() + 1.0D, par0Hopper.getZPos());
}
public static EntityItem func_96119_a(World par0World, double par1, double par3, double par5)
{
List list = par0World.selectEntitiesWithinAABB(EntityItem.class, AxisAlignedBB.getAABBPool().getAABB(par1, par3, par5, par1 + 1.0D, par3 + 1.0D, par5 + 1.0D), IEntitySelector.selectAnything);
return list.size() > 0 ? (EntityItem)list.get(0) : null;
}
/**
* Gets an inventory at the given location to extract items into or take items from. Can find either a tile entity
* or regular entity implementing IInventory.
*/
public static IInventory getInventoryAtLocation(World par0World, double par1, double par3, double par5)
{
IInventory iinventory = null;
int i = MathHelper.floor_double(par1);
int j = MathHelper.floor_double(par3);
int k = MathHelper.floor_double(par5);
TileEntity tileentity = par0World.getBlockTileEntity(i, j, k);
if (tileentity != null && tileentity instanceof IInventory)
{
iinventory = (IInventory)tileentity;
if (iinventory instanceof TileEntityChest)
{
int l = par0World.getBlockId(i, j, k);
Block block = Block.blocksList[l];
if (block instanceof BlockChest)
{
iinventory = ((BlockChest)block).getInventory(par0World, i, j, k);
}
}
}
if (iinventory == null)
{
List list = par0World.getEntitiesWithinAABBExcludingEntity((Entity)null, AxisAlignedBB.getAABBPool().getAABB(par1, par3, par5, par1 + 1.0D, par3 + 1.0D, par5 + 1.0D), IEntitySelector.selectInventories);
if (list != null && list.size() > 0)
{
iinventory = (IInventory)list.get(par0World.rand.nextInt(list.size()));
}
}
return iinventory;
}
private static boolean areItemStacksEqualItem(ItemStack par1ItemStack, ItemStack par2ItemStack)
{
return par1ItemStack.itemID != par2ItemStack.itemID ? false : (par1ItemStack.getItemDamage() != par2ItemStack.getItemDamage() ? false : (par1ItemStack.stackSize > par1ItemStack.getMaxStackSize() ? false : ItemStack.areItemStackTagsEqual(par1ItemStack, par2ItemStack)));
}
/**
* Gets the world X position for this hopper entity.
*/
public double getXPos()
{
return (double)this.xCoord;
}
/**
* Gets the world Y position for this hopper entity.
*/
public double getYPos()
{
return (double)this.yCoord;
}
/**
* Gets the world Z position for this hopper entity.
*/
public double getZPos()
{
return (double)this.zCoord;
}
public void setTransferCooldown(int par1)
{
this.transferCooldown = par1;
}
public boolean isCoolingDown()
{
return this.transferCooldown > 0;
}
}