org.blockartistry.mod.ThermalRecycling.util.ItemStackHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.blockartistry.mod.ThermalRecycling.util.ItemStackHelper.java

Source

/*
 * This file is part of ThermalRecycling, licensed under the MIT License (MIT).
 *
 * Copyright (c) OreCruncher
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.blockartistry.mod.ThermalRecycling.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.blockartistry.mod.ThermalRecycling.ModLog;

import cofh.lib.util.helpers.ItemHelper;

import com.google.common.collect.ImmutableMap;

import cpw.mods.fml.common.registry.GameData;
import net.minecraft.block.Block;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagString;
import net.minecraft.world.World;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.oredict.OreDictionary;

public final class ItemStackHelper {

    protected static final Random rand = XorShiftRandom.shared;
    protected static Map<String, ItemStack> preferred = new HashMap<String, ItemStack>();

    static final Item materialBase = GameData.getItemRegistry().getObject("ThermalFoundation:material");
    static final Item storageBase = GameData.getItemRegistry().getObject("ThermalFoundation:Storage");
    static final Item materialBaseTE = GameData.getItemRegistry().getObject("ThermalExpansion:material");

    public static final ItemStack dustIron = new ItemStack(materialBase, 1, 0);
    public static final ItemStack dustGold = new ItemStack(materialBase, 1, 1);
    public static final ItemStack dustCopper = new ItemStack(materialBase, 1, 32);
    public static final ItemStack dustTin = new ItemStack(materialBase, 1, 33);
    public static final ItemStack dustSilver = new ItemStack(materialBase, 1, 34);
    public static final ItemStack dustLead = new ItemStack(materialBase, 1, 35);
    public static final ItemStack dustNickel = new ItemStack(materialBase, 1, 36);
    public static final ItemStack dustPlatinum = new ItemStack(materialBase, 1, 37);
    public static final ItemStack dustManaInfused = new ItemStack(materialBase, 1, 38);
    public static final ItemStack dustElectrum = new ItemStack(materialBase, 1, 39);
    public static final ItemStack dustInvar = new ItemStack(materialBase, 1, 40);
    public static final ItemStack dustBronze = new ItemStack(materialBase, 1, 41);
    public static final ItemStack dustSignalum = new ItemStack(materialBase, 1, 42);
    public static final ItemStack dustLumium = new ItemStack(materialBase, 1, 43);
    public static final ItemStack dustEnderium = new ItemStack(materialBase, 1, 44);

    public static final ItemStack dustWood = new ItemStack(materialBaseTE, 1, 512);
    public static final ItemStack boneMeal = new ItemStack(Items.dye, 1, 15);

    public static final ItemStack dustCoal = new ItemStack(materialBase, 1, 2);
    public static final ItemStack dustCharcoal = new ItemStack(materialBase, 1, 3);
    public static final ItemStack sulfer = new ItemStack(materialBase, 1, 16);
    public static final ItemStack niter = new ItemStack(materialBase, 1, 17);

    public static final ItemStack dustObsidian = new ItemStack(materialBase, 1, 4);

    static {

        // Cache builtins
        preferred.put("ingotIron", new ItemStack(Items.iron_ingot));
        preferred.put("ingotGold", new ItemStack(Items.gold_ingot));
        preferred.put("nuggetGold", new ItemStack(Items.gold_nugget));
        preferred.put("blockGold", new ItemStack(Blocks.gold_block));
        preferred.put("blockIron", new ItemStack(Blocks.iron_block));
        preferred.put("gemDiamond", new ItemStack(Items.diamond));
        preferred.put("gemEmerald", new ItemStack(Items.emerald));

        preferred.put("ingotCopper", new ItemStack(materialBase, 1, 64));
        preferred.put("ingotTin", new ItemStack(materialBase, 1, 65));
        preferred.put("ingotSilver", new ItemStack(materialBase, 1, 66));
        preferred.put("ingotLead", new ItemStack(materialBase, 1, 67));
        preferred.put("ingotNickel", new ItemStack(materialBase, 1, 68));
        preferred.put("ingotPlatinum", new ItemStack(materialBase, 1, 69));
        preferred.put("ingotManaInfused", new ItemStack(materialBase, 1, 70));
        preferred.put("ingotElectrum", new ItemStack(materialBase, 1, 71));
        preferred.put("ingotInvar", new ItemStack(materialBase, 1, 72));
        preferred.put("ingotBronze", new ItemStack(materialBase, 1, 73));
        preferred.put("ingotSignalum", new ItemStack(materialBase, 1, 74));
        preferred.put("ingotLumium", new ItemStack(materialBase, 1, 75));
        preferred.put("ingotEnderium", new ItemStack(materialBase, 1, 76));

        preferred.put("nuggetIron", new ItemStack(materialBase, 1, 8));
        preferred.put("nuggetCopper", new ItemStack(materialBase, 1, 96));
        preferred.put("nuggetTin", new ItemStack(materialBase, 1, 97));
        preferred.put("nuggetSilver", new ItemStack(materialBase, 1, 98));
        preferred.put("nuggetLead", new ItemStack(materialBase, 1, 99));
        preferred.put("nuggetNickel", new ItemStack(materialBase, 1, 100));
        preferred.put("nuggetPlatinum", new ItemStack(materialBase, 1, 101));
        preferred.put("nuggetManaInfused", new ItemStack(materialBase, 1, 102));
        preferred.put("nuggetElectrum", new ItemStack(materialBase, 1, 103));
        preferred.put("nuggetInvar", new ItemStack(materialBase, 1, 104));
        preferred.put("nuggetBronze", new ItemStack(materialBase, 1, 105));
        preferred.put("nuggetSignalum", new ItemStack(materialBase, 1, 106));
        preferred.put("nuggetLumium", new ItemStack(materialBase, 1, 107));
        preferred.put("nuggetEnderium", new ItemStack(materialBase, 1, 108));

        preferred.put("dustIron", dustIron);
        preferred.put("dustGold", dustGold);
        preferred.put("dustCopper", dustCopper);
        preferred.put("dustTin", dustTin);
        preferred.put("dustSilver", dustSilver);
        preferred.put("dustLead", dustLead);
        preferred.put("dustNickel", dustNickel);
        preferred.put("dustPlatinum", dustPlatinum);
        preferred.put("dustManaInfused", dustManaInfused);
        preferred.put("dustElectrum", dustElectrum);
        preferred.put("dustInvar", dustInvar);
        preferred.put("dustBronze", dustBronze);
        preferred.put("dustSignalum", dustSignalum);
        preferred.put("dustLumium", dustLumium);
        preferred.put("dustEnderium", dustEnderium);

        preferred.put("dustCoal", dustCoal);
        preferred.put("dustCharcoal", dustCharcoal);
        preferred.put("dustSulfer", sulfer);
        preferred.put("dustObsidian", dustObsidian);

        preferred.put("blockCopper", new ItemStack(storageBase, 1, 0));
        preferred.put("blockTin", new ItemStack(storageBase, 1, 1));
        preferred.put("blockSilver", new ItemStack(storageBase, 1, 2));
        preferred.put("blockLead", new ItemStack(storageBase, 1, 3));
        preferred.put("blockFerrous", new ItemStack(storageBase, 1, 4));
        preferred.put("blockPlatinum", new ItemStack(storageBase, 1, 5));
        preferred.put("blockManaInfused", new ItemStack(storageBase, 1, 6));
        preferred.put("blockElectrum", new ItemStack(storageBase, 1, 7));
        preferred.put("blockInvar", new ItemStack(storageBase, 1, 8));
        preferred.put("blockBronze", new ItemStack(storageBase, 1, 9));
        preferred.put("blockSignalum", new ItemStack(storageBase, 1, 10));
        preferred.put("blockLumium", new ItemStack(storageBase, 1, 11));
        preferred.put("blockEnderium", new ItemStack(storageBase, 1, 12));

        preferred.put("gearIron", new ItemStack(materialBase, 1, 12));
        preferred.put("gearGold", new ItemStack(materialBase, 1, 13));
        preferred.put("gearCopper", new ItemStack(materialBase, 1, 128));
        preferred.put("gearTin", new ItemStack(materialBase, 1, 129));
        preferred.put("gearSilver", new ItemStack(materialBase, 1, 130));
        preferred.put("gearLead", new ItemStack(materialBase, 1, 131));
        preferred.put("gearNickel", new ItemStack(materialBase, 1, 132));
        preferred.put("gearPlatinum", new ItemStack(materialBase, 1, 133));
        preferred.put("gearManaInfused", new ItemStack(materialBase, 1, 134));
        preferred.put("gearElectrum", new ItemStack(materialBase, 1, 135));
        preferred.put("gearInvar", new ItemStack(materialBase, 1, 136));
        preferred.put("gearBronze", new ItemStack(materialBase, 1, 137));
        preferred.put("gearSignalum", new ItemStack(materialBase, 1, 138));
        preferred.put("gearLumium", new ItemStack(materialBase, 1, 139));
        preferred.put("gearEnderium", new ItemStack(materialBase, 1, 140));

        preferred = ImmutableMap.copyOf(preferred);

    }

    public static ItemStack convertToDustIfPossible(final ItemStack stack) {

        String oreName = ItemHelper.getOreName(stack);

        if (oreName == null)
            return stack;

        if (oreName.startsWith("ingot"))
            oreName = StringUtils.replaceOnce(oreName, "ingot", "dust");
        else if (oreName.startsWith("plank"))
            oreName = StringUtils.replaceOnce(oreName, "plank", "dust");
        else
            return stack;

        return getItemStack(oreName);
    }

    public static ItemStack getPreferredStack(final ItemStack stack) {
        final String oreName = ItemHelper.getOreName(stack);
        if (oreName == null || oreName.isEmpty() || "Unknown".compareToIgnoreCase(oreName) == 0)
            return stack;

        final ItemStack newStack = getItemStack(oreName);
        if (newStack == null)
            return stack;

        newStack.stackSize = stack.stackSize;
        return newStack;
    }

    public static ItemStack getItemStack(final String name) {
        return getItemStack(name, 1);
    }

    public static ItemStack getItemStack(final String name, final int quantity) {

        if (name == null || name.isEmpty())
            return null;

        // Check our preferred list first. If we have a hit, use it.
        ItemStack result = preferred.get(name);

        if (result != null) {

            result = result.copy();
            result.stackSize = quantity;

        } else {

            // Parse out the possible subtype from the end of the string
            String workingName = name;
            int subType = -1;

            if (StringUtils.countMatches(name, ":") == 2) {
                workingName = StringUtils.substringBeforeLast(name, ":");
                final String num = StringUtils.substringAfterLast(name, ":");

                if (num != null && !num.isEmpty()) {

                    if ("*".compareTo(num) == 0)
                        subType = OreDictionary.WILDCARD_VALUE;
                    else {
                        try {
                            subType = Integer.parseInt(num);
                        } catch (Exception e) {
                            // It appears malformed - assume the incoming name
                            // is
                            // the real name and continue.
                            ;
                        }
                    }
                }
            }

            // Check the OreDictionary first for any alias matches. Otherwise
            // go to the game registry to find a match.
            final ArrayList<ItemStack> ores = OreDictionary.getOres(workingName);
            if (!ores.isEmpty()) {
                result = ores.get(0).copy();
                result.stackSize = quantity;
            } else {
                final Item i = GameData.getItemRegistry().getObject(workingName);
                if (i != null) {
                    result = new ItemStack(i, quantity);
                }
            }

            // If we did have a hit on a base item, set the sub-type
            // as needed.
            if (result != null && subType != -1) {
                if (subType == OreDictionary.WILDCARD_VALUE && !result.getHasSubtypes()) {
                    ModLog.warn("[%s] GENERIC requested but Item does not support sub-types", name);
                } else {
                    result.setItemDamage(subType);
                }
            }
        }

        return result;
    }

    public static List<ItemStack> getItemStackRange(final String name, final int startSubtype, final int endSubtype,
            final int quantity) {

        return getItemStackRange(getItemStack(name).getItem(), startSubtype, endSubtype, quantity);
    }

    public static List<ItemStack> getItemStackRange(final Item item, final int start, final int end,
            final int quantity) {

        final ArrayList<ItemStack> result = new ArrayList<ItemStack>();

        for (int i = start; i <= end; i++) {
            result.add(new ItemStack(item, quantity, i));
        }

        return result;
    }

    public static List<ItemStack> getItemStackRange(final Block block, final int start, final int end,
            final int quantity) {

        final ArrayList<ItemStack> result = new ArrayList<ItemStack>();

        for (int i = start; i <= end; i++) {
            result.add(new ItemStack(block, quantity, i));
        }

        return result;
    }

    public static String resolveName(final ItemStack stack) {
        String result = null;

        if (stack != null) {

            try {
                result = stack.getDisplayName();
            } catch (Exception e) {
                ;
            }

            if (result == null) {
                try {
                    result = stack.getUnlocalizedName();
                } catch (Exception e) {
                    ;
                }
            }
        }

        return result == null || result.isEmpty() ? "UNKNOWN" : result;
    }

    public void dumpSubItems(final Logger log, final String itemId) {
        final ItemStack stack = getItemStack(itemId, 1);
        if (stack != null) {

            try {
                for (int i = 0; i < 1024; i++) {
                    stack.setItemDamage(i);
                    final String name = resolveName(stack);
                    if (name != null && !name.isEmpty() && !name.contains("(Destroy)"))
                        log.info(String.format("%s:%d = %s", itemId, i, name));
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                ;
            }
        }
    }

    public void dumpItemStack(final Logger log, final String title, final ItemStack... items) {

        log.info("");
        log.info(title);
        log.info(StringUtils.repeat('-', 32));

        if (items == null || items.length == 0) {
            log.info("No items in list");
            return;
        }

        for (final ItemStack stack : items) {
            log.info(String.format("%s (%s)", resolveName(stack), stack.toString()));
        }
        log.info(StringUtils.repeat('-', 32));
        log.info(String.format("Total: %d item stacks", items.length));
    }

    public static void dumpFluidRegistry(final Logger log) {

        log.info("Fluid Registry:");

        for (final Entry<String, Fluid> e : FluidRegistry.getRegisteredFluids().entrySet()) {
            log.info(String.format("%s: %s", e.getKey(), e.getValue().getName()));
        }
    }

    public static List<ItemStack> clone(final ItemStack... stacks) {
        final ArrayList<ItemStack> result = new ArrayList<ItemStack>(stacks.length);
        for (final ItemStack stack : stacks)
            if (stack != null)
                result.add(stack.copy());
        return result;
    }

    public static List<ItemStack> clone(final List<ItemStack> stacks) {
        final ArrayList<ItemStack> result = new ArrayList<ItemStack>(stacks.size());
        for (final ItemStack stack : stacks)
            if (stack != null)
                result.add(stack.copy());
        return result;
    }

    public static void append(final List<ItemStack> list, final ItemStack stack) {

        assert list != null;
        assert stack != null;

        list.add(stack);
    }

    public static void append(final List<ItemStack> list, final String... items) {

        assert list != null;
        assert items != null && items.length > 0;

        for (final String s : items)
            list.add(ItemStackHelper.getItemStack(s));
    }

    public static void append(final List<ItemStack> list, final String item, final int quantity) {

        assert list != null;
        assert item != null;
        assert quantity > 0;

        list.add(ItemStackHelper.getItemStack(item, quantity));
    }

    public static void append(final List<ItemStack> list, final Block... blocks) {

        assert list != null;
        assert blocks != null && blocks.length > 0;

        for (final Block b : blocks)
            list.add(new ItemStack(b));
    }

    public static void append(final List<ItemStack> list, final Block block, final int quantity) {

        assert list != null;
        assert block != null;
        assert quantity > 0;

        list.add(new ItemStack(block, quantity));
    }

    public static void append(final List<ItemStack> list, final Item... items) {

        assert list != null;
        assert items != null && items.length > 0;

        for (final Item i : items)
            list.add(new ItemStack(i));
    }

    public static void append(final List<ItemStack> list, final Item item, final int quantity) {

        assert list != null;
        assert item != null;
        assert quantity > 0;

        list.add(new ItemStack(item, quantity));
    }

    public static void append(final List<ItemStack> list, final List<ItemStack> stacks) {

        assert list != null;
        assert stacks != null && !stacks.isEmpty();

        list.addAll(stacks);
    }

    public static void append(final List<ItemStack> list, final ItemStack... stacks) {

        assert list != null;
        assert stacks != null && stacks.length > 0;

        for (final ItemStack stack : stacks) {
            list.add(stack);
        }
    }

    public static void appendSubtype(final List<ItemStack> list, final ItemStack stack, final int subtype) {

        assert list != null;
        assert stack != null;
        assert subtype >= 0;

        final ItemStack s = stack.copy();
        s.setItemDamage(subtype);
        list.add(s);
    }

    public static void appendSubtype(final List<ItemStack> list, final Item item, final int subtype) {

        assert list != null;
        assert item != null;
        assert subtype >= 0;

        list.add(new ItemStack(item, 1, subtype));
    }

    public static void appendSubtypeRange(final List<ItemStack> list, final String item, final int start,
            final int end, final int quantity) {

        assert list != null;
        assert item != null;
        assert start >= 0 && end >= start;
        assert quantity > 0;

        list.addAll(ItemStackHelper.getItemStackRange(item, start, end, quantity));
    }

    public static void appendSubtypeRange(final List<ItemStack> list, final String item, final int start,
            final int end) {

        assert list != null;
        assert item != null;
        assert start >= 0 && end >= start;

        list.addAll(ItemStackHelper.getItemStackRange(item, start, end, 1));
    }

    public static void appendSubtypeRange(final List<ItemStack> list, final Item item, final int start,
            final int end, final int quantity) {

        assert list != null;
        assert item != null;
        assert start >= 0 && end >= start;
        assert quantity > 0;

        list.addAll(ItemStackHelper.getItemStackRange(item, start, end, quantity));
    }

    public static void appendSubtypeRange(final List<ItemStack> list, final Item item, final int start,
            final int end) {

        assert list != null;
        assert item != null;
        assert start >= 0 && end >= start;

        list.addAll(ItemStackHelper.getItemStackRange(item, start, end, 1));
    }

    public static void appendSubtypeRange(final List<ItemStack> list, final Block block, final int start,
            final int end, final int quantity) {

        assert list != null;
        assert block != null;
        assert start >= 0 && end >= start;
        assert quantity > 0;

        list.addAll(ItemStackHelper.getItemStackRange(block, start, end, quantity));
    }

    public static void appendSubtypeRange(final List<ItemStack> list, final Block block, final int start,
            final int end) {

        assert list != null;
        assert block != null;
        assert start >= 0 && end >= start;

        list.addAll(ItemStackHelper.getItemStackRange(block, start, end, 1));
    }

    public static void appendSubtypeRange(final List<ItemStack> list, final ItemStack stack, final int start,
            final int end) {
        appendSubtypeRange(list, stack, start, end, 1);
    }

    public static void appendSubtypeRange(final List<ItemStack> list, final ItemStack stack, final int start,
            final int end, final int quantity) {

        assert list != null;
        assert stack != null;
        assert start >= 0 && end >= start;
        assert quantity > 0;

        for (int i = start; i <= end; i++) {
            final ItemStack s = stack.copy();
            s.setItemDamage(i);
            s.stackSize = quantity;
            list.add(s);
        }
    }

    public static void spawnIntoWorld(final World world, final ItemStack stack, final double x, final double y,
            final double z) {

        if (stack == null)
            return;

        final float f = rand.nextFloat() * 0.8F + 0.1F;
        final float f1 = rand.nextFloat() * 0.8F + 0.1F;
        final float f2 = rand.nextFloat() * 0.8F + 0.1F;

        while (stack.stackSize > 0) {
            int j = rand.nextInt(21) + 10;

            if (j > stack.stackSize) {
                j = stack.stackSize;
            }

            stack.stackSize -= j;

            final EntityItem item = new EntityItem(world, x + f, y + f1, z + f2,
                    new ItemStack(stack.getItem(), j, stack.getItemDamage()));

            if (stack.hasTagCompound()) {
                item.getEntityItem().setTagCompound((NBTTagCompound) stack.getTagCompound().copy());
            }

            world.spawnEntityInWorld(item);
        }
    }

    /**
     * Compresses the inventory list by consolidating stacks toward the
     * beginning of the array. This operation occurs in place meaning the return
     * array is the original one passed in.
     * 
     * @param inv
     * @return
     */
    public static void coelece(final ItemStack[] inv, final int startSlot, final int endSlot) {

        assert inv != null;
        assert startSlot >= 0 && endSlot >= startSlot;
        assert startSlot < inv.length;

        for (int i = startSlot + 1; i <= endSlot; i++) {

            final ItemStack stack = inv[i];
            if (stack != null) {

                for (int j = startSlot; j < i; j++) {

                    final ItemStack target = inv[j];
                    if (target == null) {
                        inv[j] = stack;
                        inv[i] = null;
                        break;
                    } else if (ItemHelper.itemsIdentical(stack, target)) {

                        final int hold = target.getMaxStackSize() - target.stackSize;

                        if (hold >= stack.stackSize) {
                            target.stackSize += stack.stackSize;
                            inv[i] = null;
                            break;
                        } else if (hold != 0) {
                            stack.stackSize -= hold;
                            target.stackSize += hold;
                        }
                    }
                }
            }
        }
    }

    /**
     * Compresses the inventory list by consolidating stacks toward the
     * beginning of the array. This operation occurs in place meaning the return
     * array is the original one passed in.
     * 
     * @param inv
     * @return
     */
    public static List<ItemStack> coelece(final List<ItemStack> inv) {

        if (inv == null) {
            return inv;
        }

        for (int i = 1; i < inv.size(); i++) {

            final ItemStack stack = inv.get(i);
            if (stack != null) {

                for (int j = 0; j < i; j++) {

                    final ItemStack target = inv.get(j);
                    if (target == null) {
                        inv.set(j, stack);
                        inv.set(i, null);
                        break;
                    } else if (ItemHelper.itemsIdentical(stack, target)) {

                        final int hold = target.getMaxStackSize() - target.stackSize;

                        if (hold >= stack.stackSize) {
                            target.stackSize += stack.stackSize;
                            inv.set(i, null);
                            break;
                        } else if (hold != 0) {
                            stack.stackSize -= hold;
                            target.stackSize += hold;
                        }
                    }
                }
            }
        }

        // Purge all null entries
        inv.removeAll(Collections.singleton(null));

        return inv;
    }

    public static boolean addItemStackToInventory(final ItemStack inv[], final ItemStack stack, final int startSlot,
            final int endSlot) {

        if (stack == null || stack.stackSize == 0)
            return true;

        for (int slot = startSlot; slot <= endSlot; slot++) {
            ItemStack invStack = inv[slot];

            // Quick and easy - if the slot is empty its the target
            if (invStack == null) {
                inv[slot] = stack;
                return true;
            }

            // If the stack can fit into this slot do the merge
            final int remainingSpace = invStack.getMaxStackSize() - invStack.stackSize;
            if (remainingSpace > 0 && ItemHelper.itemsIdentical(stack, invStack)) {

                if (remainingSpace >= stack.stackSize) {
                    invStack.stackSize += stack.stackSize;
                    return true;
                }

                stack.stackSize -= remainingSpace;
                invStack.stackSize += remainingSpace;
            }
        }

        return false;
    }

    public static boolean removeItemStackFromInventory(final ItemStack inv[], final ItemStack stack,
            final int startSlot, final int endSlot) {
        if (stack == null || stack.stackSize == 0)
            return true;

        for (int slot = startSlot; slot <= endSlot && stack.stackSize > 0; slot++) {
            final ItemStack invStack = inv[slot];
            if (invStack != null && ItemStackHelper.areEqual(invStack, stack)) {
                if (invStack.stackSize > stack.stackSize) {
                    invStack.stackSize -= stack.stackSize;
                    stack.stackSize = 0;
                } else {
                    stack.stackSize -= invStack.stackSize;
                    inv[slot] = null;
                }
            }
        }

        return stack.stackSize == 0;
    }

    public static void setItemName(final ItemStack stack, final String name) {

        if (stack == null)
            return;

        NBTTagCompound nbt = stack.getTagCompound();
        if (nbt == null)
            nbt = new NBTTagCompound();

        NBTTagCompound display = nbt.getCompoundTag("display");
        if (display == null)
            display = new NBTTagCompound();
        display.setString("Name", name);

        nbt.setTag("display", display);
        stack.setTagCompound(nbt);
    }

    public static void setItemLore(final ItemStack stack, final List<String> lore) {

        if (stack == null)
            return;

        NBTTagCompound nbt = stack.getTagCompound();
        if (nbt == null)
            nbt = new NBTTagCompound();

        final NBTTagList l = new NBTTagList();
        for (final String s : lore)
            l.appendTag(new NBTTagString(s));
        NBTTagCompound display = nbt.getCompoundTag("display");
        if (display == null)
            display = new NBTTagCompound();
        display.setTag("Lore", l);

        nbt.setTag("display", display);
        stack.setTagCompound(nbt);
    }

    public static ItemStack makeGlow(final ItemStack stack) {
        final NBTTagCompound nbt = stack.hasTagCompound() ? stack.getTagCompound() : new NBTTagCompound();
        nbt.setTag("ench", new NBTTagList());
        stack.setTagCompound(nbt);
        return stack;
    }

    /**
     * Determines if the two specified tags are equal.
     * 
     * @param nbt1
     *            First tag compound to compare
     * @param nbt2
     *            Second tag compound to compare
     * @return true if they are equal; false otherwise
     */
    public static boolean areTagsEqual(final NBTTagCompound nbt1, final NBTTagCompound nbt2) {
        return nbt1 == null && nbt2 != null ? false : nbt1 == null || nbt1.equals(nbt2);
    }

    /**
     * Determines if the two ItemStacks are equal. Does not include quantity in
     * the determination, but does include tags.
     * 
     * @param stack1
     *            First stack to compare
     * @param stack2
     *            Second stack to compare
     * @return true if they are equal; false otherwise
     */
    public static boolean areEqual(final ItemStack stack1, final ItemStack stack2) {

        if (stack1 == stack2)
            return true;

        if (stack1 == null || stack2 == null)
            return false;

        return stack1.isItemEqual(stack2) && areTagsEqual(stack1.stackTagCompound, stack2.stackTagCompound);
    }
}