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