001package net.minecraft.tileentity; 002 003import java.util.List; 004import net.minecraft.block.Block; 005import net.minecraft.block.BlockChest; 006import net.minecraft.block.BlockHopper; 007import net.minecraft.command.IEntitySelector; 008import net.minecraft.entity.Entity; 009import net.minecraft.entity.item.EntityItem; 010import net.minecraft.entity.player.EntityPlayer; 011import net.minecraft.inventory.IInventory; 012import net.minecraft.inventory.ISidedInventory; 013import net.minecraft.item.ItemStack; 014import net.minecraft.nbt.NBTTagCompound; 015import net.minecraft.nbt.NBTTagList; 016import net.minecraft.util.AxisAlignedBB; 017import net.minecraft.util.Facing; 018import net.minecraft.util.MathHelper; 019import net.minecraft.world.World; 020 021public class TileEntityHopper extends TileEntity implements Hopper 022{ 023 private ItemStack[] field_94124_b = new ItemStack[5]; 024 025 /** The name that is displayed if the hopper was renamed */ 026 private String inventoryName; 027 private int field_98048_c = -1; 028 029 /** 030 * Reads a tile entity from NBT. 031 */ 032 public void readFromNBT(NBTTagCompound par1NBTTagCompound) 033 { 034 super.readFromNBT(par1NBTTagCompound); 035 NBTTagList nbttaglist = par1NBTTagCompound.getTagList("Items"); 036 this.field_94124_b = new ItemStack[this.getSizeInventory()]; 037 038 if (par1NBTTagCompound.hasKey("CustomName")) 039 { 040 this.inventoryName = par1NBTTagCompound.getString("CustomName"); 041 } 042 043 this.field_98048_c = par1NBTTagCompound.getInteger("TransferCooldown"); 044 045 for (int i = 0; i < nbttaglist.tagCount(); ++i) 046 { 047 NBTTagCompound nbttagcompound1 = (NBTTagCompound)nbttaglist.tagAt(i); 048 byte b0 = nbttagcompound1.getByte("Slot"); 049 050 if (b0 >= 0 && b0 < this.field_94124_b.length) 051 { 052 this.field_94124_b[b0] = ItemStack.loadItemStackFromNBT(nbttagcompound1); 053 } 054 } 055 } 056 057 /** 058 * Writes a tile entity to NBT. 059 */ 060 public void writeToNBT(NBTTagCompound par1NBTTagCompound) 061 { 062 super.writeToNBT(par1NBTTagCompound); 063 NBTTagList nbttaglist = new NBTTagList(); 064 065 for (int i = 0; i < this.field_94124_b.length; ++i) 066 { 067 if (this.field_94124_b[i] != null) 068 { 069 NBTTagCompound nbttagcompound1 = new NBTTagCompound(); 070 nbttagcompound1.setByte("Slot", (byte)i); 071 this.field_94124_b[i].writeToNBT(nbttagcompound1); 072 nbttaglist.appendTag(nbttagcompound1); 073 } 074 } 075 076 par1NBTTagCompound.setTag("Items", nbttaglist); 077 par1NBTTagCompound.setInteger("TransferCooldown", this.field_98048_c); 078 079 if (this.isInvNameLocalized()) 080 { 081 par1NBTTagCompound.setString("CustomName", this.inventoryName); 082 } 083 } 084 085 /** 086 * Called when an the contents of an Inventory change, usually 087 */ 088 public void onInventoryChanged() 089 { 090 super.onInventoryChanged(); 091 } 092 093 /** 094 * Returns the number of slots in the inventory. 095 */ 096 public int getSizeInventory() 097 { 098 return this.field_94124_b.length; 099 } 100 101 /** 102 * Returns the stack in slot i 103 */ 104 public ItemStack getStackInSlot(int par1) 105 { 106 return this.field_94124_b[par1]; 107 } 108 109 /** 110 * Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a 111 * new stack. 112 */ 113 public ItemStack decrStackSize(int par1, int par2) 114 { 115 if (this.field_94124_b[par1] != null) 116 { 117 ItemStack itemstack; 118 119 if (this.field_94124_b[par1].stackSize <= par2) 120 { 121 itemstack = this.field_94124_b[par1]; 122 this.field_94124_b[par1] = null; 123 return itemstack; 124 } 125 else 126 { 127 itemstack = this.field_94124_b[par1].splitStack(par2); 128 129 if (this.field_94124_b[par1].stackSize == 0) 130 { 131 this.field_94124_b[par1] = null; 132 } 133 134 return itemstack; 135 } 136 } 137 else 138 { 139 return null; 140 } 141 } 142 143 /** 144 * When some containers are closed they call this on each slot, then drop whatever it returns as an EntityItem - 145 * like when you close a workbench GUI. 146 */ 147 public ItemStack getStackInSlotOnClosing(int par1) 148 { 149 if (this.field_94124_b[par1] != null) 150 { 151 ItemStack itemstack = this.field_94124_b[par1]; 152 this.field_94124_b[par1] = null; 153 return itemstack; 154 } 155 else 156 { 157 return null; 158 } 159 } 160 161 /** 162 * Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections). 163 */ 164 public void setInventorySlotContents(int par1, ItemStack par2ItemStack) 165 { 166 this.field_94124_b[par1] = par2ItemStack; 167 168 if (par2ItemStack != null && par2ItemStack.stackSize > this.getInventoryStackLimit()) 169 { 170 par2ItemStack.stackSize = this.getInventoryStackLimit(); 171 } 172 } 173 174 /** 175 * Returns the name of the inventory. 176 */ 177 public String getInvName() 178 { 179 return this.isInvNameLocalized() ? this.inventoryName : "container.hopper"; 180 } 181 182 /** 183 * If this returns false, the inventory name will be used as an unlocalized name, and translated into the player's 184 * language. Otherwise it will be used directly. 185 */ 186 public boolean isInvNameLocalized() 187 { 188 return this.inventoryName != null && this.inventoryName.length() > 0; 189 } 190 191 public void func_96115_a(String par1Str) 192 { 193 this.inventoryName = par1Str; 194 } 195 196 /** 197 * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't 198 * this more of a set than a get?* 199 */ 200 public int getInventoryStackLimit() 201 { 202 return 64; 203 } 204 205 /** 206 * Do not make give this method the name canInteractWith because it clashes with Container 207 */ 208 public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer) 209 { 210 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; 211 } 212 213 public void openChest() {} 214 215 public void closeChest() {} 216 217 /** 218 * Returns true if automation is allowed to insert the given stack (ignoring stack size) into the given slot. 219 */ 220 public boolean isStackValidForSlot(int par1, ItemStack par2ItemStack) 221 { 222 return true; 223 } 224 225 /** 226 * Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count 227 * ticks and creates a new spawn inside its implementation. 228 */ 229 public void updateEntity() 230 { 231 if (this.worldObj != null && !this.worldObj.isRemote) 232 { 233 --this.field_98048_c; 234 235 if (!this.func_98047_l()) 236 { 237 this.func_98046_c(0); 238 this.func_98045_j(); 239 } 240 } 241 } 242 243 public boolean func_98045_j() 244 { 245 if (this.worldObj != null && !this.worldObj.isRemote) 246 { 247 if (!this.func_98047_l() && BlockHopper.func_94452_d(this.getBlockMetadata())) 248 { 249 boolean flag = this.func_94116_j() | func_96116_a(this); 250 251 if (flag) 252 { 253 this.func_98046_c(8); 254 this.onInventoryChanged(); 255 return true; 256 } 257 } 258 259 return false; 260 } 261 else 262 { 263 return false; 264 } 265 } 266 267 private boolean func_94116_j() 268 { 269 IInventory iinventory = this.func_94119_v(); 270 271 if (iinventory == null) 272 { 273 return false; 274 } 275 else 276 { 277 for (int i = 0; i < this.getSizeInventory(); ++i) 278 { 279 if (this.getStackInSlot(i) != null) 280 { 281 ItemStack itemstack = this.getStackInSlot(i).copy(); 282 ItemStack itemstack1 = func_94117_a(iinventory, this.decrStackSize(i, 1), Facing.faceToSide[BlockHopper.func_94451_c(this.getBlockMetadata())]); 283 284 if (itemstack1 == null || itemstack1.stackSize == 0) 285 { 286 iinventory.onInventoryChanged(); 287 return true; 288 } 289 290 this.setInventorySlotContents(i, itemstack); 291 } 292 } 293 294 return false; 295 } 296 } 297 298 public static boolean func_96116_a(Hopper par0Hopper) 299 { 300 IInventory iinventory = func_96118_b(par0Hopper); 301 302 if (iinventory != null) 303 { 304 byte b0 = 0; 305 306 if (iinventory instanceof ISidedInventory && b0 > -1) 307 { 308 ISidedInventory isidedinventory = (ISidedInventory)iinventory; 309 int[] aint = isidedinventory.getSizeInventorySide(b0); 310 311 for (int i = 0; i < aint.length; ++i) 312 { 313 if (func_102012_a(par0Hopper, iinventory, aint[i], b0)) 314 { 315 return true; 316 } 317 } 318 } 319 else 320 { 321 int j = iinventory.getSizeInventory(); 322 323 for (int k = 0; k < j; ++k) 324 { 325 if (func_102012_a(par0Hopper, iinventory, k, b0)) 326 { 327 return true; 328 } 329 } 330 } 331 } 332 else 333 { 334 EntityItem entityitem = func_96119_a(par0Hopper.getWorldObj(), par0Hopper.func_96107_aA(), par0Hopper.func_96109_aB() + 1.0D, par0Hopper.func_96108_aC()); 335 336 if (entityitem != null) 337 { 338 return func_96114_a(par0Hopper, entityitem); 339 } 340 } 341 342 return false; 343 } 344 345 private static boolean func_102012_a(Hopper par0Hopper, IInventory par1IInventory, int par2, int par3) 346 { 347 ItemStack itemstack = par1IInventory.getStackInSlot(par2); 348 349 if (itemstack != null && func_102013_b(par1IInventory, itemstack, par2, par3)) 350 { 351 ItemStack itemstack1 = itemstack.copy(); 352 ItemStack itemstack2 = func_94117_a(par0Hopper, par1IInventory.decrStackSize(par2, 1), -1); 353 354 if (itemstack2 == null || itemstack2.stackSize == 0) 355 { 356 par1IInventory.onInventoryChanged(); 357 return true; 358 } 359 360 par1IInventory.setInventorySlotContents(par2, itemstack1); 361 } 362 363 return false; 364 } 365 366 public static boolean func_96114_a(IInventory par0IInventory, EntityItem par1EntityItem) 367 { 368 boolean flag = false; 369 370 if (par1EntityItem == null) 371 { 372 return false; 373 } 374 else 375 { 376 ItemStack itemstack = par1EntityItem.getEntityItem().copy(); 377 ItemStack itemstack1 = func_94117_a(par0IInventory, itemstack, -1); 378 379 if (itemstack1 != null && itemstack1.stackSize != 0) 380 { 381 par1EntityItem.setEntityItemStack(itemstack1); 382 } 383 else 384 { 385 flag = true; 386 par1EntityItem.setDead(); 387 } 388 389 return flag; 390 } 391 } 392 393 public static ItemStack func_94117_a(IInventory par1IInventory, ItemStack par2ItemStack, int par3) 394 { 395 if (par1IInventory instanceof ISidedInventory && par3 > -1) 396 { 397 ISidedInventory isidedinventory = (ISidedInventory)par1IInventory; 398 int[] aint = isidedinventory.getSizeInventorySide(par3); 399 400 for (int j = 0; j < aint.length && par2ItemStack != null && par2ItemStack.stackSize > 0; ++j) 401 { 402 par2ItemStack = func_102014_c(par1IInventory, par2ItemStack, aint[j], par3); 403 } 404 } 405 else 406 { 407 int k = par1IInventory.getSizeInventory(); 408 409 for (int l = 0; l < k && par2ItemStack != null && par2ItemStack.stackSize > 0; ++l) 410 { 411 par2ItemStack = func_102014_c(par1IInventory, par2ItemStack, l, par3); 412 } 413 } 414 415 if (par2ItemStack != null && par2ItemStack.stackSize == 0) 416 { 417 par2ItemStack = null; 418 } 419 420 return par2ItemStack; 421 } 422 423 private static boolean func_102015_a(IInventory par0IInventory, ItemStack par1ItemStack, int par2, int par3) 424 { 425 return !par0IInventory.isStackValidForSlot(par2, par1ItemStack) ? false : !(par0IInventory instanceof ISidedInventory) || ((ISidedInventory)par0IInventory).func_102007_a(par2, par1ItemStack, par3); 426 } 427 428 private static boolean func_102013_b(IInventory par0IInventory, ItemStack par1ItemStack, int par2, int par3) 429 { 430 return !(par0IInventory instanceof ISidedInventory) || ((ISidedInventory)par0IInventory).func_102008_b(par2, par1ItemStack, par3); 431 } 432 433 private static ItemStack func_102014_c(IInventory par0IInventory, ItemStack par1ItemStack, int par2, int par3) 434 { 435 ItemStack itemstack1 = par0IInventory.getStackInSlot(par2); 436 437 if (func_102015_a(par0IInventory, par1ItemStack, par2, par3)) 438 { 439 boolean flag = false; 440 441 if (itemstack1 == null) 442 { 443 par0IInventory.setInventorySlotContents(par2, par1ItemStack); 444 par1ItemStack = null; 445 flag = true; 446 } 447 else if (func_94114_a(itemstack1, par1ItemStack)) 448 { 449 int k = par1ItemStack.getMaxStackSize() - itemstack1.stackSize; 450 int l = Math.min(par1ItemStack.stackSize, k); 451 par1ItemStack.stackSize -= l; 452 itemstack1.stackSize += l; 453 flag = l > 0; 454 } 455 456 if (flag) 457 { 458 if (par0IInventory instanceof TileEntityHopper) 459 { 460 ((TileEntityHopper)par0IInventory).func_98046_c(8); 461 } 462 463 par0IInventory.onInventoryChanged(); 464 } 465 } 466 467 return par1ItemStack; 468 } 469 470 private IInventory func_94119_v() 471 { 472 int i = BlockHopper.func_94451_c(this.getBlockMetadata()); 473 return func_96117_b(this.getWorldObj(), (double)(this.xCoord + Facing.offsetsXForSide[i]), (double)(this.yCoord + Facing.offsetsYForSide[i]), (double)(this.zCoord + Facing.offsetsZForSide[i])); 474 } 475 476 public static IInventory func_96118_b(Hopper par0Hopper) 477 { 478 return func_96117_b(par0Hopper.getWorldObj(), par0Hopper.func_96107_aA(), par0Hopper.func_96109_aB() + 1.0D, par0Hopper.func_96108_aC()); 479 } 480 481 public static EntityItem func_96119_a(World par0World, double par1, double par3, double par5) 482 { 483 List list = par0World.selectEntitiesWithinAABB(EntityItem.class, AxisAlignedBB.getAABBPool().getAABB(par1, par3, par5, par1 + 1.0D, par3 + 1.0D, par5 + 1.0D), IEntitySelector.field_94557_a); 484 return list.size() > 0 ? (EntityItem)list.get(0) : null; 485 } 486 487 public static IInventory func_96117_b(World par0World, double par1, double par3, double par5) 488 { 489 IInventory iinventory = null; 490 int i = MathHelper.floor_double(par1); 491 int j = MathHelper.floor_double(par3); 492 int k = MathHelper.floor_double(par5); 493 TileEntity tileentity = par0World.getBlockTileEntity(i, j, k); 494 495 if (tileentity != null && tileentity instanceof IInventory) 496 { 497 iinventory = (IInventory)tileentity; 498 499 if (iinventory instanceof TileEntityChest) 500 { 501 int l = par0World.getBlockId(i, j, k); 502 Block block = Block.blocksList[l]; 503 504 if (block instanceof BlockChest) 505 { 506 iinventory = ((BlockChest)block).func_94442_h_(par0World, i, j, k); 507 } 508 } 509 } 510 511 if (iinventory == null) 512 { 513 List list = par0World.func_94576_a((Entity)null, AxisAlignedBB.getAABBPool().getAABB(par1, par3, par5, par1 + 1.0D, par3 + 1.0D, par5 + 1.0D), IEntitySelector.field_96566_b); 514 515 if (list != null && list.size() > 0) 516 { 517 iinventory = (IInventory)list.get(par0World.rand.nextInt(list.size())); 518 } 519 } 520 521 return iinventory; 522 } 523 524 private static boolean func_94114_a(ItemStack par1ItemStack, ItemStack par2ItemStack) 525 { 526 return par1ItemStack.itemID != par2ItemStack.itemID ? false : (par1ItemStack.getItemDamage() != par2ItemStack.getItemDamage() ? false : (par1ItemStack.stackSize > par1ItemStack.getMaxStackSize() ? false : ItemStack.areItemStackTagsEqual(par1ItemStack, par2ItemStack))); 527 } 528 529 public double func_96107_aA() 530 { 531 return (double)this.xCoord; 532 } 533 534 public double func_96109_aB() 535 { 536 return (double)this.yCoord; 537 } 538 539 public double func_96108_aC() 540 { 541 return (double)this.zCoord; 542 } 543 544 public void func_98046_c(int par1) 545 { 546 this.field_98048_c = par1; 547 } 548 549 public boolean func_98047_l() 550 { 551 return this.field_98048_c > 0; 552 } 553}