    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}