001 package net.minecraft.item; 002 003 import cpw.mods.fml.common.Side; 004 import cpw.mods.fml.common.asm.SideOnly; 005 import java.util.ArrayList; 006 import java.util.List; 007 import net.minecraft.block.Block; 008 import net.minecraft.enchantment.Enchantment; 009 import net.minecraft.enchantment.EnchantmentHelper; 010 import net.minecraft.entity.Entity; 011 import net.minecraft.entity.EntityLiving; 012 import net.minecraft.entity.item.EntityItemFrame; 013 import net.minecraft.entity.player.EntityPlayer; 014 import net.minecraft.nbt.NBTBase; 015 import net.minecraft.nbt.NBTTagCompound; 016 import net.minecraft.nbt.NBTTagList; 017 import net.minecraft.nbt.NBTTagString; 018 import net.minecraft.stats.StatList; 019 import net.minecraft.util.StatCollector; 020 import net.minecraft.world.World; 021 022 public final class ItemStack 023 { 024 /** Size of the stack. */ 025 public int stackSize; 026 027 /** 028 * Number of animation frames to go when receiving an item (by walking into it, for example). 029 */ 030 public int animationsToGo; 031 032 /** ID of the item. */ 033 public int itemID; 034 035 /** 036 * A NBTTagMap containing data about an ItemStack. Can only be used for non stackable items 037 */ 038 public NBTTagCompound stackTagCompound; 039 040 /** Damage dealt to the item or number of use. Raise when using items. */ 041 private int itemDamage; 042 043 /** Item frame this stack is on, or null if not on an item frame. */ 044 private EntityItemFrame itemFrame; 045 046 public ItemStack(Block par1Block) 047 { 048 this(par1Block, 1); 049 } 050 051 public ItemStack(Block par1Block, int par2) 052 { 053 this(par1Block.blockID, par2, 0); 054 } 055 056 public ItemStack(Block par1Block, int par2, int par3) 057 { 058 this(par1Block.blockID, par2, par3); 059 } 060 061 public ItemStack(Item par1Item) 062 { 063 this(par1Item.shiftedIndex, 1, 0); 064 } 065 066 public ItemStack(Item par1Item, int par2) 067 { 068 this(par1Item.shiftedIndex, par2, 0); 069 } 070 071 public ItemStack(Item par1Item, int par2, int par3) 072 { 073 this(par1Item.shiftedIndex, par2, par3); 074 } 075 076 public ItemStack(int par1, int par2, int par3) 077 { 078 this.stackSize = 0; 079 this.itemFrame = null; 080 this.itemID = par1; 081 this.stackSize = par2; 082 this.itemDamage = par3; 083 } 084 085 public static ItemStack loadItemStackFromNBT(NBTTagCompound par0NBTTagCompound) 086 { 087 ItemStack var1 = new ItemStack(); 088 var1.readFromNBT(par0NBTTagCompound); 089 return var1.getItem() != null ? var1 : null; 090 } 091 092 private ItemStack() 093 { 094 this.stackSize = 0; 095 this.itemFrame = null; 096 } 097 098 /** 099 * Remove the argument from the stack size. Return a new stack object with argument size. 100 */ 101 public ItemStack splitStack(int par1) 102 { 103 ItemStack var2 = new ItemStack(this.itemID, par1, this.itemDamage); 104 105 if (this.stackTagCompound != null) 106 { 107 var2.stackTagCompound = (NBTTagCompound)this.stackTagCompound.copy(); 108 } 109 110 this.stackSize -= par1; 111 return var2; 112 } 113 114 /** 115 * Returns the object corresponding to the stack. 116 */ 117 public Item getItem() 118 { 119 return Item.itemsList[this.itemID]; 120 } 121 122 @SideOnly(Side.CLIENT) 123 124 /** 125 * Returns the icon index of the current stack. 126 */ 127 public int getIconIndex() 128 { 129 return this.getItem().getIconIndex(this); 130 } 131 132 public boolean tryPlaceItemIntoWorld(EntityPlayer par1EntityPlayer, World par2World, int par3, int par4, int par5, int par6, float par7, float par8, float par9) 133 { 134 boolean var10 = this.getItem().onItemUse(this, par1EntityPlayer, par2World, par3, par4, par5, par6, par7, par8, par9); 135 136 if (var10) 137 { 138 par1EntityPlayer.addStat(StatList.objectUseStats[this.itemID], 1); 139 } 140 141 return var10; 142 } 143 144 /** 145 * Returns the strength of the stack against a given block. 146 */ 147 public float getStrVsBlock(Block par1Block) 148 { 149 return this.getItem().getStrVsBlock(this, par1Block); 150 } 151 152 /** 153 * Called whenever this item stack is equipped and right clicked. Returns the new item stack to put in the position 154 * where this item is. Args: world, player 155 */ 156 public ItemStack useItemRightClick(World par1World, EntityPlayer par2EntityPlayer) 157 { 158 return this.getItem().onItemRightClick(this, par1World, par2EntityPlayer); 159 } 160 161 public ItemStack onFoodEaten(World par1World, EntityPlayer par2EntityPlayer) 162 { 163 return this.getItem().onFoodEaten(this, par1World, par2EntityPlayer); 164 } 165 166 /** 167 * Write the stack fields to a NBT object. Return the new NBT object. 168 */ 169 public NBTTagCompound writeToNBT(NBTTagCompound par1NBTTagCompound) 170 { 171 par1NBTTagCompound.setShort("id", (short)this.itemID); 172 par1NBTTagCompound.setByte("Count", (byte)this.stackSize); 173 par1NBTTagCompound.setShort("Damage", (short)this.itemDamage); 174 175 if (this.stackTagCompound != null) 176 { 177 par1NBTTagCompound.setTag("tag", this.stackTagCompound); 178 } 179 180 return par1NBTTagCompound; 181 } 182 183 /** 184 * Read the stack fields from a NBT object. 185 */ 186 public void readFromNBT(NBTTagCompound par1NBTTagCompound) 187 { 188 this.itemID = par1NBTTagCompound.getShort("id"); 189 this.stackSize = par1NBTTagCompound.getByte("Count"); 190 this.itemDamage = par1NBTTagCompound.getShort("Damage"); 191 192 if (par1NBTTagCompound.hasKey("tag")) 193 { 194 this.stackTagCompound = par1NBTTagCompound.getCompoundTag("tag"); 195 } 196 } 197 198 /** 199 * Returns maximum size of the stack. 200 */ 201 public int getMaxStackSize() 202 { 203 return this.getItem().getItemStackLimit(); 204 } 205 206 /** 207 * Returns true if the ItemStack can hold 2 or more units of the item. 208 */ 209 public boolean isStackable() 210 { 211 return this.getMaxStackSize() > 1 && (!this.isItemStackDamageable() || !this.isItemDamaged()); 212 } 213 214 /** 215 * true if this itemStack is damageable 216 */ 217 public boolean isItemStackDamageable() 218 { 219 return Item.itemsList[this.itemID].getMaxDamage() > 0; 220 } 221 222 public boolean getHasSubtypes() 223 { 224 return Item.itemsList[this.itemID].getHasSubtypes(); 225 } 226 227 /** 228 * returns true when a damageable item is damaged 229 */ 230 public boolean isItemDamaged() 231 { 232 return this.isItemStackDamageable() && this.itemDamage > 0; 233 } 234 235 /** 236 * gets the damage of an itemstack, for displaying purposes 237 */ 238 public int getItemDamageForDisplay() 239 { 240 return this.itemDamage; 241 } 242 243 /** 244 * gets the damage of an itemstack 245 */ 246 public int getItemDamage() 247 { 248 return this.itemDamage; 249 } 250 251 /** 252 * Sets the item damage of the ItemStack. 253 */ 254 public void setItemDamage(int par1) 255 { 256 this.itemDamage = par1; 257 } 258 259 /** 260 * Returns the max damage an item in the stack can take. 261 */ 262 public int getMaxDamage() 263 { 264 return Item.itemsList[this.itemID].getMaxDamage(); 265 } 266 267 /** 268 * Damages the item in the ItemStack 269 */ 270 public void damageItem(int par1, EntityLiving par2EntityLiving) 271 { 272 if (this.isItemStackDamageable()) 273 { 274 if (par1 > 0 && par2EntityLiving instanceof EntityPlayer) 275 { 276 int var3 = EnchantmentHelper.getUnbreakingModifier(par2EntityLiving); 277 278 if (var3 > 0 && par2EntityLiving.worldObj.rand.nextInt(var3 + 1) > 0) 279 { 280 return; 281 } 282 } 283 284 if (!(par2EntityLiving instanceof EntityPlayer) || !((EntityPlayer)par2EntityLiving).capabilities.isCreativeMode) 285 { 286 this.itemDamage += par1; 287 } 288 289 if (this.itemDamage > this.getMaxDamage()) 290 { 291 par2EntityLiving.renderBrokenItemStack(this); 292 293 if (par2EntityLiving instanceof EntityPlayer) 294 { 295 ((EntityPlayer)par2EntityLiving).addStat(StatList.objectBreakStats[this.itemID], 1); 296 } 297 298 --this.stackSize; 299 300 if (this.stackSize < 0) 301 { 302 this.stackSize = 0; 303 } 304 305 this.itemDamage = 0; 306 } 307 } 308 } 309 310 /** 311 * Calls the corresponding fct in di 312 */ 313 public void hitEntity(EntityLiving par1EntityLiving, EntityPlayer par2EntityPlayer) 314 { 315 boolean var3 = Item.itemsList[this.itemID].hitEntity(this, par1EntityLiving, par2EntityPlayer); 316 317 if (var3) 318 { 319 par2EntityPlayer.addStat(StatList.objectUseStats[this.itemID], 1); 320 } 321 } 322 323 public void onBlockDestroyed(World par1World, int par2, int par3, int par4, int par5, EntityPlayer par6EntityPlayer) 324 { 325 boolean var7 = Item.itemsList[this.itemID].onBlockDestroyed(this, par1World, par2, par3, par4, par5, par6EntityPlayer); 326 327 if (var7) 328 { 329 par6EntityPlayer.addStat(StatList.objectUseStats[this.itemID], 1); 330 } 331 } 332 333 /** 334 * Returns the damage against a given entity. 335 */ 336 public int getDamageVsEntity(Entity par1Entity) 337 { 338 return Item.itemsList[this.itemID].getDamageVsEntity(par1Entity); 339 } 340 341 /** 342 * Checks if the itemStack object can harvest a specified block 343 */ 344 public boolean canHarvestBlock(Block par1Block) 345 { 346 return Item.itemsList[this.itemID].canHarvestBlock(par1Block); 347 } 348 349 public boolean interactWith(EntityLiving par1EntityLiving) 350 { 351 return Item.itemsList[this.itemID].itemInteractionForEntity(this, par1EntityLiving); 352 } 353 354 /** 355 * Returns a new stack with the same properties. 356 */ 357 public ItemStack copy() 358 { 359 ItemStack var1 = new ItemStack(this.itemID, this.stackSize, this.itemDamage); 360 361 if (this.stackTagCompound != null) 362 { 363 var1.stackTagCompound = (NBTTagCompound)this.stackTagCompound.copy(); 364 } 365 366 return var1; 367 } 368 369 public static boolean areItemStackTagsEqual(ItemStack par0ItemStack, ItemStack par1ItemStack) 370 { 371 return par0ItemStack == null && par1ItemStack == null ? true : (par0ItemStack != null && par1ItemStack != null ? (par0ItemStack.stackTagCompound == null && par1ItemStack.stackTagCompound != null ? false : par0ItemStack.stackTagCompound == null || par0ItemStack.stackTagCompound.equals(par1ItemStack.stackTagCompound)) : false); 372 } 373 374 /** 375 * compares ItemStack argument1 with ItemStack argument2; returns true if both ItemStacks are equal 376 */ 377 public static boolean areItemStacksEqual(ItemStack par0ItemStack, ItemStack par1ItemStack) 378 { 379 return par0ItemStack == null && par1ItemStack == null ? true : (par0ItemStack != null && par1ItemStack != null ? par0ItemStack.isItemStackEqual(par1ItemStack) : false); 380 } 381 382 /** 383 * compares ItemStack argument to the instance ItemStack; returns true if both ItemStacks are equal 384 */ 385 private boolean isItemStackEqual(ItemStack par1ItemStack) 386 { 387 return this.stackSize != par1ItemStack.stackSize ? false : (this.itemID != par1ItemStack.itemID ? false : (this.itemDamage != par1ItemStack.itemDamage ? false : (this.stackTagCompound == null && par1ItemStack.stackTagCompound != null ? false : this.stackTagCompound == null || this.stackTagCompound.equals(par1ItemStack.stackTagCompound)))); 388 } 389 390 /** 391 * compares ItemStack argument to the instance ItemStack; returns true if the Items contained in both ItemStacks are 392 * equal 393 */ 394 public boolean isItemEqual(ItemStack par1ItemStack) 395 { 396 return this.itemID == par1ItemStack.itemID && this.itemDamage == par1ItemStack.itemDamage; 397 } 398 399 public String getItemName() 400 { 401 return Item.itemsList[this.itemID].getItemNameIS(this); 402 } 403 404 /** 405 * Creates a copy of a ItemStack, a null parameters will return a null. 406 */ 407 public static ItemStack copyItemStack(ItemStack par0ItemStack) 408 { 409 return par0ItemStack == null ? null : par0ItemStack.copy(); 410 } 411 412 public String toString() 413 { 414 return this.stackSize + "x" + Item.itemsList[this.itemID].getItemName() + "@" + this.itemDamage; 415 } 416 417 /** 418 * Called each tick as long the ItemStack in on player inventory. Used to progress the pickup animation and update 419 * maps. 420 */ 421 public void updateAnimation(World par1World, Entity par2Entity, int par3, boolean par4) 422 { 423 if (this.animationsToGo > 0) 424 { 425 --this.animationsToGo; 426 } 427 428 Item.itemsList[this.itemID].onUpdate(this, par1World, par2Entity, par3, par4); 429 } 430 431 public void onCrafting(World par1World, EntityPlayer par2EntityPlayer, int par3) 432 { 433 par2EntityPlayer.addStat(StatList.objectCraftStats[this.itemID], par3); 434 Item.itemsList[this.itemID].onCreated(this, par1World, par2EntityPlayer); 435 } 436 437 public int getMaxItemUseDuration() 438 { 439 return this.getItem().getMaxItemUseDuration(this); 440 } 441 442 public EnumAction getItemUseAction() 443 { 444 return this.getItem().getItemUseAction(this); 445 } 446 447 /** 448 * Called when the player releases the use item button. Args: world, entityplayer, itemInUseCount 449 */ 450 public void onPlayerStoppedUsing(World par1World, EntityPlayer par2EntityPlayer, int par3) 451 { 452 this.getItem().onPlayerStoppedUsing(this, par1World, par2EntityPlayer, par3); 453 } 454 455 /** 456 * Returns true if the ItemStack has an NBTTagCompound. Currently used to store enchantments. 457 */ 458 public boolean hasTagCompound() 459 { 460 return this.stackTagCompound != null; 461 } 462 463 /** 464 * Returns the NBTTagCompound of the ItemStack. 465 */ 466 public NBTTagCompound getTagCompound() 467 { 468 return this.stackTagCompound; 469 } 470 471 public NBTTagList getEnchantmentTagList() 472 { 473 return this.stackTagCompound == null ? null : (NBTTagList)this.stackTagCompound.getTag("ench"); 474 } 475 476 /** 477 * Assigns a NBTTagCompound to the ItemStack, minecraft validates that only non-stackable items can have it. 478 */ 479 public void setTagCompound(NBTTagCompound par1NBTTagCompound) 480 { 481 this.stackTagCompound = par1NBTTagCompound; 482 } 483 484 /** 485 * returns the display name of the itemstack 486 */ 487 public String getDisplayName() 488 { 489 String var1 = this.getItem().getItemDisplayName(this); 490 491 if (this.stackTagCompound != null && this.stackTagCompound.hasKey("display")) 492 { 493 NBTTagCompound var2 = this.stackTagCompound.getCompoundTag("display"); 494 495 if (var2.hasKey("Name")) 496 { 497 var1 = var2.getString("Name"); 498 } 499 } 500 501 return var1; 502 } 503 504 /** 505 * Sets the item's name (used by anvil to rename the items). 506 */ 507 public void setItemName(String par1Str) 508 { 509 if (this.stackTagCompound == null) 510 { 511 this.stackTagCompound = new NBTTagCompound(); 512 } 513 514 if (!this.stackTagCompound.hasKey("display")) 515 { 516 this.stackTagCompound.setCompoundTag("display", new NBTTagCompound()); 517 } 518 519 this.stackTagCompound.getCompoundTag("display").setString("Name", par1Str); 520 } 521 522 /** 523 * Returns true if the itemstack has a display name 524 */ 525 public boolean hasDisplayName() 526 { 527 return this.stackTagCompound == null ? false : (!this.stackTagCompound.hasKey("display") ? false : this.stackTagCompound.getCompoundTag("display").hasKey("Name")); 528 } 529 530 @SideOnly(Side.CLIENT) 531 532 /** 533 * Return a list of strings containing information about the item 534 */ 535 public List getTooltip(EntityPlayer par1EntityPlayer, boolean par2) 536 { 537 ArrayList var3 = new ArrayList(); 538 Item var4 = Item.itemsList[this.itemID]; 539 String var5 = this.getDisplayName(); 540 541 if (this.hasDisplayName()) 542 { 543 var5 = "\u00a7o" + var5 + "\u00a7r"; 544 } 545 546 if (par2) 547 { 548 String var6 = ""; 549 550 if (var5.length() > 0) 551 { 552 var5 = var5 + " ("; 553 var6 = ")"; 554 } 555 556 if (this.getHasSubtypes()) 557 { 558 var5 = var5 + String.format("#%04d/%d%s", new Object[] {Integer.valueOf(this.itemID), Integer.valueOf(this.itemDamage), var6}); 559 } 560 else 561 { 562 var5 = var5 + String.format("#%04d%s", new Object[] {Integer.valueOf(this.itemID), var6}); 563 } 564 } 565 else if (!this.hasDisplayName() && this.itemID == Item.map.shiftedIndex) 566 { 567 var5 = var5 + " #" + this.itemDamage; 568 } 569 570 var3.add(var5); 571 var4.addInformation(this, par1EntityPlayer, var3, par2); 572 573 if (this.hasTagCompound()) 574 { 575 NBTTagList var10 = this.getEnchantmentTagList(); 576 577 if (var10 != null) 578 { 579 for (int var7 = 0; var7 < var10.tagCount(); ++var7) 580 { 581 short var8 = ((NBTTagCompound)var10.tagAt(var7)).getShort("id"); 582 short var9 = ((NBTTagCompound)var10.tagAt(var7)).getShort("lvl"); 583 584 if (Enchantment.enchantmentsList[var8] != null) 585 { 586 var3.add(Enchantment.enchantmentsList[var8].getTranslatedName(var9)); 587 } 588 } 589 } 590 591 if (this.stackTagCompound.hasKey("display")) 592 { 593 NBTTagCompound var11 = this.stackTagCompound.getCompoundTag("display"); 594 595 if (var11.hasKey("color")) 596 { 597 if (par2) 598 { 599 var3.add("Color: #" + Integer.toHexString(var11.getInteger("color")).toUpperCase()); 600 } 601 else 602 { 603 var3.add("\u00a7o" + StatCollector.translateToLocal("item.dyed")); 604 } 605 } 606 607 if (var11.hasKey("Lore")) 608 { 609 NBTTagList var12 = var11.getTagList("Lore"); 610 611 if (var12.tagCount() > 0) 612 { 613 for (int var13 = 0; var13 < var12.tagCount(); ++var13) 614 { 615 var3.add("\u00a75\u00a7o" + ((NBTTagString)var12.tagAt(var13)).data); 616 } 617 } 618 } 619 } 620 } 621 622 if (par2 && this.isItemDamaged()) 623 { 624 var3.add("Durability: " + (this.getMaxDamage() - this.getItemDamageForDisplay()) + " / " + this.getMaxDamage()); 625 } 626 627 return var3; 628 } 629 630 @SideOnly(Side.CLIENT) 631 public boolean hasEffect() 632 { 633 return this.getItem().hasEffect(this); 634 } 635 636 @SideOnly(Side.CLIENT) 637 public EnumRarity getRarity() 638 { 639 return this.getItem().getRarity(this); 640 } 641 642 /** 643 * True if it is a tool and has no enchantments to begin with 644 */ 645 public boolean isItemEnchantable() 646 { 647 return !this.getItem().isItemTool(this) ? false : !this.isItemEnchanted(); 648 } 649 650 /** 651 * Adds an enchantment with a desired level on the ItemStack. 652 */ 653 public void addEnchantment(Enchantment par1Enchantment, int par2) 654 { 655 if (this.stackTagCompound == null) 656 { 657 this.setTagCompound(new NBTTagCompound()); 658 } 659 660 if (!this.stackTagCompound.hasKey("ench")) 661 { 662 this.stackTagCompound.setTag("ench", new NBTTagList("ench")); 663 } 664 665 NBTTagList var3 = (NBTTagList)this.stackTagCompound.getTag("ench"); 666 NBTTagCompound var4 = new NBTTagCompound(); 667 var4.setShort("id", (short)par1Enchantment.effectId); 668 var4.setShort("lvl", (short)((byte)par2)); 669 var3.appendTag(var4); 670 } 671 672 /** 673 * True if the item has enchantment data 674 */ 675 public boolean isItemEnchanted() 676 { 677 return this.stackTagCompound != null && this.stackTagCompound.hasKey("ench"); 678 } 679 680 public void setTagInfo(String par1Str, NBTBase par2NBTBase) 681 { 682 if (this.stackTagCompound == null) 683 { 684 this.setTagCompound(new NBTTagCompound()); 685 } 686 687 this.stackTagCompound.setTag(par1Str, par2NBTBase); 688 } 689 690 public boolean func_82835_x() 691 { 692 return this.getItem().func_82788_x(); 693 } 694 695 /** 696 * Return whether this stack is on an item frame. 697 */ 698 public boolean isOnItemFrame() 699 { 700 return this.itemFrame != null; 701 } 702 703 /** 704 * Set the item frame this stack is on. 705 */ 706 public void setItemFrame(EntityItemFrame par1EntityItemFrame) 707 { 708 this.itemFrame = par1EntityItemFrame; 709 } 710 711 /** 712 * Return the item frame this stack is on. Returns null if not on an item frame. 713 */ 714 public EntityItemFrame getItemFrame() 715 { 716 return this.itemFrame; 717 } 718 719 /** 720 * Get this stack's repair cost, or 0 if no repair cost is defined. 721 */ 722 public int getRepairCost() 723 { 724 return this.hasTagCompound() && this.stackTagCompound.hasKey("RepairCost") ? this.stackTagCompound.getInteger("RepairCost") : 0; 725 } 726 727 /** 728 * Set this stack's repair cost. 729 */ 730 public void setRepairCost(int par1) 731 { 732 if (!this.hasTagCompound()) 733 { 734 this.stackTagCompound = new NBTTagCompound(); 735 } 736 737 this.stackTagCompound.setInteger("RepairCost", par1); 738 } 739 }