001package net.minecraft.entity.player;
002
003import cpw.mods.fml.relauncher.Side;
004import cpw.mods.fml.relauncher.SideOnly;
005import net.minecraft.block.Block;
006import net.minecraft.crash.CrashReport;
007import net.minecraft.crash.CrashReportCategory;
008import net.minecraft.entity.Entity;
009import net.minecraft.inventory.IInventory;
010import net.minecraft.item.Item;
011import net.minecraft.item.ItemArmor;
012import net.minecraft.item.ItemStack;
013import net.minecraft.nbt.NBTTagCompound;
014import net.minecraft.nbt.NBTTagList;
015import net.minecraft.util.ReportedException;
016
017public class InventoryPlayer implements IInventory
018{
019    /**
020     * An array of 36 item stacks indicating the main player inventory (including the visible bar).
021     */
022    public ItemStack[] mainInventory = new ItemStack[36];
023
024    /** An array of 4 item stacks containing the currently worn armor pieces. */
025    public ItemStack[] armorInventory = new ItemStack[4];
026
027    /** The index of the currently held item (0-8). */
028    public int currentItem = 0;
029    @SideOnly(Side.CLIENT)
030
031    /** The current ItemStack. */
032    private ItemStack currentItemStack;
033
034    /** The player whose inventory this is. */
035    public EntityPlayer player;
036    private ItemStack itemStack;
037
038    /**
039     * Set true whenever the inventory changes. Nothing sets it false so you will have to write your own code to check
040     * it and reset the value.
041     */
042    public boolean inventoryChanged = false;
043
044    public InventoryPlayer(EntityPlayer par1EntityPlayer)
045    {
046        this.player = par1EntityPlayer;
047    }
048
049    /**
050     * Returns the item stack currently held by the player.
051     */
052    public ItemStack getCurrentItem()
053    {
054        return this.currentItem < 9 && this.currentItem >= 0 ? this.mainInventory[this.currentItem] : null;
055    }
056
057    /**
058     * Get the size of the player hotbar inventory
059     */
060    public static int getHotbarSize()
061    {
062        return 9;
063    }
064
065    /**
066     * Returns a slot index in main inventory containing a specific itemID
067     */
068    private int getInventorySlotContainItem(int par1)
069    {
070        for (int j = 0; j < this.mainInventory.length; ++j)
071        {
072            if (this.mainInventory[j] != null && this.mainInventory[j].itemID == par1)
073            {
074                return j;
075            }
076        }
077
078        return -1;
079    }
080
081    @SideOnly(Side.CLIENT)
082    private int getInventorySlotContainItemAndDamage(int par1, int par2)
083    {
084        for (int k = 0; k < this.mainInventory.length; ++k)
085        {
086            if (this.mainInventory[k] != null && this.mainInventory[k].itemID == par1 && this.mainInventory[k].getItemDamage() == par2)
087            {
088                return k;
089            }
090        }
091
092        return -1;
093    }
094
095    /**
096     * stores an itemstack in the users inventory
097     */
098    private int storeItemStack(ItemStack par1ItemStack)
099    {
100        for (int i = 0; i < this.mainInventory.length; ++i)
101        {
102            if (this.mainInventory[i] != null && this.mainInventory[i].itemID == par1ItemStack.itemID && this.mainInventory[i].isStackable() && this.mainInventory[i].stackSize < this.mainInventory[i].getMaxStackSize() && this.mainInventory[i].stackSize < this.getInventoryStackLimit() && (!this.mainInventory[i].getHasSubtypes() || this.mainInventory[i].getItemDamage() == par1ItemStack.getItemDamage()) && ItemStack.areItemStackTagsEqual(this.mainInventory[i], par1ItemStack))
103            {
104                return i;
105            }
106        }
107
108        return -1;
109    }
110
111    /**
112     * Returns the first item stack that is empty.
113     */
114    public int getFirstEmptyStack()
115    {
116        for (int i = 0; i < this.mainInventory.length; ++i)
117        {
118            if (this.mainInventory[i] == null)
119            {
120                return i;
121            }
122        }
123
124        return -1;
125    }
126
127    @SideOnly(Side.CLIENT)
128
129    /**
130     * Sets a specific itemID as the current item being held (only if it exists on the hotbar)
131     */
132    public void setCurrentItem(int par1, int par2, boolean par3, boolean par4)
133    {
134        boolean flag2 = true;
135        this.currentItemStack = this.getCurrentItem();
136        int k;
137
138        if (par3)
139        {
140            k = this.getInventorySlotContainItemAndDamage(par1, par2);
141        }
142        else
143        {
144            k = this.getInventorySlotContainItem(par1);
145        }
146
147        if (k >= 0 && k < 9)
148        {
149            this.currentItem = k;
150        }
151        else
152        {
153            if (par4 && par1 > 0)
154            {
155                int l = this.getFirstEmptyStack();
156
157                if (l >= 0 && l < 9)
158                {
159                    this.currentItem = l;
160                }
161
162                this.func_70439_a(Item.itemsList[par1], par2);
163            }
164        }
165    }
166
167    @SideOnly(Side.CLIENT)
168
169    /**
170     * Switch the current item to the next one or the previous one
171     */
172    public void changeCurrentItem(int par1)
173    {
174        if (par1 > 0)
175        {
176            par1 = 1;
177        }
178
179        if (par1 < 0)
180        {
181            par1 = -1;
182        }
183
184        for (this.currentItem -= par1; this.currentItem < 0; this.currentItem += 9)
185        {
186            ;
187        }
188
189        while (this.currentItem >= 9)
190        {
191            this.currentItem -= 9;
192        }
193    }
194
195    /**
196     * Clear this player's inventory, using the specified ID and metadata as filters or -1 for no filter.
197     */
198    public int clearInventory(int par1, int par2)
199    {
200        int k = 0;
201        int l;
202        ItemStack itemstack;
203
204        for (l = 0; l < this.mainInventory.length; ++l)
205        {
206            itemstack = this.mainInventory[l];
207
208            if (itemstack != null && (par1 <= -1 || itemstack.itemID == par1) && (par2 <= -1 || itemstack.getItemDamage() == par2))
209            {
210                k += itemstack.stackSize;
211                this.mainInventory[l] = null;
212            }
213        }
214
215        for (l = 0; l < this.armorInventory.length; ++l)
216        {
217            itemstack = this.armorInventory[l];
218
219            if (itemstack != null && (par1 <= -1 || itemstack.itemID == par1) && (par2 <= -1 || itemstack.getItemDamage() == par2))
220            {
221                k += itemstack.stackSize;
222                this.armorInventory[l] = null;
223            }
224        }
225
226        return k;
227    }
228
229    @SideOnly(Side.CLIENT)
230    public void func_70439_a(Item par1Item, int par2)
231    {
232        if (par1Item != null)
233        {
234            int j = this.getInventorySlotContainItemAndDamage(par1Item.itemID, par2);
235
236            if (j >= 0)
237            {
238                this.mainInventory[j] = this.mainInventory[this.currentItem];
239            }
240
241            if (this.currentItemStack != null && this.currentItemStack.isItemEnchantable() && this.getInventorySlotContainItemAndDamage(this.currentItemStack.itemID, this.currentItemStack.getItemDamageForDisplay()) == this.currentItem)
242            {
243                return;
244            }
245
246            this.mainInventory[this.currentItem] = new ItemStack(Item.itemsList[par1Item.itemID], 1, par2);
247        }
248    }
249
250    /**
251     * This function stores as many items of an ItemStack as possible in a matching slot and returns the quantity of
252     * left over items.
253     */
254    private int storePartialItemStack(ItemStack par1ItemStack)
255    {
256        int i = par1ItemStack.itemID;
257        int j = par1ItemStack.stackSize;
258        int k;
259
260        if (par1ItemStack.getMaxStackSize() == 1)
261        {
262            k = this.getFirstEmptyStack();
263
264            if (k < 0)
265            {
266                return j;
267            }
268            else
269            {
270                if (this.mainInventory[k] == null)
271                {
272                    this.mainInventory[k] = ItemStack.copyItemStack(par1ItemStack);
273                }
274
275                return 0;
276            }
277        }
278        else
279        {
280            k = this.storeItemStack(par1ItemStack);
281
282            if (k < 0)
283            {
284                k = this.getFirstEmptyStack();
285            }
286
287            if (k < 0)
288            {
289                return j;
290            }
291            else
292            {
293                if (this.mainInventory[k] == null)
294                {
295                    this.mainInventory[k] = new ItemStack(i, 0, par1ItemStack.getItemDamage());
296
297                    if (par1ItemStack.hasTagCompound())
298                    {
299                        this.mainInventory[k].setTagCompound((NBTTagCompound)par1ItemStack.getTagCompound().copy());
300                    }
301                }
302
303                int l = j;
304
305                if (j > this.mainInventory[k].getMaxStackSize() - this.mainInventory[k].stackSize)
306                {
307                    l = this.mainInventory[k].getMaxStackSize() - this.mainInventory[k].stackSize;
308                }
309
310                if (l > this.getInventoryStackLimit() - this.mainInventory[k].stackSize)
311                {
312                    l = this.getInventoryStackLimit() - this.mainInventory[k].stackSize;
313                }
314
315                if (l == 0)
316                {
317                    return j;
318                }
319                else
320                {
321                    j -= l;
322                    this.mainInventory[k].stackSize += l;
323                    this.mainInventory[k].animationsToGo = 5;
324                    return j;
325                }
326            }
327        }
328    }
329
330    /**
331     * Decrement the number of animations remaining. Only called on client side. This is used to handle the animation of
332     * receiving a block.
333     */
334    public void decrementAnimations()
335    {
336        for (int i = 0; i < this.mainInventory.length; ++i)
337        {
338            if (this.mainInventory[i] != null)
339            {
340                this.mainInventory[i].updateAnimation(this.player.worldObj, this.player, i, this.currentItem == i);
341            }
342        }
343
344        for (int i = 0; i < this.armorInventory.length; i++)
345        {
346            if (this.armorInventory[i] != null)
347            {
348                this.armorInventory[i].getItem().onArmorTickUpdate(this.player.worldObj, this.player, this.armorInventory[i]);
349            }
350        }
351    }
352
353    /**
354     * removed one item of specified itemID from inventory (if it is in a stack, the stack size will reduce with 1)
355     */
356    public boolean consumeInventoryItem(int par1)
357    {
358        int j = this.getInventorySlotContainItem(par1);
359
360        if (j < 0)
361        {
362            return false;
363        }
364        else
365        {
366            if (--this.mainInventory[j].stackSize <= 0)
367            {
368                this.mainInventory[j] = null;
369            }
370
371            return true;
372        }
373    }
374
375    /**
376     * Get if a specifiied item id is inside the inventory.
377     */
378    public boolean hasItem(int par1)
379    {
380        int j = this.getInventorySlotContainItem(par1);
381        return j >= 0;
382    }
383
384    /**
385     * Adds the item stack to the inventory, returns false if it is impossible.
386     */
387    public boolean addItemStackToInventory(ItemStack par1ItemStack)
388    {
389        if (par1ItemStack == null)
390        {
391            return false;
392        }
393        else
394        {
395            try
396            {
397                int i;
398
399                if (par1ItemStack.isItemDamaged())
400                {
401                    i = this.getFirstEmptyStack();
402
403                    if (i >= 0)
404                    {
405                        this.mainInventory[i] = ItemStack.copyItemStack(par1ItemStack);
406                        this.mainInventory[i].animationsToGo = 5;
407                        par1ItemStack.stackSize = 0;
408                        return true;
409                    }
410                    else if (this.player.capabilities.isCreativeMode)
411                    {
412                        par1ItemStack.stackSize = 0;
413                        return true;
414                    }
415                    else
416                    {
417                        return false;
418                    }
419                }
420                else
421                {
422                    do
423                    {
424                        i = par1ItemStack.stackSize;
425                        par1ItemStack.stackSize = this.storePartialItemStack(par1ItemStack);
426                    }
427                    while (par1ItemStack.stackSize > 0 && par1ItemStack.stackSize < i);
428
429                    if (par1ItemStack.stackSize == i && this.player.capabilities.isCreativeMode)
430                    {
431                        par1ItemStack.stackSize = 0;
432                        return true;
433                    }
434                    else
435                    {
436                        return par1ItemStack.stackSize < i;
437                    }
438                }
439            }
440            catch (Throwable throwable)
441            {
442                CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Adding item to inventory");
443                CrashReportCategory crashreportcategory = crashreport.makeCategory("Item being added");
444                crashreportcategory.addCrashSection("Item ID", Integer.valueOf(par1ItemStack.itemID));
445                crashreportcategory.addCrashSection("Item data", Integer.valueOf(par1ItemStack.getItemDamage()));
446                crashreportcategory.addCrashSectionCallable("Item name", new CallableItemName(this, par1ItemStack));
447                throw new ReportedException(crashreport);
448            }
449        }
450    }
451
452    /**
453     * Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a
454     * new stack.
455     */
456    public ItemStack decrStackSize(int par1, int par2)
457    {
458        ItemStack[] aitemstack = this.mainInventory;
459
460        if (par1 >= this.mainInventory.length)
461        {
462            aitemstack = this.armorInventory;
463            par1 -= this.mainInventory.length;
464        }
465
466        if (aitemstack[par1] != null)
467        {
468            ItemStack itemstack;
469
470            if (aitemstack[par1].stackSize <= par2)
471            {
472                itemstack = aitemstack[par1];
473                aitemstack[par1] = null;
474                return itemstack;
475            }
476            else
477            {
478                itemstack = aitemstack[par1].splitStack(par2);
479
480                if (aitemstack[par1].stackSize == 0)
481                {
482                    aitemstack[par1] = null;
483                }
484
485                return itemstack;
486            }
487        }
488        else
489        {
490            return null;
491        }
492    }
493
494    /**
495     * When some containers are closed they call this on each slot, then drop whatever it returns as an EntityItem -
496     * like when you close a workbench GUI.
497     */
498    public ItemStack getStackInSlotOnClosing(int par1)
499    {
500        ItemStack[] aitemstack = this.mainInventory;
501
502        if (par1 >= this.mainInventory.length)
503        {
504            aitemstack = this.armorInventory;
505            par1 -= this.mainInventory.length;
506        }
507
508        if (aitemstack[par1] != null)
509        {
510            ItemStack itemstack = aitemstack[par1];
511            aitemstack[par1] = null;
512            return itemstack;
513        }
514        else
515        {
516            return null;
517        }
518    }
519
520    /**
521     * Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections).
522     */
523    public void setInventorySlotContents(int par1, ItemStack par2ItemStack)
524    {
525        ItemStack[] aitemstack = this.mainInventory;
526
527        if (par1 >= aitemstack.length)
528        {
529            par1 -= aitemstack.length;
530            aitemstack = this.armorInventory;
531        }
532
533        aitemstack[par1] = par2ItemStack;
534    }
535
536    /**
537     * Gets the strength of the current item (tool) against the specified block, 1.0f if not holding anything.
538     */
539    public float getStrVsBlock(Block par1Block)
540    {
541        float f = 1.0F;
542
543        if (this.mainInventory[this.currentItem] != null)
544        {
545            f *= this.mainInventory[this.currentItem].getStrVsBlock(par1Block);
546        }
547
548        return f;
549    }
550
551    /**
552     * Writes the inventory out as a list of compound tags. This is where the slot indices are used (+100 for armor, +80
553     * for crafting).
554     */
555    public NBTTagList writeToNBT(NBTTagList par1NBTTagList)
556    {
557        int i;
558        NBTTagCompound nbttagcompound;
559
560        for (i = 0; i < this.mainInventory.length; ++i)
561        {
562            if (this.mainInventory[i] != null)
563            {
564                nbttagcompound = new NBTTagCompound();
565                nbttagcompound.setByte("Slot", (byte)i);
566                this.mainInventory[i].writeToNBT(nbttagcompound);
567                par1NBTTagList.appendTag(nbttagcompound);
568            }
569        }
570
571        for (i = 0; i < this.armorInventory.length; ++i)
572        {
573            if (this.armorInventory[i] != null)
574            {
575                nbttagcompound = new NBTTagCompound();
576                nbttagcompound.setByte("Slot", (byte)(i + 100));
577                this.armorInventory[i].writeToNBT(nbttagcompound);
578                par1NBTTagList.appendTag(nbttagcompound);
579            }
580        }
581
582        return par1NBTTagList;
583    }
584
585    /**
586     * Reads from the given tag list and fills the slots in the inventory with the correct items.
587     */
588    public void readFromNBT(NBTTagList par1NBTTagList)
589    {
590        this.mainInventory = new ItemStack[36];
591        this.armorInventory = new ItemStack[4];
592
593        for (int i = 0; i < par1NBTTagList.tagCount(); ++i)
594        {
595            NBTTagCompound nbttagcompound = (NBTTagCompound)par1NBTTagList.tagAt(i);
596            int j = nbttagcompound.getByte("Slot") & 255;
597            ItemStack itemstack = ItemStack.loadItemStackFromNBT(nbttagcompound);
598
599            if (itemstack != null)
600            {
601                if (j >= 0 && j < this.mainInventory.length)
602                {
603                    this.mainInventory[j] = itemstack;
604                }
605
606                if (j >= 100 && j < this.armorInventory.length + 100)
607                {
608                    this.armorInventory[j - 100] = itemstack;
609                }
610            }
611        }
612    }
613
614    /**
615     * Returns the number of slots in the inventory.
616     */
617    public int getSizeInventory()
618    {
619        return this.mainInventory.length + 4;
620    }
621
622    /**
623     * Returns the stack in slot i
624     */
625    public ItemStack getStackInSlot(int par1)
626    {
627        ItemStack[] aitemstack = this.mainInventory;
628
629        if (par1 >= aitemstack.length)
630        {
631            par1 -= aitemstack.length;
632            aitemstack = this.armorInventory;
633        }
634
635        return aitemstack[par1];
636    }
637
638    /**
639     * Returns the name of the inventory.
640     */
641    public String getInvName()
642    {
643        return "container.inventory";
644    }
645
646    /**
647     * If this returns false, the inventory name will be used as an unlocalized name, and translated into the player's
648     * language. Otherwise it will be used directly.
649     */
650    public boolean isInvNameLocalized()
651    {
652        return false;
653    }
654
655    /**
656     * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't
657     * this more of a set than a get?*
658     */
659    public int getInventoryStackLimit()
660    {
661        return 64;
662    }
663
664    /**
665     * Return damage vs an entity done by the current held weapon, or 1 if nothing is held
666     */
667    public int getDamageVsEntity(Entity par1Entity)
668    {
669        ItemStack itemstack = this.getStackInSlot(this.currentItem);
670        return itemstack != null ? itemstack.getDamageVsEntity(par1Entity) : 1;
671    }
672
673    /**
674     * Returns whether the current item (tool) can harvest from the specified block (actually get a result).
675     */
676    public boolean canHarvestBlock(Block par1Block)
677    {
678        if (par1Block.blockMaterial.isToolNotRequired())
679        {
680            return true;
681        }
682        else
683        {
684            ItemStack itemstack = this.getStackInSlot(this.currentItem);
685            return itemstack != null ? itemstack.canHarvestBlock(par1Block) : false;
686        }
687    }
688
689    /**
690     * returns a player armor item (as itemstack) contained in specified armor slot.
691     */
692    public ItemStack armorItemInSlot(int par1)
693    {
694        return this.armorInventory[par1];
695    }
696
697    /**
698     * Based on the damage values and maximum damage values of each armor item, returns the current armor value.
699     */
700    public int getTotalArmorValue()
701    {
702        int i = 0;
703
704        for (int j = 0; j < this.armorInventory.length; ++j)
705        {
706            if (this.armorInventory[j] != null && this.armorInventory[j].getItem() instanceof ItemArmor)
707            {
708                int k = ((ItemArmor)this.armorInventory[j].getItem()).damageReduceAmount;
709                i += k;
710            }
711        }
712
713        return i;
714    }
715
716    /**
717     * Damages armor in each slot by the specified amount.
718     */
719    public void damageArmor(int par1)
720    {
721        par1 /= 4;
722
723        if (par1 < 1)
724        {
725            par1 = 1;
726        }
727
728        for (int j = 0; j < this.armorInventory.length; ++j)
729        {
730            if (this.armorInventory[j] != null && this.armorInventory[j].getItem() instanceof ItemArmor)
731            {
732                this.armorInventory[j].damageItem(par1, this.player);
733
734                if (this.armorInventory[j].stackSize == 0)
735                {
736                    this.armorInventory[j] = null;
737                }
738            }
739        }
740    }
741
742    /**
743     * Drop all armor and main inventory items.
744     */
745    public void dropAllItems()
746    {
747        int i;
748
749        for (i = 0; i < this.mainInventory.length; ++i)
750        {
751            if (this.mainInventory[i] != null)
752            {
753                this.player.dropPlayerItemWithRandomChoice(this.mainInventory[i], true);
754                this.mainInventory[i] = null;
755            }
756        }
757
758        for (i = 0; i < this.armorInventory.length; ++i)
759        {
760            if (this.armorInventory[i] != null)
761            {
762                this.player.dropPlayerItemWithRandomChoice(this.armorInventory[i], true);
763                this.armorInventory[i] = null;
764            }
765        }
766    }
767
768    /**
769     * Called when an the contents of an Inventory change, usually
770     */
771    public void onInventoryChanged()
772    {
773        this.inventoryChanged = true;
774    }
775
776    public void setItemStack(ItemStack par1ItemStack)
777    {
778        this.itemStack = par1ItemStack;
779    }
780
781    public ItemStack getItemStack()
782    {
783        return this.itemStack;
784    }
785
786    /**
787     * Do not make give this method the name canInteractWith because it clashes with Container
788     */
789    public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer)
790    {
791        return this.player.isDead ? false : par1EntityPlayer.getDistanceSqToEntity(this.player) <= 64.0D;
792    }
793
794    /**
795     * Returns true if the specified ItemStack exists in the inventory.
796     */
797    public boolean hasItemStack(ItemStack par1ItemStack)
798    {
799        int i;
800
801        for (i = 0; i < this.armorInventory.length; ++i)
802        {
803            if (this.armorInventory[i] != null && this.armorInventory[i].isItemEqual(par1ItemStack))
804            {
805                return true;
806            }
807        }
808
809        for (i = 0; i < this.mainInventory.length; ++i)
810        {
811            if (this.mainInventory[i] != null && this.mainInventory[i].isItemEqual(par1ItemStack))
812            {
813                return true;
814            }
815        }
816
817        return false;
818    }
819
820    public void openChest() {}
821
822    public void closeChest() {}
823
824    /**
825     * Returns true if automation is allowed to insert the given stack (ignoring stack size) into the given slot.
826     */
827    public boolean isStackValidForSlot(int par1, ItemStack par2ItemStack)
828    {
829        return true;
830    }
831
832    /**
833     * Copy the ItemStack contents from another InventoryPlayer instance
834     */
835    public void copyInventory(InventoryPlayer par1InventoryPlayer)
836    {
837        int i;
838
839        for (i = 0; i < this.mainInventory.length; ++i)
840        {
841            this.mainInventory[i] = ItemStack.copyItemStack(par1InventoryPlayer.mainInventory[i]);
842        }
843
844        for (i = 0; i < this.armorInventory.length; ++i)
845        {
846            this.armorInventory[i] = ItemStack.copyItemStack(par1InventoryPlayer.armorInventory[i]);
847        }
848
849        this.currentItem = par1InventoryPlayer.currentItem;
850    }
851}