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