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[] field_94124_b = new ItemStack[5];
024
025    /** The name that is displayed if the hopper was renamed */
026    private String inventoryName;
027    private int field_98048_c = -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.field_94124_b = new ItemStack[this.getSizeInventory()];
037
038        if (par1NBTTagCompound.hasKey("CustomName"))
039        {
040            this.inventoryName = par1NBTTagCompound.getString("CustomName");
041        }
042
043        this.field_98048_c = 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.field_94124_b.length)
051            {
052                this.field_94124_b[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.field_94124_b.length; ++i)
066        {
067            if (this.field_94124_b[i] != null)
068            {
069                NBTTagCompound nbttagcompound1 = new NBTTagCompound();
070                nbttagcompound1.setByte("Slot", (byte)i);
071                this.field_94124_b[i].writeToNBT(nbttagcompound1);
072                nbttaglist.appendTag(nbttagcompound1);
073            }
074        }
075
076        par1NBTTagCompound.setTag("Items", nbttaglist);
077        par1NBTTagCompound.setInteger("TransferCooldown", this.field_98048_c);
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.field_94124_b.length;
099    }
100
101    /**
102     * Returns the stack in slot i
103     */
104    public ItemStack getStackInSlot(int par1)
105    {
106        return this.field_94124_b[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.field_94124_b[par1] != null)
116        {
117            ItemStack itemstack;
118
119            if (this.field_94124_b[par1].stackSize <= par2)
120            {
121                itemstack = this.field_94124_b[par1];
122                this.field_94124_b[par1] = null;
123                return itemstack;
124            }
125            else
126            {
127                itemstack = this.field_94124_b[par1].splitStack(par2);
128
129                if (this.field_94124_b[par1].stackSize == 0)
130                {
131                    this.field_94124_b[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.field_94124_b[par1] != null)
150        {
151            ItemStack itemstack = this.field_94124_b[par1];
152            this.field_94124_b[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.field_94124_b[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.field_98048_c;
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.func_94116_j() | func_96116_a(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    private boolean func_94116_j()
268    {
269        IInventory iinventory = this.func_94119_v();
270
271        if (iinventory == null)
272        {
273            return false;
274        }
275        else
276        {
277            for (int i = 0; i < this.getSizeInventory(); ++i)
278            {
279                if (this.getStackInSlot(i) != null)
280                {
281                    ItemStack itemstack = this.getStackInSlot(i).copy();
282                    ItemStack itemstack1 = func_94117_a(iinventory, this.decrStackSize(i, 1), Facing.faceToSide[BlockHopper.func_94451_c(this.getBlockMetadata())]);
283
284                    if (itemstack1 == null || itemstack1.stackSize == 0)
285                    {
286                        iinventory.onInventoryChanged();
287                        return true;
288                    }
289
290                    this.setInventorySlotContents(i, itemstack);
291                }
292            }
293
294            return false;
295        }
296    }
297
298    public static boolean func_96116_a(Hopper par0Hopper)
299    {
300        IInventory iinventory = func_96118_b(par0Hopper);
301
302        if (iinventory != null)
303        {
304            byte b0 = 0;
305
306            if (iinventory instanceof ISidedInventory && b0 > -1)
307            {
308                ISidedInventory isidedinventory = (ISidedInventory)iinventory;
309                int[] aint = isidedinventory.getSizeInventorySide(b0);
310
311                for (int i = 0; i < aint.length; ++i)
312                {
313                    if (func_102012_a(par0Hopper, iinventory, aint[i], b0))
314                    {
315                        return true;
316                    }
317                }
318            }
319            else
320            {
321                int j = iinventory.getSizeInventory();
322
323                for (int k = 0; k < j; ++k)
324                {
325                    if (func_102012_a(par0Hopper, iinventory, k, b0))
326                    {
327                        return true;
328                    }
329                }
330            }
331        }
332        else
333        {
334            EntityItem entityitem = func_96119_a(par0Hopper.getWorldObj(), par0Hopper.func_96107_aA(), par0Hopper.func_96109_aB() + 1.0D, par0Hopper.func_96108_aC());
335
336            if (entityitem != null)
337            {
338                return func_96114_a(par0Hopper, entityitem);
339            }
340        }
341
342        return false;
343    }
344
345    private static boolean func_102012_a(Hopper par0Hopper, IInventory par1IInventory, int par2, int par3)
346    {
347        ItemStack itemstack = par1IInventory.getStackInSlot(par2);
348
349        if (itemstack != null && func_102013_b(par1IInventory, itemstack, par2, par3))
350        {
351            ItemStack itemstack1 = itemstack.copy();
352            ItemStack itemstack2 = func_94117_a(par0Hopper, par1IInventory.decrStackSize(par2, 1), -1);
353
354            if (itemstack2 == null || itemstack2.stackSize == 0)
355            {
356                par1IInventory.onInventoryChanged();
357                return true;
358            }
359
360            par1IInventory.setInventorySlotContents(par2, itemstack1);
361        }
362
363        return false;
364    }
365
366    public static boolean func_96114_a(IInventory par0IInventory, EntityItem par1EntityItem)
367    {
368        boolean flag = false;
369
370        if (par1EntityItem == null)
371        {
372            return false;
373        }
374        else
375        {
376            ItemStack itemstack = par1EntityItem.getEntityItem().copy();
377            ItemStack itemstack1 = func_94117_a(par0IInventory, itemstack, -1);
378
379            if (itemstack1 != null && itemstack1.stackSize != 0)
380            {
381                par1EntityItem.setEntityItemStack(itemstack1);
382            }
383            else
384            {
385                flag = true;
386                par1EntityItem.setDead();
387            }
388
389            return flag;
390        }
391    }
392
393    public static ItemStack func_94117_a(IInventory par1IInventory, ItemStack par2ItemStack, int par3)
394    {
395        if (par1IInventory instanceof ISidedInventory && par3 > -1)
396        {
397            ISidedInventory isidedinventory = (ISidedInventory)par1IInventory;
398            int[] aint = isidedinventory.getSizeInventorySide(par3);
399
400            for (int j = 0; j < aint.length && par2ItemStack != null && par2ItemStack.stackSize > 0; ++j)
401            {
402                par2ItemStack = func_102014_c(par1IInventory, par2ItemStack, aint[j], par3);
403            }
404        }
405        else
406        {
407            int k = par1IInventory.getSizeInventory();
408
409            for (int l = 0; l < k && par2ItemStack != null && par2ItemStack.stackSize > 0; ++l)
410            {
411                par2ItemStack = func_102014_c(par1IInventory, par2ItemStack, l, par3);
412            }
413        }
414
415        if (par2ItemStack != null && par2ItemStack.stackSize == 0)
416        {
417            par2ItemStack = null;
418        }
419
420        return par2ItemStack;
421    }
422
423    private static boolean func_102015_a(IInventory par0IInventory, ItemStack par1ItemStack, int par2, int par3)
424    {
425        return !par0IInventory.isStackValidForSlot(par2, par1ItemStack) ? false : !(par0IInventory instanceof ISidedInventory) || ((ISidedInventory)par0IInventory).func_102007_a(par2, par1ItemStack, par3);
426    }
427
428    private static boolean func_102013_b(IInventory par0IInventory, ItemStack par1ItemStack, int par2, int par3)
429    {
430        return !(par0IInventory instanceof ISidedInventory) || ((ISidedInventory)par0IInventory).func_102008_b(par2, par1ItemStack, par3);
431    }
432
433    private static ItemStack func_102014_c(IInventory par0IInventory, ItemStack par1ItemStack, int par2, int par3)
434    {
435        ItemStack itemstack1 = par0IInventory.getStackInSlot(par2);
436
437        if (func_102015_a(par0IInventory, par1ItemStack, par2, par3))
438        {
439            boolean flag = false;
440
441            if (itemstack1 == null)
442            {
443                par0IInventory.setInventorySlotContents(par2, par1ItemStack);
444                par1ItemStack = null;
445                flag = true;
446            }
447            else if (func_94114_a(itemstack1, par1ItemStack))
448            {
449                int k = par1ItemStack.getMaxStackSize() - itemstack1.stackSize;
450                int l = Math.min(par1ItemStack.stackSize, k);
451                par1ItemStack.stackSize -= l;
452                itemstack1.stackSize += l;
453                flag = l > 0;
454            }
455
456            if (flag)
457            {
458                if (par0IInventory instanceof TileEntityHopper)
459                {
460                    ((TileEntityHopper)par0IInventory).func_98046_c(8);
461                }
462
463                par0IInventory.onInventoryChanged();
464            }
465        }
466
467        return par1ItemStack;
468    }
469
470    private IInventory func_94119_v()
471    {
472        int i = BlockHopper.func_94451_c(this.getBlockMetadata());
473        return func_96117_b(this.getWorldObj(), (double)(this.xCoord + Facing.offsetsXForSide[i]), (double)(this.yCoord + Facing.offsetsYForSide[i]), (double)(this.zCoord + Facing.offsetsZForSide[i]));
474    }
475
476    public static IInventory func_96118_b(Hopper par0Hopper)
477    {
478        return func_96117_b(par0Hopper.getWorldObj(), par0Hopper.func_96107_aA(), par0Hopper.func_96109_aB() + 1.0D, par0Hopper.func_96108_aC());
479    }
480
481    public static EntityItem func_96119_a(World par0World, double par1, double par3, double par5)
482    {
483        List list = par0World.selectEntitiesWithinAABB(EntityItem.class, AxisAlignedBB.getAABBPool().getAABB(par1, par3, par5, par1 + 1.0D, par3 + 1.0D, par5 + 1.0D), IEntitySelector.field_94557_a);
484        return list.size() > 0 ? (EntityItem)list.get(0) : null;
485    }
486
487    public static IInventory func_96117_b(World par0World, double par1, double par3, double par5)
488    {
489        IInventory iinventory = null;
490        int i = MathHelper.floor_double(par1);
491        int j = MathHelper.floor_double(par3);
492        int k = MathHelper.floor_double(par5);
493        TileEntity tileentity = par0World.getBlockTileEntity(i, j, k);
494
495        if (tileentity != null && tileentity instanceof IInventory)
496        {
497            iinventory = (IInventory)tileentity;
498
499            if (iinventory instanceof TileEntityChest)
500            {
501                int l = par0World.getBlockId(i, j, k);
502                Block block = Block.blocksList[l];
503
504                if (block instanceof BlockChest)
505                {
506                    iinventory = ((BlockChest)block).func_94442_h_(par0World, i, j, k);
507                }
508            }
509        }
510
511        if (iinventory == null)
512        {
513            List list = par0World.func_94576_a((Entity)null, AxisAlignedBB.getAABBPool().getAABB(par1, par3, par5, par1 + 1.0D, par3 + 1.0D, par5 + 1.0D), IEntitySelector.field_96566_b);
514
515            if (list != null && list.size() > 0)
516            {
517                iinventory = (IInventory)list.get(par0World.rand.nextInt(list.size()));
518            }
519        }
520
521        return iinventory;
522    }
523
524    private static boolean func_94114_a(ItemStack par1ItemStack, ItemStack par2ItemStack)
525    {
526        return par1ItemStack.itemID != par2ItemStack.itemID ? false : (par1ItemStack.getItemDamage() != par2ItemStack.getItemDamage() ? false : (par1ItemStack.stackSize > par1ItemStack.getMaxStackSize() ? false : ItemStack.areItemStackTagsEqual(par1ItemStack, par2ItemStack)));
527    }
528
529    public double func_96107_aA()
530    {
531        return (double)this.xCoord;
532    }
533
534    public double func_96109_aB()
535    {
536        return (double)this.yCoord;
537    }
538
539    public double func_96108_aC()
540    {
541        return (double)this.zCoord;
542    }
543
544    public void func_98046_c(int par1)
545    {
546        this.field_98048_c = par1;
547    }
548
549    public boolean func_98047_l()
550    {
551        return this.field_98048_c > 0;
552    }
553}