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