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 }