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; } }