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}