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