001package net.minecraft.tileentity;
002
003import java.util.Iterator;
004import java.util.List;
005import net.minecraft.block.Block;
006import net.minecraft.entity.player.EntityPlayer;
007import net.minecraft.inventory.ContainerChest;
008import net.minecraft.inventory.IInventory;
009import net.minecraft.inventory.InventoryLargeChest;
010import net.minecraft.item.ItemStack;
011import net.minecraft.nbt.NBTTagCompound;
012import net.minecraft.nbt.NBTTagList;
013import net.minecraft.util.AxisAlignedBB;
014
015public class TileEntityChest extends TileEntity implements IInventory
016{
017    private ItemStack[] chestContents = new ItemStack[36];
018
019    /** Determines if the check for adjacent chests has taken place. */
020    public boolean adjacentChestChecked = false;
021
022    /** Contains the chest tile located adjacent to this one (if any) */
023    public TileEntityChest adjacentChestZNeg;
024
025    /** Contains the chest tile located adjacent to this one (if any) */
026    public TileEntityChest adjacentChestXPos;
027
028    /** Contains the chest tile located adjacent to this one (if any) */
029    public TileEntityChest adjacentChestXNeg;
030
031    /** Contains the chest tile located adjacent to this one (if any) */
032    public TileEntityChest adjacentChestZPosition;
033
034    /** The current angle of the lid (between 0 and 1) */
035    public float lidAngle;
036
037    /** The angle of the lid last tick */
038    public float prevLidAngle;
039
040    /** The number of players currently using this chest */
041    public int numUsingPlayers;
042
043    /** Server sync counter (once per 20 ticks) */
044    private int ticksSinceSync;
045
046    /**
047     * Returns the number of slots in the inventory.
048     */
049    public int getSizeInventory()
050    {
051        return 27;
052    }
053
054    /**
055     * Returns the stack in slot i
056     */
057    public ItemStack getStackInSlot(int par1)
058    {
059        return this.chestContents[par1];
060    }
061
062    /**
063     * Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a
064     * new stack.
065     */
066    public ItemStack decrStackSize(int par1, int par2)
067    {
068        if (this.chestContents[par1] != null)
069        {
070            ItemStack var3;
071
072            if (this.chestContents[par1].stackSize <= par2)
073            {
074                var3 = this.chestContents[par1];
075                this.chestContents[par1] = null;
076                this.onInventoryChanged();
077                return var3;
078            }
079            else
080            {
081                var3 = this.chestContents[par1].splitStack(par2);
082
083                if (this.chestContents[par1].stackSize == 0)
084                {
085                    this.chestContents[par1] = null;
086                }
087
088                this.onInventoryChanged();
089                return var3;
090            }
091        }
092        else
093        {
094            return null;
095        }
096    }
097
098    /**
099     * When some containers are closed they call this on each slot, then drop whatever it returns as an EntityItem -
100     * like when you close a workbench GUI.
101     */
102    public ItemStack getStackInSlotOnClosing(int par1)
103    {
104        if (this.chestContents[par1] != null)
105        {
106            ItemStack var2 = this.chestContents[par1];
107            this.chestContents[par1] = null;
108            return var2;
109        }
110        else
111        {
112            return null;
113        }
114    }
115
116    /**
117     * Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections).
118     */
119    public void setInventorySlotContents(int par1, ItemStack par2ItemStack)
120    {
121        this.chestContents[par1] = par2ItemStack;
122
123        if (par2ItemStack != null && par2ItemStack.stackSize > this.getInventoryStackLimit())
124        {
125            par2ItemStack.stackSize = this.getInventoryStackLimit();
126        }
127
128        this.onInventoryChanged();
129    }
130
131    /**
132     * Returns the name of the inventory.
133     */
134    public String getInvName()
135    {
136        return "container.chest";
137    }
138
139    /**
140     * Reads a tile entity from NBT.
141     */
142    public void readFromNBT(NBTTagCompound par1NBTTagCompound)
143    {
144        super.readFromNBT(par1NBTTagCompound);
145        NBTTagList var2 = par1NBTTagCompound.getTagList("Items");
146        this.chestContents = new ItemStack[this.getSizeInventory()];
147
148        for (int var3 = 0; var3 < var2.tagCount(); ++var3)
149        {
150            NBTTagCompound var4 = (NBTTagCompound)var2.tagAt(var3);
151            int var5 = var4.getByte("Slot") & 255;
152
153            if (var5 >= 0 && var5 < this.chestContents.length)
154            {
155                this.chestContents[var5] = ItemStack.loadItemStackFromNBT(var4);
156            }
157        }
158    }
159
160    /**
161     * Writes a tile entity to NBT.
162     */
163    public void writeToNBT(NBTTagCompound par1NBTTagCompound)
164    {
165        super.writeToNBT(par1NBTTagCompound);
166        NBTTagList var2 = new NBTTagList();
167
168        for (int var3 = 0; var3 < this.chestContents.length; ++var3)
169        {
170            if (this.chestContents[var3] != null)
171            {
172                NBTTagCompound var4 = new NBTTagCompound();
173                var4.setByte("Slot", (byte)var3);
174                this.chestContents[var3].writeToNBT(var4);
175                var2.appendTag(var4);
176            }
177        }
178
179        par1NBTTagCompound.setTag("Items", var2);
180    }
181
182    /**
183     * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't
184     * this more of a set than a get?*
185     */
186    public int getInventoryStackLimit()
187    {
188        return 64;
189    }
190
191    /**
192     * Do not make give this method the name canInteractWith because it clashes with Container
193     */
194    public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer)
195    {
196        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;
197    }
198
199    /**
200     * Causes the TileEntity to reset all it's cached values for it's container block, blockID, metaData and in the case
201     * of chests, the adjcacent chest check
202     */
203    public void updateContainingBlockInfo()
204    {
205        super.updateContainingBlockInfo();
206        this.adjacentChestChecked = false;
207    }
208
209    private void func_90009_a(TileEntityChest par1TileEntityChest, int par2)
210    {
211        if (par1TileEntityChest.isInvalid())
212        {
213            this.adjacentChestChecked = false;
214        }
215        else if (this.adjacentChestChecked)
216        {
217            switch (par2)
218            {
219                case 0:
220                    if (this.adjacentChestZPosition != par1TileEntityChest)
221                    {
222                        this.adjacentChestChecked = false;
223                    }
224
225                    break;
226                case 1:
227                    if (this.adjacentChestXNeg != par1TileEntityChest)
228                    {
229                        this.adjacentChestChecked = false;
230                    }
231
232                    break;
233                case 2:
234                    if (this.adjacentChestZNeg != par1TileEntityChest)
235                    {
236                        this.adjacentChestChecked = false;
237                    }
238
239                    break;
240                case 3:
241                    if (this.adjacentChestXPos != par1TileEntityChest)
242                    {
243                        this.adjacentChestChecked = false;
244                    }
245            }
246        }
247    }
248
249    /**
250     * Performs the check for adjacent chests to determine if this chest is double or not.
251     */
252    public void checkForAdjacentChests()
253    {
254        if (!this.adjacentChestChecked)
255        {
256            this.adjacentChestChecked = true;
257            this.adjacentChestZNeg = null;
258            this.adjacentChestXPos = null;
259            this.adjacentChestXNeg = null;
260            this.adjacentChestZPosition = null;
261
262            if (this.worldObj.getBlockId(this.xCoord - 1, this.yCoord, this.zCoord) == Block.chest.blockID)
263            {
264                this.adjacentChestXNeg = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord - 1, this.yCoord, this.zCoord);
265            }
266
267            if (this.worldObj.getBlockId(this.xCoord + 1, this.yCoord, this.zCoord) == Block.chest.blockID)
268            {
269                this.adjacentChestXPos = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord + 1, this.yCoord, this.zCoord);
270            }
271
272            if (this.worldObj.getBlockId(this.xCoord, this.yCoord, this.zCoord - 1) == Block.chest.blockID)
273            {
274                this.adjacentChestZNeg = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord - 1);
275            }
276
277            if (this.worldObj.getBlockId(this.xCoord, this.yCoord, this.zCoord + 1) == Block.chest.blockID)
278            {
279                this.adjacentChestZPosition = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord + 1);
280            }
281
282            if (this.adjacentChestZNeg != null)
283            {
284                this.adjacentChestZNeg.func_90009_a(this, 0);
285            }
286
287            if (this.adjacentChestZPosition != null)
288            {
289                this.adjacentChestZPosition.func_90009_a(this, 2);
290            }
291
292            if (this.adjacentChestXPos != null)
293            {
294                this.adjacentChestXPos.func_90009_a(this, 1);
295            }
296
297            if (this.adjacentChestXNeg != null)
298            {
299                this.adjacentChestXNeg.func_90009_a(this, 3);
300            }
301        }
302    }
303
304    /**
305     * Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count
306     * ticks and creates a new spawn inside its implementation.
307     */
308    public void updateEntity()
309    {
310        super.updateEntity();
311        this.checkForAdjacentChests();
312        ++this.ticksSinceSync;
313        float var1;
314
315        if (!this.worldObj.isRemote && this.numUsingPlayers != 0 && (this.ticksSinceSync + this.xCoord + this.yCoord + this.zCoord) % 200 == 0)
316        {
317            this.numUsingPlayers = 0;
318            var1 = 5.0F;
319            List var2 = this.worldObj.getEntitiesWithinAABB(EntityPlayer.class, AxisAlignedBB.getAABBPool().addOrModifyAABBInPool((double)((float)this.xCoord - var1), (double)((float)this.yCoord - var1), (double)((float)this.zCoord - var1), (double)((float)(this.xCoord + 1) + var1), (double)((float)(this.yCoord + 1) + var1), (double)((float)(this.zCoord + 1) + var1)));
320            Iterator var3 = var2.iterator();
321
322            while (var3.hasNext())
323            {
324                EntityPlayer var4 = (EntityPlayer)var3.next();
325
326                if (var4.openContainer instanceof ContainerChest)
327                {
328                    IInventory var5 = ((ContainerChest)var4.openContainer).getLowerChestInventory();
329
330                    if (var5 == this || var5 instanceof InventoryLargeChest && ((InventoryLargeChest)var5).isPartOfLargeChest(this))
331                    {
332                        ++this.numUsingPlayers;
333                    }
334                }
335            }
336        }
337
338        this.prevLidAngle = this.lidAngle;
339        var1 = 0.1F;
340        double var11;
341
342        if (this.numUsingPlayers > 0 && this.lidAngle == 0.0F && this.adjacentChestZNeg == null && this.adjacentChestXNeg == null)
343        {
344            double var8 = (double)this.xCoord + 0.5D;
345            var11 = (double)this.zCoord + 0.5D;
346
347            if (this.adjacentChestZPosition != null)
348            {
349                var11 += 0.5D;
350            }
351
352            if (this.adjacentChestXPos != null)
353            {
354                var8 += 0.5D;
355            }
356
357            this.worldObj.playSoundEffect(var8, (double)this.yCoord + 0.5D, var11, "random.chestopen", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F);
358        }
359
360        if (this.numUsingPlayers == 0 && this.lidAngle > 0.0F || this.numUsingPlayers > 0 && this.lidAngle < 1.0F)
361        {
362            float var9 = this.lidAngle;
363
364            if (this.numUsingPlayers > 0)
365            {
366                this.lidAngle += var1;
367            }
368            else
369            {
370                this.lidAngle -= var1;
371            }
372
373            if (this.lidAngle > 1.0F)
374            {
375                this.lidAngle = 1.0F;
376            }
377
378            float var10 = 0.5F;
379
380            if (this.lidAngle < var10 && var9 >= var10 && this.adjacentChestZNeg == null && this.adjacentChestXNeg == null)
381            {
382                var11 = (double)this.xCoord + 0.5D;
383                double var6 = (double)this.zCoord + 0.5D;
384
385                if (this.adjacentChestZPosition != null)
386                {
387                    var6 += 0.5D;
388                }
389
390                if (this.adjacentChestXPos != null)
391                {
392                    var11 += 0.5D;
393                }
394
395                this.worldObj.playSoundEffect(var11, (double)this.yCoord + 0.5D, var6, "random.chestclosed", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F);
396            }
397
398            if (this.lidAngle < 0.0F)
399            {
400                this.lidAngle = 0.0F;
401            }
402        }
403    }
404
405    /**
406     * Called when a client event is received with the event number and argument, see World.sendClientEvent
407     */
408    public void receiveClientEvent(int par1, int par2)
409    {
410        if (par1 == 1)
411        {
412            this.numUsingPlayers = par2;
413        }
414    }
415
416    public void openChest()
417    {
418        ++this.numUsingPlayers;
419        this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, Block.chest.blockID, 1, this.numUsingPlayers);
420    }
421
422    public void closeChest()
423    {
424        --this.numUsingPlayers;
425        this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, Block.chest.blockID, 1, this.numUsingPlayers);
426    }
427
428    /**
429     * invalidates a tile entity
430     */
431    public void invalidate()
432    {
433        super.invalidate();
434        this.updateContainingBlockInfo();
435        this.checkForAdjacentChests();
436    }
437}