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        /**
201         * Performs the check for adjacent chests to determine if this chest is double or not.
202         */
203        public void checkForAdjacentChests()
204        {
205            if (!this.adjacentChestChecked)
206            {
207                this.adjacentChestChecked = true;
208                this.adjacentChestZNeg = null;
209                this.adjacentChestXPos = null;
210                this.adjacentChestXNeg = null;
211                this.adjacentChestZPosition = null;
212    
213                if (this.worldObj.getBlockId(this.xCoord - 1, this.yCoord, this.zCoord) == Block.chest.blockID)
214                {
215                    this.adjacentChestXNeg = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord - 1, this.yCoord, this.zCoord);
216                }
217    
218                if (this.worldObj.getBlockId(this.xCoord + 1, this.yCoord, this.zCoord) == Block.chest.blockID)
219                {
220                    this.adjacentChestXPos = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord + 1, this.yCoord, this.zCoord);
221                }
222    
223                if (this.worldObj.getBlockId(this.xCoord, this.yCoord, this.zCoord - 1) == Block.chest.blockID)
224                {
225                    this.adjacentChestZNeg = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord - 1);
226                }
227    
228                if (this.worldObj.getBlockId(this.xCoord, this.yCoord, this.zCoord + 1) == Block.chest.blockID)
229                {
230                    this.adjacentChestZPosition = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord + 1);
231                }
232    
233                if (this.adjacentChestZNeg != null)
234                {
235                    this.adjacentChestZNeg.updateContainingBlockInfo();
236                }
237    
238                if (this.adjacentChestZPosition != null)
239                {
240                    this.adjacentChestZPosition.updateContainingBlockInfo();
241                }
242    
243                if (this.adjacentChestXPos != null)
244                {
245                    this.adjacentChestXPos.updateContainingBlockInfo();
246                }
247    
248                if (this.adjacentChestXNeg != null)
249                {
250                    this.adjacentChestXNeg.updateContainingBlockInfo();
251                }
252            }
253        }
254    
255        /**
256         * Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count
257         * ticks and creates a new spawn inside its implementation.
258         */
259        public void updateEntity()
260        {
261            super.updateEntity();
262            this.checkForAdjacentChests();
263            ++this.ticksSinceSync;
264            float var1;
265    
266            if (!this.worldObj.isRemote && this.numUsingPlayers != 0 && (this.ticksSinceSync + this.xCoord + this.yCoord + this.zCoord) % 200 == 0)
267            {
268                this.numUsingPlayers = 0;
269                var1 = 5.0F;
270                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)));
271                Iterator var3 = var2.iterator();
272    
273                while (var3.hasNext())
274                {
275                    EntityPlayer var4 = (EntityPlayer)var3.next();
276    
277                    if (var4.craftingInventory instanceof ContainerChest && ((ContainerChest)var4.craftingInventory).func_85151_d() == this)
278                    {
279                        ++this.numUsingPlayers;
280                    }
281                }
282            }
283    
284            this.prevLidAngle = this.lidAngle;
285            var1 = 0.1F;
286            double var11;
287    
288            if (this.numUsingPlayers > 0 && this.lidAngle == 0.0F && this.adjacentChestZNeg == null && this.adjacentChestXNeg == null)
289            {
290                double var8 = (double)this.xCoord + 0.5D;
291                var11 = (double)this.zCoord + 0.5D;
292    
293                if (this.adjacentChestZPosition != null)
294                {
295                    var11 += 0.5D;
296                }
297    
298                if (this.adjacentChestXPos != null)
299                {
300                    var8 += 0.5D;
301                }
302    
303                this.worldObj.playSoundEffect(var8, (double)this.yCoord + 0.5D, var11, "random.chestopen", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F);
304            }
305    
306            if (this.numUsingPlayers == 0 && this.lidAngle > 0.0F || this.numUsingPlayers > 0 && this.lidAngle < 1.0F)
307            {
308                float var9 = this.lidAngle;
309    
310                if (this.numUsingPlayers > 0)
311                {
312                    this.lidAngle += var1;
313                }
314                else
315                {
316                    this.lidAngle -= var1;
317                }
318    
319                if (this.lidAngle > 1.0F)
320                {
321                    this.lidAngle = 1.0F;
322                }
323    
324                float var10 = 0.5F;
325    
326                if (this.lidAngle < var10 && var9 >= var10 && this.adjacentChestZNeg == null && this.adjacentChestXNeg == null)
327                {
328                    var11 = (double)this.xCoord + 0.5D;
329                    double var6 = (double)this.zCoord + 0.5D;
330    
331                    if (this.adjacentChestZPosition != null)
332                    {
333                        var6 += 0.5D;
334                    }
335    
336                    if (this.adjacentChestXPos != null)
337                    {
338                        var11 += 0.5D;
339                    }
340    
341                    this.worldObj.playSoundEffect(var11, (double)this.yCoord + 0.5D, var6, "random.chestclosed", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F);
342                }
343    
344                if (this.lidAngle < 0.0F)
345                {
346                    this.lidAngle = 0.0F;
347                }
348            }
349        }
350    
351        /**
352         * Called when a client event is received with the event number and argument, see World.sendClientEvent
353         */
354        public void receiveClientEvent(int par1, int par2)
355        {
356            if (par1 == 1)
357            {
358                this.numUsingPlayers = par2;
359            }
360        }
361    
362        public void openChest()
363        {
364            ++this.numUsingPlayers;
365            this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, Block.chest.blockID, 1, this.numUsingPlayers);
366        }
367    
368        public void closeChest()
369        {
370            --this.numUsingPlayers;
371            this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, Block.chest.blockID, 1, this.numUsingPlayers);
372        }
373    
374        /**
375         * invalidates a tile entity
376         */
377        public void invalidate()
378        {
379            this.updateContainingBlockInfo();
380            this.checkForAdjacentChests();
381            super.invalidate();
382        }
383    }