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}