    001package net.minecraft.tileentity;
    002
    003import java.util.List;
    004import net.minecraft.block.Block;
    005import net.minecraft.block.BlockChest;
    006import net.minecraft.block.BlockHopper;
    007import net.minecraft.command.IEntitySelector;
    008import net.minecraft.entity.Entity;
    009import net.minecraft.entity.item.EntityItem;
    010import net.minecraft.entity.player.EntityPlayer;
    011import net.minecraft.inventory.IInventory;
    012import net.minecraft.inventory.ISidedInventory;
    013import net.minecraft.item.ItemStack;
    014import net.minecraft.nbt.NBTTagCompound;
    015import net.minecraft.nbt.NBTTagList;
    016import net.minecraft.util.AxisAlignedBB;
    017import net.minecraft.util.Facing;
    018import net.minecraft.util.MathHelper;
    019import net.minecraft.world.World;
    020
    021public class TileEntityHopper extends TileEntity implements Hopper
    022{
    023    private ItemStack[] hopperItemStacks = new ItemStack[5];
    024
    025    /** The name that is displayed if the hopper was renamed */
    026    private String inventoryName;
    027    private int transferCooldown = -1;
    028
    029    /**
    030     * Reads a tile entity from NBT.
    031     */
    032    public void readFromNBT(NBTTagCompound par1NBTTagCompound)
    033    {
    034        super.readFromNBT(par1NBTTagCompound);
    035        NBTTagList nbttaglist = par1NBTTagCompound.getTagList("Items");
    036        this.hopperItemStacks = new ItemStack[this.getSizeInventory()];
    037
    038        if (par1NBTTagCompound.hasKey("CustomName"))
    039        {
    040            this.inventoryName = par1NBTTagCompound.getString("CustomName");
    041        }
    042
    043        this.transferCooldown = par1NBTTagCompound.getInteger("TransferCooldown");
    044
    045        for (int i = 0; i < nbttaglist.tagCount(); ++i)
    046        {
    047            NBTTagCompound nbttagcompound1 = (NBTTagCompound)nbttaglist.tagAt(i);
    048            byte b0 = nbttagcompound1.getByte("Slot");
    049
    050            if (b0 >= 0 && b0 < this.hopperItemStacks.length)
    051            {
    052                this.hopperItemStacks[b0] = ItemStack.loadItemStackFromNBT(nbttagcompound1);
    053            }
    054        }
    055    }
    056
    057    /**
    058     * Writes a tile entity to NBT.
    059     */
    060    public void writeToNBT(NBTTagCompound par1NBTTagCompound)
    061    {
    062        super.writeToNBT(par1NBTTagCompound);
    063        NBTTagList nbttaglist = new NBTTagList();
    064
    065        for (int i = 0; i < this.hopperItemStacks.length; ++i)
    066        {
    067            if (this.hopperItemStacks[i] != null)
    068            {
    069                NBTTagCompound nbttagcompound1 = new NBTTagCompound();
    070                nbttagcompound1.setByte("Slot", (byte)i);
    071                this.hopperItemStacks[i].writeToNBT(nbttagcompound1);
    072                nbttaglist.appendTag(nbttagcompound1);
    073            }
    074        }
    075
    076        par1NBTTagCompound.setTag("Items", nbttaglist);
    077        par1NBTTagCompound.setInteger("TransferCooldown", this.transferCooldown);
    078
    079        if (this.isInvNameLocalized())
    080        {
    081            par1NBTTagCompound.setString("CustomName", this.inventoryName);
    082        }
    083    }
    084
    085    /**
    086     * Called when an the contents of an Inventory change, usually
    087     */
    088    public void onInventoryChanged()
    089    {
    090        super.onInventoryChanged();
    091    }
    092
    093    /**
    094     * Returns the number of slots in the inventory.
    095     */
    096    public int getSizeInventory()
    097    {
    098        return this.hopperItemStacks.length;
    099    }
    100
    101    /**
    102     * Returns the stack in slot i
    103     */
    104    public ItemStack getStackInSlot(int par1)
    105    {
    106        return this.hopperItemStacks[par1];
    107    }
    108
    109    /**
    110     * Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a
    111     * new stack.
    112     */
    113    public ItemStack decrStackSize(int par1, int par2)
    114    {
    115        if (this.hopperItemStacks[par1] != null)
    116        {
    117            ItemStack itemstack;
    118
    119            if (this.hopperItemStacks[par1].stackSize <= par2)
    120            {
    121                itemstack = this.hopperItemStacks[par1];
    122                this.hopperItemStacks[par1] = null;
    123                return itemstack;
    124            }
    125            else
    126            {
    127                itemstack = this.hopperItemStacks[par1].splitStack(par2);
    128
    129                if (this.hopperItemStacks[par1].stackSize == 0)
    130                {
    131                    this.hopperItemStacks[par1] = null;
    132                }
    133
    134                return itemstack;
    135            }
    136        }
    137        else
    138        {
    139            return null;
    140        }
    141    }
    142
    143    /**
    144     * When some containers are closed they call this on each slot, then drop whatever it returns as an EntityItem -
    145     * like when you close a workbench GUI.
    146     */
    147    public ItemStack getStackInSlotOnClosing(int par1)
    148    {
    149        if (this.hopperItemStacks[par1] != null)
    150        {
    151            ItemStack itemstack = this.hopperItemStacks[par1];
    152            this.hopperItemStacks[par1] = null;
    153            return itemstack;
    154        }
    155        else
    156        {
    157            return null;
    158        }
    159    }
    160
    161    /**
    162     * Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections).
    163     */
    164    public void setInventorySlotContents(int par1, ItemStack par2ItemStack)
    165    {
    166        this.hopperItemStacks[par1] = par2ItemStack;
    167
    168        if (par2ItemStack != null && par2ItemStack.stackSize > this.getInventoryStackLimit())
    169        {
    170            par2ItemStack.stackSize = this.getInventoryStackLimit();
    171        }
    172    }
    173
    174    /**
    175     * Returns the name of the inventory.
    176     */
    177    public String getInvName()
    178    {
    179        return this.isInvNameLocalized() ? this.inventoryName : "container.hopper";
    180    }
    181
    182    /**
    183     * If this returns false, the inventory name will be used as an unlocalized name, and translated into the player's
    184     * language. Otherwise it will be used directly.
    185     */
    186    public boolean isInvNameLocalized()
    187    {
    188        return this.inventoryName != null && this.inventoryName.length() > 0;
    189    }
    190
    191    public void func_96115_a(String par1Str)
    192    {
    193        this.inventoryName = par1Str;
    194    }
    195
    196    /**
    197     * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't
    198     * this more of a set than a get?*
    199     */
    200    public int getInventoryStackLimit()
    201    {
    202        return 64;
    203    }
    204
    205    /**
    206     * Do not make give this method the name canInteractWith because it clashes with Container
    207     */
    208    public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer)
    209    {
    210        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;
    211    }
    212
    213    public void openChest() {}
    214
    215    public void closeChest() {}
    216
    217    /**
    218     * Returns true if automation is allowed to insert the given stack (ignoring stack size) into the given slot.
    219     */
    220    public boolean isStackValidForSlot(int par1, ItemStack par2ItemStack)
    221    {
    222        return true;
    223    }
    224
    225    /**
    226     * Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count
    227     * ticks and creates a new spawn inside its implementation.
    228     */
    229    public void updateEntity()
    230    {
    231        if (this.worldObj != null && !this.worldObj.isRemote)
    232        {
    233            --this.transferCooldown;
    234
    235            if (!this.func_98047_l())
    236            {
    237                this.func_98046_c(0);
    238                this.func_98045_j();
    239            }
    240        }
    241    }
    242
    243    public boolean func_98045_j()
    244    {
    245        if (this.worldObj != null && !this.worldObj.isRemote)
    246        {
    247            if (!this.func_98047_l() && BlockHopper.func_94452_d(this.getBlockMetadata()))
    248            {
    249                boolean flag = this.insertItemToInventory() | suckItemsIntoHopper(this);
    250
    251                if (flag)
    252                {
    253                    this.func_98046_c(8);
    254                    this.onInventoryChanged();
    255                    return true;
    256                }
    257            }
    258
    259            return false;
    260        }
    261        else
    262        {
    263            return false;
    264        }
    265    }
    266
    267    /**
    268     * Inserts one item from the hopper into the inventory the hopper is pointing at.
    269     */
    270    private boolean insertItemToInventory()
    271    {
    272        IInventory iinventory = this.getOutputInventory();
    273
    274        if (iinventory == null)
    275        {
    276            return false;
    277        }
    278        else
    279        {
    280            for (int i = 0; i < this.getSizeInventory(); ++i)
    281            {
    282                if (this.getStackInSlot(i) != null)
    283                {
    284                    ItemStack itemstack = this.getStackInSlot(i).copy();
    285                    ItemStack itemstack1 = insertStack(iinventory, this.decrStackSize(i, 1), Facing.oppositeSide[BlockHopper.getDirectionFromMetadata(this.getBlockMetadata())]);
    286
    287                    if (itemstack1 == null || itemstack1.stackSize == 0)
    288                    {
    289                        iinventory.onInventoryChanged();
    290                        return true;
    291                    }
    292
    293                    this.setInventorySlotContents(i, itemstack);
    294                }
    295            }
    296
    297            return false;
    298        }
    299    }
    300
    301    /**
    302     * Sucks one item into the given hopper from an inventory or EntityItem above it.
    303     */
    304    public static boolean suckItemsIntoHopper(Hopper par0Hopper)
    305    {
    306        IInventory iinventory = func_96118_b(par0Hopper);
    307
    308        if (iinventory != null)
    309        {
    310            byte b0 = 0;
    311
    312            if (iinventory instanceof ISidedInventory && b0 > -1)
    313            {
    314                ISidedInventory isidedinventory = (ISidedInventory)iinventory;
    315                int[] aint = isidedinventory.getSizeInventorySide(b0);
    316
    317                for (int i = 0; i < aint.length; ++i)
    318                {
    319                    if (func_102012_a(par0Hopper, iinventory, aint[i], b0))
    320                    {
    321                        return true;
    322                    }
    323                }
    324            }
    325            else
    326            {
    327                int j = iinventory.getSizeInventory();
    328
    329                for (int k = 0; k < j; ++k)
    330                {
    331                    if (func_102012_a(par0Hopper, iinventory, k, b0))
    332                    {
    333                        return true;
    334                    }
    335                }
    336            }
    337        }
    338        else
    339        {
    340            EntityItem entityitem = func_96119_a(par0Hopper.getWorldObj(), par0Hopper.getXPos(), par0Hopper.getYPos() + 1.0D, par0Hopper.getZPos());
    341
    342            if (entityitem != null)
    343            {
    344                return func_96114_a(par0Hopper, entityitem);
    345            }
    346        }
    347
    348        return false;
    349    }
    350
    351    private static boolean func_102012_a(Hopper par0Hopper, IInventory par1IInventory, int par2, int par3)
    352    {
    353        ItemStack itemstack = par1IInventory.getStackInSlot(par2);
    354
    355        if (itemstack != null && func_102013_b(par1IInventory, itemstack, par2, par3))
    356        {
    357            ItemStack itemstack1 = itemstack.copy();
    358            ItemStack itemstack2 = insertStack(par0Hopper, par1IInventory.decrStackSize(par2, 1), -1);
    359
    360            if (itemstack2 == null || itemstack2.stackSize == 0)
    361            {
    362                par1IInventory.onInventoryChanged();
    363                return true;
    364            }
    365
    366            par1IInventory.setInventorySlotContents(par2, itemstack1);
    367        }
    368
    369        return false;
    370    }
    371
    372    public static boolean func_96114_a(IInventory par0IInventory, EntityItem par1EntityItem)
    373    {
    374        boolean flag = false;
    375
    376        if (par1EntityItem == null)
    377        {
    378            return false;
    379        }
    380        else
    381        {
    382            ItemStack itemstack = par1EntityItem.getEntityItem().copy();
    383            ItemStack itemstack1 = insertStack(par0IInventory, itemstack, -1);
    384
    385            if (itemstack1 != null && itemstack1.stackSize != 0)
    386            {
    387                par1EntityItem.setEntityItemStack(itemstack1);
    388            }
    389            else
    390            {
    391                flag = true;
    392                par1EntityItem.setDead();
    393            }
    394
    395            return flag;
    396        }
    397    }
    398
    399    /**
    400     * Inserts a stack into an inventory. Args: Inventory, stack, side. Returns leftover items.
    401     */
    402    public static ItemStack insertStack(IInventory par1IInventory, ItemStack par2ItemStack, int par3)
    403    {
    404        if (par1IInventory instanceof ISidedInventory && par3 > -1)
    405        {
    406            ISidedInventory isidedinventory = (ISidedInventory)par1IInventory;
    407            int[] aint = isidedinventory.getSizeInventorySide(par3);
    408
    409            for (int j = 0; j < aint.length && par2ItemStack != null && par2ItemStack.stackSize > 0; ++j)
    410            {
    411                par2ItemStack = func_102014_c(par1IInventory, par2ItemStack, aint[j], par3);
    412            }
    413        }
    414        else
    415        {
    416            int k = par1IInventory.getSizeInventory();
    417
    418            for (int l = 0; l < k && par2ItemStack != null && par2ItemStack.stackSize > 0; ++l)
    419            {
    420                par2ItemStack = func_102014_c(par1IInventory, par2ItemStack, l, par3);
    421            }
    422        }
    423
    424        if (par2ItemStack != null && par2ItemStack.stackSize == 0)
    425        {
    426            par2ItemStack = null;
    427        }
    428
    429        return par2ItemStack;
    430    }
    431
    432    private static boolean func_102015_a(IInventory par0IInventory, ItemStack par1ItemStack, int par2, int par3)
    433    {
    434        return !par0IInventory.isStackValidForSlot(par2, par1ItemStack) ? false : !(par0IInventory instanceof ISidedInventory) || ((ISidedInventory)par0IInventory).func_102007_a(par2, par1ItemStack, par3);
    435    }
    436
    437    private static boolean func_102013_b(IInventory par0IInventory, ItemStack par1ItemStack, int par2, int par3)
    438    {
    439        return !(par0IInventory instanceof ISidedInventory) || ((ISidedInventory)par0IInventory).func_102008_b(par2, par1ItemStack, par3);
    440    }
    441
    442    private static ItemStack func_102014_c(IInventory par0IInventory, ItemStack par1ItemStack, int par2, int par3)
    443    {
    444        ItemStack itemstack1 = par0IInventory.getStackInSlot(par2);
    445
    446        if (func_102015_a(par0IInventory, par1ItemStack, par2, par3))
    447        {
    448            boolean flag = false;
    449
    450            if (itemstack1 == null)
    451            {
    452                par0IInventory.setInventorySlotContents(par2, par1ItemStack);
    453                par1ItemStack = null;
    454                flag = true;
    455            }
    456            else if (func_94114_a(itemstack1, par1ItemStack))
    457            {
    458                int k = par1ItemStack.getMaxStackSize() - itemstack1.stackSize;
    459                int l = Math.min(par1ItemStack.stackSize, k);
    460                par1ItemStack.stackSize -= l;
    461                itemstack1.stackSize += l;
    462                flag = l > 0;
    463            }
    464
    465            if (flag)
    466            {
    467                if (par0IInventory instanceof TileEntityHopper)
    468                {
    469                    ((TileEntityHopper)par0IInventory).func_98046_c(8);
    470                }
    471
    472                par0IInventory.onInventoryChanged();
    473            }
    474        }
    475
    476        return par1ItemStack;
    477    }
    478
    479    /**
    480     * Gets the inventory the hopper is pointing at.
    481     */
    482    private IInventory getOutputInventory()
    483    {
    484        int i = BlockHopper.getDirectionFromMetadata(this.getBlockMetadata());
    485        return getInventoryAtLocation(this.getWorldObj(), (double)(this.xCoord + Facing.offsetsXForSide[i]), (double)(this.yCoord + Facing.offsetsYForSide[i]), (double)(this.zCoord + Facing.offsetsZForSide[i]));
    486    }
    487
    488    public static IInventory func_96118_b(Hopper par0Hopper)
    489    {
    490        return getInventoryAtLocation(par0Hopper.getWorldObj(), par0Hopper.getXPos(), par0Hopper.getYPos() + 1.0D, par0Hopper.getZPos());
    491    }
    492
    493    public static EntityItem func_96119_a(World par0World, double par1, double par3, double par5)
    494    {
    495        List list = par0World.selectEntitiesWithinAABB(EntityItem.class, AxisAlignedBB.getAABBPool().getAABB(par1, par3, par5, par1 + 1.0D, par3 + 1.0D, par5 + 1.0D), IEntitySelector.selectAnything);
    496        return list.size() > 0 ? (EntityItem)list.get(0) : null;
    497    }
    498
    499    /**
    500     * Gets an inventory at the given location to extract items into or take items from. Can find either a tile entity
    501     * or regular entity implementing IInventory.
    502     */
    503    public static IInventory getInventoryAtLocation(World par0World, double par1, double par3, double par5)
    504    {
    505        IInventory iinventory = null;
    506        int i = MathHelper.floor_double(par1);
    507        int j = MathHelper.floor_double(par3);
    508        int k = MathHelper.floor_double(par5);
    509        TileEntity tileentity = par0World.getBlockTileEntity(i, j, k);
    510
    511        if (tileentity != null && tileentity instanceof IInventory)
    512        {
    513            iinventory = (IInventory)tileentity;
    514
    515            if (iinventory instanceof TileEntityChest)
    516            {
    517                int l = par0World.getBlockId(i, j, k);
    518                Block block = Block.blocksList[l];
    519
    520                if (block instanceof BlockChest)
    521                {
    522                    iinventory = ((BlockChest)block).getInventory(par0World, i, j, k);
    523                }
    524            }
    525        }
    526
    527        if (iinventory == null)
    528        {
    529            List list = par0World.getEntitiesWithinAABBExcludingEntity((Entity)null, AxisAlignedBB.getAABBPool().getAABB(par1, par3, par5, par1 + 1.0D, par3 + 1.0D, par5 + 1.0D), IEntitySelector.selectInventories);
    530
    531            if (list != null && list.size() > 0)
    532            {
    533                iinventory = (IInventory)list.get(par0World.rand.nextInt(list.size()));
    534            }
    535        }
    536
    537        return iinventory;
    538    }
    539
    540    private static boolean func_94114_a(ItemStack par1ItemStack, ItemStack par2ItemStack)
    541    {
    542        return par1ItemStack.itemID != par2ItemStack.itemID ? false : (par1ItemStack.getItemDamage() != par2ItemStack.getItemDamage() ? false : (par1ItemStack.stackSize > par1ItemStack.getMaxStackSize() ? false : ItemStack.areItemStackTagsEqual(par1ItemStack, par2ItemStack)));
    543    }
    544
    545    /**
    546     * Gets the world X position for this hopper entity.
    547     */
    548    public double getXPos()
    549    {
    550        return (double)this.xCoord;
    551    }
    552
    553    /**
    554     * Gets the world Y position for this hopper entity.
    555     */
    556    public double getYPos()
    557    {
    558        return (double)this.yCoord;
    559    }
    560
    561    /**
    562     * Gets the world Z position for this hopper entity.
    563     */
    564    public double getZPos()
    565    {
    566        return (double)this.zCoord;
    567    }
    568
    569    public void func_98046_c(int par1)
    570    {
    571        this.transferCooldown = par1;
    572    }
    573
    574    public boolean func_98047_l()
    575    {
    576        return this.transferCooldown > 0;
    577    }
    578}