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