package electroblob.wizardry.util; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.inventory.EntityEquipmentSlot.Type; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHandSide; import net.minecraft.util.NonNullList; import net.minecraft.world.World; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Contains useful static methods for interacting with item stacks and entity inventories. These methods used to be part * of {@code WizardryUtilities}. * @author Electroblob * @since Wizardry 4.3 */ public final class InventoryUtils { private InventoryUtils(){} // No instances! /** Constant which is simply an array of the four armour slots. (Could've sworn this exists somewhere in vanilla, * but I can't find it anywhere...) */ public static final EntityEquipmentSlot[] ARMOUR_SLOTS; static { // The list of slots needs to be mutable. List slots = new ArrayList<>(Arrays.asList(EntityEquipmentSlot.values())); slots.removeIf(slot -> slot.getSlotType() != Type.ARMOR); ARMOUR_SLOTS = slots.toArray(new EntityEquipmentSlot[0]); } /** * Returns a list of the itemstacks in the given player's hotbar. Defined here for convenience and to centralise the * (unfortunately unavoidable) use of hardcoded numbers to reference the inventory slots. The returned list is a * modifiable copy of part of the player's inventory stack list; as such, changes to the list are not written * through to the player's inventory. However, the ItemStack instances themselves are not copied, so changes to any * of their fields (size, metadata...) will change those in the player's inventory. * * @since Wizardry 1.2 */ public static List getHotbar(EntityPlayer player){ NonNullList hotbar = NonNullList.create(); hotbar.addAll(player.inventory.mainInventory.subList(0, 9)); return hotbar; } /** * Returns a list of the itemstacks in the given player's hotbar and offhand, sorted into the following order: main * hand, offhand, rest of hotbar left-to-right. The returned list is a modifiable shallow copy of part of the player's * inventory stack list; as such, changes to the list are not written through to the player's inventory. * However, the ItemStack instances themselves are not copied, so changes to any of their fields (size, metadata...) * will change those in the player's inventory. * * @since Wizardry 1.2 */ public static List getPrioritisedHotbarAndOffhand(EntityPlayer player){ List hotbar = getHotbar(player); // Adds the offhand item to the beginning of the list so it is processed before the hotbar hotbar.add(0, player.getHeldItemOffhand()); // Moves the item in the main hand to the beginning of the list so it is processed first hotbar.remove(player.getHeldItemMainhand()); hotbar.add(0, player.getHeldItemMainhand()); return hotbar; } /** Returns which {@link EnumHandSide} the given {@link EnumHand} is on for the given entity. */ public static EnumHandSide getSideForHand(EntityLivingBase entity, EnumHand hand){ return hand == EnumHand.MAIN_HAND ? entity.getPrimaryHand() : entity.getPrimaryHand().opposite(); } /** Returns which {@link EnumHand} is on the given {@link EnumHandSide} for the given entity. */ public static EnumHand getHandForSide(EntityLivingBase entity, EnumHandSide side){ return side == entity.getPrimaryHand() ? EnumHand.MAIN_HAND : EnumHand.OFF_HAND; } /** Returns the opposite {@link EnumHand} to the one given. */ public static EnumHand getOpposite(EnumHand hand){ return hand == EnumHand.OFF_HAND ? EnumHand.MAIN_HAND : EnumHand.OFF_HAND; } /** * Tests whether the specified player has any of the specified item in their entire inventory, including armour * slots and offhand. */ public static boolean doesPlayerHaveItem(EntityPlayer player, Item item){ for(ItemStack stack : player.inventory.mainInventory){ if(stack.getItem() == item){ return true; } } for(ItemStack stack : player.inventory.armorInventory){ if(stack.getItem() == item){ return true; } } for(ItemStack stack : player.inventory.offHandInventory){ if(stack.getItem() == item){ return true; } } return false; } /** * Returns a new {@link ItemStack} that is identical to the supplied one, except with the metadata changed to the * new value given. * @param toCopy The stack to copy * @param newMetadata The new metadata value * @return The resulting {@link ItemStack} */ public static ItemStack copyWithMeta(ItemStack toCopy, int newMetadata){ ItemStack copy = new ItemStack(toCopy.getItem(), toCopy.getCount(), newMetadata); NBTTagCompound compound = toCopy.getTagCompound(); if(compound != null) copy.setTagCompound(compound.copy()); return copy; } /** * Returns whether the two given item stacks can be merged, i.e. if they both contain the same (stackable) item, * metadata and NBT. Importantly, the number of items in each stack need not be the same. No actual merging is * performed by this method; the input stacks will not be modified. * @param stack1 The first stack to be tested for mergeability * @param stack2 The second stack to be tested for mergeability (order does not matter) * @return True if the two stacks can be merged, false if not */ public static boolean canMerge(ItemStack stack1, ItemStack stack2){ return !stack1.isEmpty() && !stack2.isEmpty() && stack1.isStackable() && stack2.isStackable() && stack1.getItem() == stack2.getItem() && (!stack1.getHasSubtypes() || stack1.getMetadata() == stack2.getMetadata()) && ItemStack.areItemStackTagsEqual(stack1, stack2); } /** * A version of {@link Entity#replaceItemInInventory(int, ItemStack)} that takes a slot index specific to the main, * armour and offhand inventories, as passed to {@link Item#onUpdate(ItemStack, World, Entity, int, boolean)}, * rather than the proper slot index used everywhere else (just why, Mojang?). * @param entity The entity to replace the item for * @param slot The slot index to replace * @param original The item stack currently in the slot (required for technical reasons) * @param replacement The new item stack * @return True if an item was replaced, false if not */ public static boolean replaceItemInInventory(Entity entity, int slot, ItemStack original, ItemStack replacement){ // Check slots that aren't in the main inventory first by comparing with the existing item if(entity instanceof EntityLivingBase){ for(EntityEquipmentSlot eslot : EntityEquipmentSlot.values()){ if(((EntityLivingBase)entity).getItemStackFromSlot(eslot) == original){ entity.setItemStackToSlot(eslot, replacement); return true; } } } // Otherwise use the normal behaviour return entity.replaceItemInInventory(slot, replacement); } }