    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.isInvNameLocalized() ? this.field_94045_s : "container.chest";
    140    }
    141
    142    /**
    143     * If this returns false, the inventory name will be used as an unlocalized name, and translated into the player's
    144     * language. Otherwise it will be used directly.
    145     */
    146    public boolean isInvNameLocalized()
    147    {
    148        return this.field_94045_s != null && this.field_94045_s.length() > 0;
    149    }
    150
    151    public void func_94043_a(String par1Str)
    152    {
    153        this.field_94045_s = par1Str;
    154    }
    155
    156    /**
    157     * Reads a tile entity from NBT.
    158     */
    159    public void readFromNBT(NBTTagCompound par1NBTTagCompound)
    160    {
    161        super.readFromNBT(par1NBTTagCompound);
    162        NBTTagList nbttaglist = par1NBTTagCompound.getTagList("Items");
    163        this.chestContents = new ItemStack[this.getSizeInventory()];
    164
    165        if (par1NBTTagCompound.hasKey("CustomName"))
    166        {
    167            this.field_94045_s = par1NBTTagCompound.getString("CustomName");
    168        }
    169
    170        for (int i = 0; i < nbttaglist.tagCount(); ++i)
    171        {
    172            NBTTagCompound nbttagcompound1 = (NBTTagCompound)nbttaglist.tagAt(i);
    173            int j = nbttagcompound1.getByte("Slot") & 255;
    174
    175            if (j >= 0 && j < this.chestContents.length)
    176            {
    177                this.chestContents[j] = ItemStack.loadItemStackFromNBT(nbttagcompound1);
    178            }
    179        }
    180    }
    181
    182    /**
    183     * Writes a tile entity to NBT.
    184     */
    185    public void writeToNBT(NBTTagCompound par1NBTTagCompound)
    186    {
    187        super.writeToNBT(par1NBTTagCompound);
    188        NBTTagList nbttaglist = new NBTTagList();
    189
    190        for (int i = 0; i < this.chestContents.length; ++i)
    191        {
    192            if (this.chestContents[i] != null)
    193            {
    194                NBTTagCompound nbttagcompound1 = new NBTTagCompound();
    195                nbttagcompound1.setByte("Slot", (byte)i);
    196                this.chestContents[i].writeToNBT(nbttagcompound1);
    197                nbttaglist.appendTag(nbttagcompound1);
    198            }
    199        }
    200
    201        par1NBTTagCompound.setTag("Items", nbttaglist);
    202
    203        if (this.isInvNameLocalized())
    204        {
    205            par1NBTTagCompound.setString("CustomName", this.field_94045_s);
    206        }
    207    }
    208
    209    /**
    210     * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't
    211     * this more of a set than a get?*
    212     */
    213    public int getInventoryStackLimit()
    214    {
    215        return 64;
    216    }
    217
    218    /**
    219     * Do not make give this method the name canInteractWith because it clashes with Container
    220     */
    221    public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer)
    222    {
    223        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;
    224    }
    225
    226    /**
    227     * Causes the TileEntity to reset all it's cached values for it's container block, blockID, metaData and in the case
    228     * of chests, the adjcacent chest check
    229     */
    230    public void updateContainingBlockInfo()
    231    {
    232        super.updateContainingBlockInfo();
    233        this.adjacentChestChecked = false;
    234    }
    235
    236    private void func_90009_a(TileEntityChest par1TileEntityChest, int par2)
    237    {
    238        if (par1TileEntityChest.isInvalid())
    239        {
    240            this.adjacentChestChecked = false;
    241        }
    242        else if (this.adjacentChestChecked)
    243        {
    244            switch (par2)
    245            {
    246                case 0:
    247                    if (this.adjacentChestZPosition != par1TileEntityChest)
    248                    {
    249                        this.adjacentChestChecked = false;
    250                    }
    251
    252                    break;
    253                case 1:
    254                    if (this.adjacentChestXNeg != par1TileEntityChest)
    255                    {
    256                        this.adjacentChestChecked = false;
    257                    }
    258
    259                    break;
    260                case 2:
    261                    if (this.adjacentChestZNeg != par1TileEntityChest)
    262                    {
    263                        this.adjacentChestChecked = false;
    264                    }
    265
    266                    break;
    267                case 3:
    268                    if (this.adjacentChestXPos != par1TileEntityChest)
    269                    {
    270                        this.adjacentChestChecked = false;
    271                    }
    272            }
    273        }
    274    }
    275
    276    /**
    277     * Performs the check for adjacent chests to determine if this chest is double or not.
    278     */
    279    public void checkForAdjacentChests()
    280    {
    281        if (!this.adjacentChestChecked)
    282        {
    283            this.adjacentChestChecked = true;
    284            this.adjacentChestZNeg = null;
    285            this.adjacentChestXPos = null;
    286            this.adjacentChestXNeg = null;
    287            this.adjacentChestZPosition = null;
    288
    289            if (this.func_94044_a(this.xCoord - 1, this.yCoord, this.zCoord))
    290            {
    291                this.adjacentChestXNeg = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord - 1, this.yCoord, this.zCoord);
    292            }
    293
    294            if (this.func_94044_a(this.xCoord + 1, this.yCoord, this.zCoord))
    295            {
    296                this.adjacentChestXPos = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord + 1, this.yCoord, this.zCoord);
    297            }
    298
    299            if (this.func_94044_a(this.xCoord, this.yCoord, this.zCoord - 1))
    300            {
    301                this.adjacentChestZNeg = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord - 1);
    302            }
    303
    304            if (this.func_94044_a(this.xCoord, this.yCoord, this.zCoord + 1))
    305            {
    306                this.adjacentChestZPosition = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord + 1);
    307            }
    308
    309            if (this.adjacentChestZNeg != null)
    310            {
    311                this.adjacentChestZNeg.func_90009_a(this, 0);
    312            }
    313
    314            if (this.adjacentChestZPosition != null)
    315            {
    316                this.adjacentChestZPosition.func_90009_a(this, 2);
    317            }
    318
    319            if (this.adjacentChestXPos != null)
    320            {
    321                this.adjacentChestXPos.func_90009_a(this, 1);
    322            }
    323
    324            if (this.adjacentChestXNeg != null)
    325            {
    326                this.adjacentChestXNeg.func_90009_a(this, 3);
    327            }
    328        }
    329    }
    330
    331    private boolean func_94044_a(int par1, int par2, int par3)
    332    {
    333        Block block = Block.blocksList[this.worldObj.getBlockId(par1, par2, par3)];
    334        return block != null && block instanceof BlockChest ? ((BlockChest)block).field_94443_a == this.func_98041_l() : false;
    335    }
    336
    337    /**
    338     * Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count
    339     * ticks and creates a new spawn inside its implementation.
    340     */
    341    public void updateEntity()
    342    {
    343        super.updateEntity();
    344        this.checkForAdjacentChests();
    345        ++this.ticksSinceSync;
    346        float f;
    347
    348        if (!this.worldObj.isRemote && this.numUsingPlayers != 0 && (this.ticksSinceSync + this.xCoord + this.yCoord + this.zCoord) % 200 == 0)
    349        {
    350            this.numUsingPlayers = 0;
    351            f = 5.0F;
    352            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)));
    353            Iterator iterator = list.iterator();
    354
    355            while (iterator.hasNext())
    356            {
    357                EntityPlayer entityplayer = (EntityPlayer)iterator.next();
    358
    359                if (entityplayer.openContainer instanceof ContainerChest)
    360                {
    361                    IInventory iinventory = ((ContainerChest)entityplayer.openContainer).getLowerChestInventory();
    362
    363                    if (iinventory == this || iinventory instanceof InventoryLargeChest && ((InventoryLargeChest)iinventory).isPartOfLargeChest(this))
    364                    {
    365                        ++this.numUsingPlayers;
    366                    }
    367                }
    368            }
    369        }
    370
    371        this.prevLidAngle = this.lidAngle;
    372        f = 0.1F;
    373        double d0;
    374
    375        if (this.numUsingPlayers > 0 && this.lidAngle == 0.0F && this.adjacentChestZNeg == null && this.adjacentChestXNeg == null)
    376        {
    377            double d1 = (double)this.xCoord + 0.5D;
    378            d0 = (double)this.zCoord + 0.5D;
    379
    380            if (this.adjacentChestZPosition != null)
    381            {
    382                d0 += 0.5D;
    383            }
    384
    385            if (this.adjacentChestXPos != null)
    386            {
    387                d1 += 0.5D;
    388            }
    389
    390            this.worldObj.playSoundEffect(d1, (double)this.yCoord + 0.5D, d0, "random.chestopen", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F);
    391        }
    392
    393        if (this.numUsingPlayers == 0 && this.lidAngle > 0.0F || this.numUsingPlayers > 0 && this.lidAngle < 1.0F)
    394        {
    395            float f1 = this.lidAngle;
    396
    397            if (this.numUsingPlayers > 0)
    398            {
    399                this.lidAngle += f;
    400            }
    401            else
    402            {
    403                this.lidAngle -= f;
    404            }
    405
    406            if (this.lidAngle > 1.0F)
    407            {
    408                this.lidAngle = 1.0F;
    409            }
    410
    411            float f2 = 0.5F;
    412
    413            if (this.lidAngle < f2 && f1 >= f2 && this.adjacentChestZNeg == null && this.adjacentChestXNeg == null)
    414            {
    415                d0 = (double)this.xCoord + 0.5D;
    416                double d2 = (double)this.zCoord + 0.5D;
    417
    418                if (this.adjacentChestZPosition != null)
    419                {
    420                    d2 += 0.5D;
    421                }
    422
    423                if (this.adjacentChestXPos != null)
    424                {
    425                    d0 += 0.5D;
    426                }
    427
    428                this.worldObj.playSoundEffect(d0, (double)this.yCoord + 0.5D, d2, "random.chestclosed", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F);
    429            }
    430
    431            if (this.lidAngle < 0.0F)
    432            {
    433                this.lidAngle = 0.0F;
    434            }
    435        }
    436    }
    437
    438    /**
    439     * Called when a client event is received with the event number and argument, see World.sendClientEvent
    440     */
    441    public boolean receiveClientEvent(int par1, int par2)
    442    {
    443        if (par1 == 1)
    444        {
    445            this.numUsingPlayers = par2;
    446            return true;
    447        }
    448        else
    449        {
    450            return super.receiveClientEvent(par1, par2);
    451        }
    452    }
    453
    454    public void openChest()
    455    {
    456        if (this.numUsingPlayers < 0)
    457        {
    458            this.numUsingPlayers = 0;
    459        }
    460
    461        ++this.numUsingPlayers;
    462        this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID, 1, this.numUsingPlayers);
    463        this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID);
    464        this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord - 1, this.zCoord, this.getBlockType().blockID);
    465    }
    466
    467    public void closeChest()
    468    {
    469        if (this.getBlockType() != null && this.getBlockType() instanceof BlockChest)
    470        {
    471            --this.numUsingPlayers;
    472            this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID, 1, this.numUsingPlayers);
    473            this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID);
    474            this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord - 1, this.zCoord, this.getBlockType().blockID);
    475        }
    476    }
    477
    478    /**
    479     * Returns true if automation is allowed to insert the given stack (ignoring stack size) into the given slot.
    480     */
    481    public boolean isStackValidForSlot(int par1, ItemStack par2ItemStack)
    482    {
    483        return true;
    484    }
    485
    486    /**
    487     * invalidates a tile entity
    488     */
    489    public void invalidate()
    490    {
    491        super.invalidate();
    492        this.updateContainingBlockInfo();
    493        this.checkForAdjacentChests();
    494    }
    495
    496    public int func_98041_l()
    497    {
    498        if (this.field_94046_i == -1)
    499        {
    500            if (this.worldObj == null || !(this.getBlockType() instanceof BlockChest))
    501            {
    502                return 0;
    503            }
    504
    505            this.field_94046_i = ((BlockChest)this.getBlockType()).field_94443_a;
    506        }
    507
    508        return this.field_94046_i;
    509    }
    510}