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