001    package net.minecraft.tileentity;
002    
003    import java.util.Iterator;
004    import java.util.List;
005    import net.minecraft.block.Block;
006    import net.minecraft.entity.player.EntityPlayer;
007    import net.minecraft.inventory.ContainerChest;
008    import net.minecraft.inventory.IInventory;
009    import net.minecraft.inventory.InventoryLargeChest;
010    import net.minecraft.item.ItemStack;
011    import net.minecraft.nbt.NBTTagCompound;
012    import net.minecraft.nbt.NBTTagList;
013    import net.minecraft.util.AxisAlignedBB;
014    
015    public 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    }