Package net.aufdemrand.denizen.objects

Source Code of net.aufdemrand.denizen.objects.dInventory

package net.aufdemrand.denizen.objects;

import net.aufdemrand.denizen.objects.aH.Argument;
import net.aufdemrand.denizen.objects.aH.PrimitiveType;
import net.aufdemrand.denizen.objects.notable.Notable;
import net.aufdemrand.denizen.objects.notable.NotableManager;
import net.aufdemrand.denizen.objects.notable.Note;
import net.aufdemrand.denizen.objects.properties.Property;
import net.aufdemrand.denizen.objects.properties.PropertyParser;
import net.aufdemrand.denizen.scripts.ScriptRegistry;
import net.aufdemrand.denizen.scripts.containers.core.InventoryScriptContainer;
import net.aufdemrand.denizen.scripts.containers.core.InventoryScriptHelper;
import net.aufdemrand.denizen.tags.Attribute;
import net.aufdemrand.denizen.utilities.Utilities;
import net.aufdemrand.denizen.utilities.debugging.dB;
import net.aufdemrand.denizen.utilities.nbt.ImprovedOfflinePlayer;
import net.aufdemrand.denizencore.utilities.CoreUtilities;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.BrewingStand;
import org.bukkit.block.Chest;
import org.bukkit.block.DoubleChest;
import org.bukkit.block.Furnace;
import org.bukkit.craftbukkit.v1_7_R4.inventory.CraftInventory;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.BookMeta;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class dInventory implements dObject, Notable, Adjustable {

    public static dInventory mirrorBukkitInventory(Inventory inventory) {
        // Scripts have priority over notables
        if (InventoryScriptHelper.tempInventoryScripts.containsKey(inventory))
            return new dInventory(inventory).setIdentifiers("script",
                    InventoryScriptHelper.tempInventoryScripts.get(inventory));
        // Use the map to get notable inventories
        if (InventoryScriptHelper.notableInventories.containsKey(inventory.getTitle()))
            return InventoryScriptHelper.notableInventories.get(inventory.getTitle());
        // Iterate through offline player inventories
        for (Map.Entry<UUID, PlayerInventory> inv : InventoryScriptHelper.offlineInventories.entrySet()) {
            if (((CraftInventory) inv.getValue()).getInventory().equals(((CraftInventory) inventory).getInventory()))
                return new dInventory(new ImprovedOfflinePlayer(inv.getKey()));
        }

        return new dInventory(inventory);
    }

    /////////////////////
    //   PATTERNS
    /////////////////

    final static Pattern inventory_by_type = Pattern.compile("(in@)(npc|player|enderchest|workbench|entity|location|generic)\\[(.+?)\\]", Pattern.CASE_INSENSITIVE);
    final static Pattern inventory_by_script = Pattern.compile("(in@)(.+)", Pattern.CASE_INSENSITIVE);


    /////////////////////
    //   STATIC FIELDS
    /////////////////

    // The maximum number of slots a Bukkit inventory can have
    public final static int maxSlots = 54;

    // All of the inventory id types we use
    public final static String[] idTypes = { "npc", "player", "enderchest", "workbench", "entity", "location", "generic" };


    /////////////////////
    //   NOTABLE METHODS
    /////////////////

    public boolean isUnique() {
        return NotableManager.isSaved(this);
    }

    @Note("Inventories")
    public String getSaveObject() {
       return "in@" + idType + PropertyParser.getPropertiesString(this);
    }

    public void makeUnique(String id) {
        String title = inventory.getTitle();
        if (title == null || title.startsWith("container."))
            title = inventory.getType().getDefaultTitle();
        // You can only have 32 characters in an inventory title... So let's make sure we have at least 3 colors...
        // which brings notable inventory title lengths down to 26... TODO: document this/fix if possible in later version
        if (title.length() > 26) title = title.substring(0, title.charAt(25) == '§' ? 25 : 26);
        String colors;
        while (true) {
            colors = Utilities.generateRandomColors(3);
            if (!InventoryScriptHelper.notableInventories.containsKey(title + colors)) {
                ItemStack[] contents = inventory.getContents();
                if (getInventoryType() == InventoryType.CHEST) {
                    inventory = Bukkit.getServer().createInventory(null, inventory.getSize(), title + colors);
                }
                else {
                    inventory = Bukkit.getServer().createInventory(null, inventory.getType(), title + colors);
                }
                inventory.setContents(contents);
                InventoryScriptHelper.notableInventories.put(title + colors, this);
                break;
            }
        }
        idType = null;
        idHolder = null;
        loadIdentifiers();
        NotableManager.saveAs(this, id);
    }

    public void forget() {
        NotableManager.remove(idHolder);
    }

    //////////////////
    //    OBJECT FETCHER
    ////////////////

    @Fetchable("in")
    public static dInventory valueOf(String string) {
        return valueOf(string, null, null);
    }

    /**
     *
     * Gets a dInventory from a string format.
     *
     * @param string
     *          The inventory in string form. (in@player[playerName], in@scriptName, etc.)
     * @return
     *          The dInventory value. If the string is incorrectly formatted or
     *          the specified inventory is invalid, this is null.
     *
     */
    public static dInventory valueOf(String string, dPlayer player, dNPC npc) {

        if (string == null) return null;

        ///////
        // Handle objects with properties through the object fetcher
        Matcher m = ObjectFetcher.DESCRIBED_PATTERN.matcher(string);
        if (m.matches()) {
            return ObjectFetcher.getObjectFrom(dInventory.class, string, player, npc);
        }

        // Match in@scriptName for Inventory Scripts, as well as in@notableName
        m = inventory_by_script.matcher(string);

        if (m.matches()) {
            if (ScriptRegistry.containsScript(m.group(2), InventoryScriptContainer.class))
                return ScriptRegistry.getScriptContainerAs(m.group(2), InventoryScriptContainer.class)
                        .getInventoryFrom(player, npc);

            if (NotableManager.isSaved(m.group(2)) && NotableManager.isType(m.group(2), dInventory.class))
                return (dInventory) NotableManager.getSavedObject(m.group(2));

            for (String idType : idTypes) if (m.group(2).equalsIgnoreCase(idType))
                return new dInventory(m.group(2));
        }

        m = inventory_by_type.matcher(string);

        if (m.matches()) {

            // Set the type of the inventory holder
            String type = m.group(2).toLowerCase();
            // Set the name/id/location of the inventory holder
            String holder = m.group(3);

            if (type.equals("generic")) {
                Argument arg = Argument.valueOf(holder);
                if (arg.matchesEnum(InventoryType.values())) {
                    return new dInventory(InventoryType.valueOf(holder.toUpperCase()));
                }
                else if (arg.matchesPrimitive(PrimitiveType.Integer)) {
                    return new dInventory(arg.asElement().asInt());
                }
                else {
                    dB.echoError("That type of inventory does not exist!");
                }
            }
            else if (type.equals("npc")) {
                if (dNPC.matches(holder))
                    return dNPC.valueOf(holder).getDenizenInventory();
            }
            else if (type.equals("player")) {
                if (dPlayer.matches(holder))
                    return dPlayer.valueOf(holder).getInventory();
            }
            else if (type.equals("workbench")) {
                if (dPlayer.matches(holder)) {
                    dInventory workbench = dPlayer.valueOf(holder).getWorkbench();
                    if (workbench != null)
                        dB.echoError("Value of dInventory returning null (" + string + ")." +
                                " Specified player does not have an open workbench.");
                    else
                        return workbench;
                }
            }
            else if (type.equals("enderchest")) {
                if (dPlayer.matches(holder))
                    return dPlayer.valueOf(holder).getEnderChest();
            }
            else if (type.equals("entity")) {
                if (dEntity.matches(holder))
                    return dEntity.valueOf(holder).getInventory();
            }
            else if (type.equals("location")) {
                if (dLocation.matches(holder))
                    return dLocation.valueOf(holder).getInventory();
            }

            // If the dInventory is invalid, alert the user and return null
            dB.echoError("Value of dInventory returning null. Invalid " +
                    type + " specified: " + holder);
            return null;
        }

        dB.echoError("Value of dInventory returning null. Invalid dInventory specified: " + string);
        return null;
    }

    /**
     * Determine whether a string is a valid inventory.
     *
     * @param arg  the arg string
     * @return  true if matched, otherwise false
     *
     */
    public static boolean matches(String arg) {

        // Every single dInventory should have the in@ prefix. No exceptions.
        return arg.toLowerCase().startsWith("in@");

    }


    ///////////////
    //   Constructors
    /////////////

    String idType = null;
    String idHolder = null;

    public dInventory(Inventory inventory) {
        this.inventory = inventory;
        loadIdentifiers();
    }

    public dInventory(Inventory inventory, InventoryHolder holder) {
        this.inventory = inventory;
        loadIdentifiers(holder);
    }

    public dInventory(InventoryHolder holder) {
        inventory = holder.getInventory();
        loadIdentifiers();
    }

    public dInventory(ItemStack[] items) {
        inventory = Bukkit.getServer().createInventory(null, (int) Math.ceil(items.length / 9) * 9);
        setContents(items);
        loadIdentifiers();
    }

    public dInventory(ImprovedOfflinePlayer offlinePlayer) {
        this(offlinePlayer, false);
    }

    public dInventory(ImprovedOfflinePlayer offlinePlayer, boolean isEnderChest) {
        inventory = isEnderChest ? offlinePlayer.getEnderChest() : offlinePlayer.getInventory();
        setIdentifiers("player", "p@" + offlinePlayer.getUniqueId());
    }

    public dInventory(int size, String title) {
        if (size <= 0 || size%9 != 0) {
            dB.echoError("InventorySize must be multiple of 9, and greater than 0.");
            return;
        }
        inventory = Bukkit.getServer().createInventory(null, size, title);
        loadIdentifiers();
    }

    public dInventory(InventoryType type) {
        inventory = Bukkit.getServer().createInventory(null, type);
        loadIdentifiers();
    }

    public dInventory(int size) {
        this(size, "Chest");
    }

    public dInventory(String idType) {
        this.idType = idType.toLowerCase();
        for (Mechanism mechanism : mechanisms) {
            adjust(mechanism);
        }
        mechanisms.clear();
    }

    /////////////////////
    //   INSTANCE FIELDS/METHODS
    /////////////////

    // Associated with Bukkit Inventory

    private Inventory inventory = null;

    public Inventory getInventory() {
        return inventory;
    }

    /**
     * Changes the inventory to a new inventory, possibly of a different
     * type, size, and with different contents.
     * NOTE: SHOULD ONLY BE USED IN CASES WHERE THERE
     *       ARE NO OTHER OPTIONS.
     *
     * @param inventory  The new inventory
     */
    public void setInventory(Inventory inventory) {
        this.inventory = inventory;
        loadIdentifiers();
    }

    public void setInventory(Inventory inventory, dPlayer player) {
        this.inventory = inventory;
        this.idHolder = player.identify();
    }

    public void setTitle(String title) {
        if (!getIdType().equals("generic") || title == null)
            return;
        else if (inventory == null) {
            inventory = Bukkit.getServer().createInventory(null, maxSlots, title);
            loadIdentifiers();
            return;
        }
        ItemStack[] contents = inventory.getContents();
        if (inventory.getType() == InventoryType.CHEST)
            inventory = Bukkit.getServer().createInventory(null, inventory.getSize(), title);
        else
            inventory = Bukkit.getServer().createInventory(null, inventory.getType(), title);
        inventory.setContents(contents);
        loadIdentifiers();
    }

    public boolean removeItem(dItem item, int amount) {
        if (item == null)
            return false;
        item.setAmount(1);
        String myItem = CoreUtilities.toLowerCase(item.getFullString());
        for (int i = 0; i < inventory.getSize(); i++) {
            ItemStack is = inventory.getItem(i);
            if (is == null)
                continue;
            is = is.clone();
            int count = is.getAmount();
            is.setAmount(1);
            String newItem = CoreUtilities.toLowerCase(new dItem(is).getFullString());
            if (myItem.equals(newItem)) {
                if (count <= amount) {
                    inventory.setItem(i, null);
                    amount -= count;
                    if (amount == 0)
                        return true;
                }
                else if (count > amount) {
                    is.setAmount(count - amount);
                    inventory.setItem(i, is);
                    return true;
                }
            }
        }
        return false;
    }

    public void setSize(int size) {
        if (!getIdType().equals("generic"))
            return;
        else if (size <= 0 || size%9 != 0) {
            dB.echoError("InventorySize must be multiple of 9, and greater than 0.");
            return;
        }
        else if (inventory == null) {
            inventory = Bukkit.getServer().createInventory(null, size, "Chest");
            loadIdentifiers();
            return;
        }
        int oldSize = inventory.getSize();
        ItemStack[] oldContents = inventory.getContents();
        ItemStack[] newContents = new ItemStack[size];
        if (oldSize > size)
            for (int i = 0; i < size; i++)
                newContents[i] = oldContents[i];
        else
            newContents = oldContents;
        String title = inventory.getTitle();
        inventory = Bukkit.getServer().createInventory(null, size,
                (title != null ? title : inventory.getType().getDefaultTitle()));
        inventory.setContents(newContents);
        loadIdentifiers();
    }

    private void loadIdentifiers() {
        loadIdentifiers(inventory.getHolder());
    }

    private void loadIdentifiers(final InventoryHolder holder) {
        if (inventory == null) return;

        if (holder != null) {
            if (holder instanceof dNPC) {
                idType = "npc";
                idHolder = ((dNPC) holder).identify();
                return;
            }
            else if (holder instanceof Player) {
                if (inventory.getType() == InventoryType.ENDER_CHEST)
                    idType = "enderchest";
                else if (inventory.getType() == InventoryType.WORKBENCH)
                    idType = "workbench";
                else
                    idType = "player";
                idHolder = new dPlayer((Player) holder).identify();
                return;
            }
            else if (holder instanceof Entity) {
                idType = "entity";
                idHolder = new dEntity((Entity) holder).identify();
                return;
            }
            else {
                idType = "location";
                try {
                    idHolder = getLocation(holder).identify();
                } catch (NullPointerException e) {
                    idHolder = "null";
                }
                return;
            }
        }
        else if (getIdType().equals("player")) {
            // Iterate through offline player inventories
            for (Map.Entry<UUID, PlayerInventory> inv : InventoryScriptHelper.offlineInventories.entrySet()) {
                if (((CraftInventory) inv.getValue()).getInventory().equals(((CraftInventory) inventory).getInventory())) {
                    idHolder = new dPlayer(inv.getKey()).identify();
                    return;
                }
            }
        }
        else if (getIdType().equals("script")) {
            // Iterate through inventory scripts
            for (InventoryScriptContainer container : InventoryScriptHelper.inventory_scripts.values()) {
                if (((CraftInventory) (container.getInventoryFrom()).inventory).getInventory().equals(((CraftInventory) inventory).getInventory())) {
                    idHolder = container.getName();
                    return;
                }
            }
        }
        idType = "generic";
        idHolder = getInventory().getType().name();
    }

    public dInventory setIdentifiers(String type, String holder) {
        idType = type;
        idHolder = holder;
        return this;
    }

    public String getIdType() {
        return idType == null ? "" : idType;
    }

    public String getIdHolder() {
        return idHolder == null ? "" : idHolder;
    }

    /**
     * Return the dLocation of this inventory's
     * holder
     *
     * @return  The holder's dLocation
     *
     */

    public dLocation getLocation() {
        return getLocation(inventory.getHolder());
    }

    public dLocation getLocation(InventoryHolder holder) {
        if (inventory != null && holder != null) {
            if (holder instanceof Chest) {
                return new dLocation(((Chest) holder).getLocation());
            }
            else if (holder instanceof DoubleChest) {
                return new dLocation(((DoubleChest) holder).getLocation());
            }
            else if (holder instanceof BrewingStand) {
                return new dLocation(((BrewingStand) holder).getLocation());
            }
            else if (holder instanceof Furnace) {
                return new dLocation(((Furnace) holder).getLocation());
            }
            else if (holder instanceof Entity) {
                return new dLocation(((Entity) holder).getLocation());
            }
            else if (holder instanceof dNPC) {
                dNPC npc = (dNPC) holder;
                if (npc.getLocation() == null)
                    return new dLocation(((dNPC) holder).getCitizen().getStoredLocation());
                return npc.getLocation();
            }
        }

        return null;
    }

    public ItemStack[] getContents() {
        if (inventory != null) return inventory.getContents();
        else return new ItemStack[0];
    }

    public dList getEquipment() {
        ItemStack[] equipment = null;
        if (inventory instanceof PlayerInventory) {
            equipment = ((PlayerInventory) inventory).getArmorContents();
        }
        else if (inventory instanceof HorseInventory) {
            equipment = new ItemStack[]{((HorseInventory) inventory).getSaddle(), ((HorseInventory) inventory).getArmor()};
        }
        if (equipment == null)
            return null;
        dList equipmentList = new dList();
        for (ItemStack item : equipment) {
            equipmentList.add(new dItem(item).identify());
        }
        return equipmentList;
    }

    public InventoryType getInventoryType() {
        return inventory.getType();
    }

    public int getSize() {
        return inventory.getSize();
    }

    public void remove(ItemStack item) {
        inventory.remove(item);
    }

    public void setContents(ItemStack[] contents) {
        inventory.setContents(contents);
    }

    public void setContents(dList list) {
        int size;
        if (inventory == null) {
            size = (int) Math.ceil(list.size() / 9)*9;
            if (size == 0) size = 9;
            inventory = Bukkit.getServer().createInventory(null, size);
            loadIdentifiers();
        }
        else
            size = inventory.getSize();
        ItemStack[] contents = new ItemStack[size];
        int filled = 0;
        for (dItem item : list.filter(dItem.class)) {
            contents[filled] = item.getItemStack();
            filled++;
        }
        final ItemStack air = new ItemStack(Material.AIR);
        while (filled < size) {
            contents[filled] = air;
            filled ++;
        }
        inventory.setContents(contents);
    }

    public boolean update() {
        if (getIdType().equals("player")) {
            dPlayer.valueOf(idHolder).getPlayerEntity().updateInventory();
            return true;
        }
        return false;
    }

    public int firstPartial(int startSlot, ItemStack item) {
        ItemStack[] inventory = getContents();
        if (item == null) {
            return -1;
        }
        for (int i = startSlot; i < inventory.length; i++) {
            ItemStack item1 = inventory[i];
            if (item1 != null && item1.getAmount() < item.getMaxStackSize() && item1.isSimilar(item)) {
                return i;
            }
        }
        return -1;
    }

    public int firstEmpty(int startSlot) {
        ItemStack[] inventory = getContents();
        for (int i = startSlot; i < inventory.length; i++) {
            if (inventory[i] == null) {
                return i;
            }
        }
        return -1;
    }

    /////////////////////
    //   INVENTORY MANIPULATION
    /////////////////

    // Somewhat simplified version of CraftBukkit's current code
    public dInventory add(int slot, ItemStack... items) {
        if (inventory == null || items == null) return this;

        for (int i = 0; i < items.length; i++) {
            ItemStack item = items[i];
            if (item == null) continue;
            int amount = item.getAmount();
            int max = item.getMaxStackSize();
            while (true) {
                // Do we already have a stack of it?
                int firstPartial = firstPartial(slot, item);

                // Drat! no partial stack
                if (firstPartial == -1) {
                    // Find a free spot!
                    int firstFree = firstEmpty(slot);

                    if (firstFree == -1) {
                        // No space at all!
                        break;
                    } else {
                        // More than a single stack!
                        if (amount > max) {
                            ItemStack clone = item.clone();
                            clone.setAmount(max);
                            inventory.setItem(firstFree, clone);
                            item.setAmount(amount -= max);
                        } else {
                            // Just store it
                            inventory.setItem(firstFree, item);
                            break;
                        }
                    }
                } else {
                    // So, apparently it might only partially fit, well lets do just that
                    ItemStack partialItem = inventory.getItem(firstPartial);

                    int partialAmount = partialItem.getAmount();
                    int total = amount + partialAmount;

                    // Check if it fully fits
                    if (total <= max) {
                        partialItem.setAmount(total);
                        break;
                    }

                    // It fits partially
                    partialItem.setAmount(max);
                    item.setAmount(amount = total - max);
                }
            }
        }

        return this;
    }

    public List<ItemStack> addWithLeftovers(int slot, boolean keepMaxStackSize, ItemStack... items) {
        if (inventory == null || items == null) return null;

        List<ItemStack> leftovers = new ArrayList<ItemStack>();

        for (int i = 0; i < items.length; i++) {
            ItemStack item = items[i];
            if (item == null) continue;
            int amount = item.getAmount();
            int max;
            if (keepMaxStackSize)
                max = item.getMaxStackSize();
            else
                max = 64;
            while (true) {
                // Do we already have a stack of it?
                int firstPartial = firstPartial(slot, item);

                // Drat! no partial stack
                if (firstPartial == -1) {
                    // Find a free spot!
                    int firstFree = firstEmpty(slot);

                    if (firstFree == -1) {
                        // No space at all!
                        leftovers.add(item);
                        break;
                    } else {
                        // More than a single stack!
                        if (amount > max) {
                            ItemStack clone = item.clone();
                            clone.setAmount(max);
                            inventory.setItem(firstFree, clone);
                            item.setAmount(amount -= max);
                        } else {
                            // Just store it
                            inventory.setItem(firstFree, item);
                            break;
                        }
                    }
                } else {
                    // So, apparently it might only partially fit, well lets do just that
                    ItemStack partialItem = inventory.getItem(firstPartial);

                    int partialAmount = partialItem.getAmount();
                    int total = amount + partialAmount;

                    // Check if it fully fits
                    if (total <= max) {
                        partialItem.setAmount(total);
                        break;
                    }

                    // It fits partially
                    partialItem.setAmount(max);
                    item.setAmount(amount = total - max);
                }
            }
        }

        return leftovers;
    }

    public List<ItemStack> setWithLeftovers(int slot, ItemStack... items) {
        if (inventory == null || items == null) return null;

        List<ItemStack> leftovers = new ArrayList<ItemStack>();

        for (int i = 0; i < items.length; i++) {
            ItemStack item = items[i];
            try {
                inventory.setItem(i+slot, item);
            } catch (Exception e) {
                leftovers.add(i+slot, item);
            }
        }

        return leftovers;
    }

    /**
     * Count the number or quantities of stacks that
     * match an item in an inventory.
     *
     * @param item  The item (can be null)
     * @param stacks  Whether stacks should be counted
     *                   instead of item quantities
     * @return  The number of stacks or quantity of items
     *
     */

    public int count(ItemStack item, boolean stacks)
    {
        if (inventory == null) return 0;

        int qty = 0;

        for (ItemStack invStack : inventory)
        {
            // If ItemStacks are empty here, they are null
            if (invStack != null)
            {
                // If item is null, include all items in the
                // inventory

                if (item == null || invStack.isSimilar(item)) {

                    // If stacks is true, only count the number
                    // of stacks
                    //
                    // Otherwise, count the quantities of stacks

                    if (stacks) qty++;
                    else qty = qty + invStack.getAmount();
                }
            }
        }

        return qty;
    }

    /**
     * Keep only the items from a certain array
     * in this inventory, removing all others
     *
     * @param items  The array of items
     * @return  The resulting dInventory
     *
     */

    public dInventory keep(ItemStack[] items) {

        if (inventory == null || items == null) return this;

        for (ItemStack invStack : inventory) {

            if (invStack != null) {

                boolean keep = false;

                // See if the item array contains
                // this inventory item
                for (ItemStack item : items) {

                    if (invStack.isSimilar(item)) {

                        keep = true;
                        break;
                    }
                }

                // If the item array did not contain
                // this inventory item, remove it
                // from the inventory
                if (!keep) {

                    this.remove(invStack);
                }
            }
        }

        return this;
    }

    /**
     * Exclude an array of items from this
     * inventory by removing them over and over
     * until they are completely gone
     *
     * @param items  The array of items
     * @return  The resulting dInventory
     *
     */

    public dInventory exclude(ItemStack[] items) {

        if (inventory == null || items == null) return this;

        int oldCount = this.count(null, false);
        int newCount = -1;

        while (oldCount != newCount) {

            oldCount = newCount;
            newCount = this.remove(items).count(null, false);
        }

        return this;
    }

    /**
     * Fill an inventory with an array of items by
     * continuing to add the items to it over and
     * over until there is no more room
     *
     * @param items  The array of items
     * @return  The resulting dInventory
     *
     */

    public dInventory fill(ItemStack[] items) {

        if (inventory == null || items == null) return this;

        int oldCount = this.count(null, false);
        int newCount = -1;

        while (oldCount != newCount) {

            oldCount = newCount;
            newCount = this.add(0, items).count(null, false);
        }

        return this;
    }

    /**
     * Remove an array of items from this inventory,
     * and return the result
     *
     * @param items  The array of items
     * @return  The resulting dInventory
     *
     */

    public dInventory remove(ItemStack[] items) {

        if (inventory == null || items == null) return this;

        for (ItemStack item : items) {
            if (item != null) inventory.removeItem(item);
        }

        return this;
    }

    /**
     * Remove a book from this inventory, comparing
     * only its title and author with books in the
     * inventory, but ignoring its text, thus having
     * Denizen support for updatable quest journals
     * and their like
     *
     * @param   book  The itemStack of the book
     * @return  The resulting dInventory
     *
     */

    public dInventory removeBook(ItemStack book) {

        if (inventory == null || book == null) return this;

        // We have to manually keep track of the quantity
        // we are removing, because we are not relying on
        // Bukkit methods to find matching itemStacks
        int qty = book.getAmount();

        // Store the book's meta information in a variable
        BookMeta bookMeta = (BookMeta) book.getItemMeta();

        for (ItemStack invStack : inventory) {

            if (qty == 0) break;

            if (invStack != null && invStack.getItemMeta() instanceof BookMeta) {

                BookMeta invMeta = (BookMeta) invStack.getItemMeta();

                if (invMeta.getAuthor().equalsIgnoreCase(bookMeta.getAuthor())
                        && invMeta.getTitle().equalsIgnoreCase(bookMeta.getTitle())) {

                    // Make sure we don't remove more books than we
                    // need to
                    if (qty - invStack.getAmount() < 0) {

                        invStack.setAmount((qty - invStack.getAmount()) * -1);
                    }
                    else {

                        inventory.removeItem(invStack);

                        // Update the quantity we still have to remove
                        qty = qty - invStack.getAmount();
                    }
                }
            }
        }

        return this;
    }

    /**
     * Replace another inventory with this one,
     * cropping it if necessary so that it fits.
     *
     * @param destination  The destination inventory
     *
     */

    public void replace(dInventory destination) {

        if (inventory == null || destination == null) return;

        // If the destination is smaller than our current inventory,
        // add as many items as possible

        if (destination.getSize() < this.getSize()) {

            destination.clear();
            destination.add(0, this.getContents());
        }
        else {

            destination.setContents(this.getContents());
        }
    }

    /**
     * Set items in an inventory, starting with a specified slot
     *
     * @param slot  The slot to start from
     * @param items  The items to add
     * @return  The resulting dInventory
     */
    public dInventory setSlots(int slot, ItemStack... items) {

        if (inventory == null || items == null) return this;

        for (int i = 0; i < items.length; i++) {
            ItemStack item = items[i];
            if (item == null) continue;
            inventory.setItem(slot+i, item);
        }

        return this;

    }

    public void clear() {
        if (inventory != null) inventory.clear();
    }

    ////////////////////////
    //  dObject Methods
    /////////////////////

    private String prefix = getObjectType();

    @Override
    public String getObjectType() {
        return "Inventory";
    }


    @Override
    public String getPrefix() {
        return prefix;
    }


    @Override
    public dInventory setPrefix(String prefix) {
        this.prefix = prefix;
        return this;
    }


    @Override
    public String debug() {
        return "<G>" + prefix + "='<Y>" + identify() + "<G>'  ";
    }


    @Override
    public String identify() {
        if (isUnique())
            return "in@" + NotableManager.getSavedId(this);
        else return "in@" + (getIdType().equals("script") ? idHolder
                : (idType + PropertyParser.getPropertiesString(this)));
    }


    @Override
    public String identifySimple() {
        if (isUnique())
            return "in@" + NotableManager.getSavedId(this);
        else return "in@" + (getIdType().equals("script") || getIdType().equals("notable")
                ? idHolder : (idType + "[" + idHolder + ']'));
    }


    @Override
    public String toString() {
        return identify();
    }

    ////////////////////////
    //  Attributes
    /////////////////////


    public String getAttribute(Attribute attribute) {

        if (attribute == null) return null;

        // <--[tag]
        // @attribute <in@inventory.contains.display[(strict:)<element>]>
        // @returns Element(Boolean)
        // @description
        // Returns whether the inventory contains an item with the specified display
        // name. Use 'strict:' in front of the search element to ensure the display
        // name is EXACTLY the search element, otherwise the searching will only
        // check if the search element is contained in the display name.
        // -->
        if (attribute.startsWith("contains.display")) {
            if (attribute.hasContext(2)) {
                int qty = 1;
                int attribs = 2;
                String search_string = attribute.getContext(2);
                boolean strict = false;
                if (search_string.toLowerCase().startsWith("strict:") && search_string.length() > 7) {
                    strict = true;
                    search_string = search_string.substring(7);
                }

                // <--[tag]
                // @attribute <in@inventory.contains.display[(strict:)<element>].qty[<#>]>
                // @returns Element(Boolean)
                // @description
                // Returns whether the inventory contains a certain quantity of an item with the
                // specified display name. Use 'strict:' in front of the search element to ensure
                // the display name is EXACTLY the search element, otherwise the searching will only
                // check if the search element is contained in the display name.
                // -->
                if (attribute.getAttribute(3).startsWith("qty") &&
                        attribute.hasContext(3) &&
                        aH.matchesInteger(attribute.getContext(3))) {
                    qty = attribute.getIntContext(3);
                    attribs = 3;
                }

                int found_items = 0;

                if (strict) {
                    for (ItemStack item : getContents()) {
                        if (item != null && item.hasItemMeta() && item.getItemMeta().hasDisplayName() &&
                                item.getItemMeta().getDisplayName().equalsIgnoreCase(search_string)) {
                            found_items += item.getAmount();
                            if (found_items >= qty) break;
                        }
                    }
                } else {
                    for (ItemStack item : getContents()) {
                        if (item != null && item.hasItemMeta() && item.getItemMeta().hasDisplayName() &&
                                item.getItemMeta().getDisplayName().toLowerCase()
                                        .contains(search_string.toLowerCase())) {
                            found_items += item.getAmount();
                            if (found_items >= qty) break;
                        }
                    }
                }

                return (found_items >= qty ? Element.TRUE.getAttribute(attribute.fulfill(attribs))
                        : Element.FALSE.getAttribute(attribute.fulfill(attribs)));
            }
        }

        // <--[tag]
        // @attribute <in@inventory.contains.lore[(strict:)<element>|...]>
        // @returns Element(Boolean)
        // @description
        // Returns whether the inventory contains an item with the specified lore.
        // Use 'strict:' in front of the search elements to ensure all lore lines
        // are EXACTLY the search elements, otherwise the searching will only
        // check if the search elements are contained in the lore.
        // -->
        if (attribute.startsWith("contains.lore")) {
            if (attribute.hasContext(2)) {
                int qty = 1;
                int attribs = 2;
                String search_string = attribute.getContext(2);
                boolean strict = false;
                if (search_string.toLowerCase().startsWith("strict:")) {
                    strict = true;
                    search_string = search_string.substring(7);
                }
                dList lore = dList.valueOf(search_string);

                // <--[tag]
                // @attribute <in@inventory.contains.lore[(strict:)<element>|...].qty[<#>]>
                // @returns Element(Boolean)
                // @description
                // Returns whether the inventory contains a certain quantity of an item
                // with the specified lore. Use 'strict:' in front of the search elements
                // to ensure all lore lines are EXACTLY the search elements, otherwise the
                // searching will only check if the search elements are contained in the lore.
                // -->
                if (attribute.getAttribute(3).startsWith("qty") &&
                        attribute.hasContext(3) &&
                        aH.matchesInteger(attribute.getContext(3))) {
                    qty = attribute.getIntContext(3);
                    attribs = 3;
                }

                int found_items = 0;

                if (strict) {
                    strict_items: for (ItemStack item : getContents()) {
                        if (item != null && item.hasItemMeta() && item.getItemMeta().hasLore()) {
                            List<String> item_lore = item.getItemMeta().getLore();
                            if (lore.size() != item_lore.size()) continue;
                            for (int i = 0; i < item_lore.size(); i++) {
                                if (lore.get(i).equalsIgnoreCase(item_lore.get(i))) {
                                    if (i == lore.size()) {
                                        found_items += item.getAmount();
                                        if (found_items >= qty) break strict_items;
                                    }
                                }
                                else continue strict_items;
                            }
                        }
                    }
                } else {
                    for (ItemStack item : getContents()) {
                        if (item != null && item.hasItemMeta() && item.getItemMeta().hasLore()) {
                            List<String> item_lore = item.getItemMeta().getLore();
                            int loreCount = 0;
                            lines: for (String line : lore) {
                                for (String item_line : item_lore) {
                                    if (item_line.toLowerCase().contains(line.toLowerCase())) {
                                        loreCount++;
                                        continue lines;
                                    }
                                }
                            }
                            if (loreCount == lore.size()) {
                                found_items += item.getAmount();
                                if (found_items >= qty) break;
                            }
                        }
                    }
                }

                return (found_items >= qty ? Element.TRUE.getAttribute(attribute.fulfill(attribs))
                        : Element.FALSE.getAttribute(attribute.fulfill(attribs)));
            }
        }

        // <--[tag]
        // @attribute <in@inventory.contains.material[<material>]>
        // @returns Element(Boolean)
        // @description
        // Returns whether the inventory contains an item with the specified material.
        // -->
        if (attribute.startsWith("contains.material")) {
            if (attribute.hasContext(2)) {
                int qty = 1;
                int attribs = 2;
                dMaterial material = dMaterial.valueOf(attribute.getContext(2));

                // <--[tag]
                // @attribute <in@inventory.contains.material[<material>].qty[<#>]>
                // @returns Element(Boolean)
                // @description
                // Returns whether the inventory contains a certain quantity of an item with the
                // specified material.
                // -->
                if (attribute.getAttribute(3).startsWith("qty") &&
                        attribute.hasContext(3) &&
                        aH.matchesInteger(attribute.getContext(3))) {
                    qty = attribute.getIntContext(3);
                    attribs = 3;
                }

                int found_items = 0;

                for (ItemStack item : getContents()) {
                    if (item != null && item.getType() == material.getMaterial()) {
                        found_items += item.getAmount();
                        if (found_items >= qty) break;
                    }
                }

                return (found_items >= qty ? Element.TRUE.getAttribute(attribute.fulfill(attribs))
                        : Element.FALSE.getAttribute(attribute.fulfill(attribs)));
            }
        }

        // <--[tag]
        // @attribute <in@inventory.contains_any[<item>|...]>
        // @returns Element(Boolean)
        // @description
        // Returns whether the inventory contains any of the specified items.
        // -->
        if (attribute.startsWith("contains_any")) {
            if (attribute.hasContext(1) && dList.matches(attribute.getContext(1))) {
                int qty = 1;
                int attribs = 1;

                // <--[tag]
                // @attribute <in@inventory.contains_any[<item>|...].qty[<#>]>
                // @returns Element(Boolean)
                // @description
                // Returns whether the inventory contains a certain quantity of any of the specified items.
                // -->
                if (attribute.getAttribute(2).startsWith("qty")
                        && attribute.hasContext(2) && aH.matchesInteger(attribute.getContext(2))) {
                    qty = attribute.getIntContext(2);
                    attribs = 2;
                }

                for (dItem item : dList.valueOf(attribute.getContext(1)).filter(dItem.class, attribute.getScriptEntry())) {
                    if (getInventory().containsAtLeast(item.getItemStack(), qty))
                        return Element.TRUE.getAttribute(attribute.fulfill(attribs));
                }
                return Element.FALSE.getAttribute(attribute.fulfill(attribs));
            }
        }

        // <--[tag]
        // @attribute <in@inventory.contains[<item>|...]>
        // @returns Element(Boolean)
        // @description
        // Returns whether the inventory contains all of the specified items.
        // -->
        if (attribute.startsWith("contains")) {
            if (attribute.hasContext(1) && dList.matches(attribute.getContext(1))) {
                int qty = 1;
                int attribs = 1;

                // <--[tag]
                // @attribute <in@inventory.contains[<item>|...].qty[<#>]>
                // @returns Element(Boolean)
                // @description
                // Returns whether the inventory contains a certain quantity of all of the specified items.
                // -->
                if (attribute.getAttribute(2).startsWith("qty")
                        && attribute.hasContext(2) && aH.matchesInteger(attribute.getContext(2))) {
                    qty = attribute.getIntContext(2);
                    attribs = 2;
                }

                for (dItem item : dList.valueOf(attribute.getContext(1)).filter(dItem.class, attribute.getScriptEntry())) {
                    if (!getInventory().containsAtLeast(item.getItemStack(), qty))
                        return Element.FALSE.getAttribute(attribute.fulfill(attribs));
                }
                return Element.TRUE.getAttribute(attribute.fulfill(attribs));
            }
        }

        // <--[tag]
        // @attribute <in@inventory.find.material[<material>]>
        // @returns Element(Number)
        // @description
        // Returns the location of the first slot that contains the material.
        // Returns -1 if there's no match.
        // -->
        if (attribute.startsWith("find.material")
                && attribute.hasContext(2)
                && dMaterial.matches(attribute.getContext(2))) {
            dMaterial material = dMaterial.valueOf(attribute.getContext(2));
            int slot = -1;
            for (int i = 0; i < inventory.getSize(); i++) {
                if (inventory.getItem(i) != null && inventory.getItem(i).getType() == material.getMaterial()) {
                    slot = i + 1;
                    break;
                }
            }
            return new Element(slot).getAttribute(attribute.fulfill(2));
        }

        // <--[tag]
        // @attribute <in@inventory.find[<item>]>
        // @returns Element(Number)
        // @description
        // Returns the location of the first slot that contains the item.
        // Returns -1 if there's no match.
        // -->
        if (attribute.startsWith("find")
                && attribute.hasContext(1)
                && dItem.matches(attribute.getContext(1))) {
                dItem item = dItem.valueOf(attribute.getContext(1), attribute.getScriptEntry().getPlayer(), attribute.getScriptEntry().getNPC());
            item.setAmount(1);
            int slot = -1;
            for (int i = 0; i < inventory.getSize(); i++) {
                if (inventory.getItem(i) != null) {
                    dItem compare_to = new dItem(inventory.getItem(i).clone());
                    compare_to.setAmount(1);
                    if (item.identify().equalsIgnoreCase(compare_to.identify())) {
                        slot = i + 1;
                        break;
                    }
                }
            }
            return new Element(slot).getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <in@inventory.id_type>
        // @returns Element
        // @description
        // Returns Denizen's type ID for this inventory. (player, location, etc.)
        // -->
        if (attribute.startsWith("id_type")) {
            return new Element(idType).getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <in@inventory.notable_name>
        // @returns Element
        // @description
        // Gets the name of a Notable dInventory. If the inventory isn't noted,
        // this is null.
        // -->
        if (attribute.startsWith("notable_name")) {
            return new Element(NotableManager.getSavedId(this)).getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <in@inventory.location>
        // @returns dLocation
        // @description
        // Returns the location of this inventory's holder.
        // -->
        if (attribute.startsWith("location")) {
            dLocation location = getLocation();
            if (location != null)
                return location.getAttribute(attribute.fulfill(1));
            else
                return null;
        }

        // <--[tag]
        // @attribute <in@inventory.qty[<item>]>
        // @returns Element(Number)
        // @description
        // Returns the combined quantity of itemstacks that match an item if
        // one if specified, or the combined quantity of all itemstacks
        // if one is not.
        // -->
        if (attribute.startsWith("qty"))
            if (attribute.hasContext(1) && dItem.matches(attribute.getContext(1)))
                return new Element(count
                        (dItem.valueOf(attribute.getContext(1), attribute.getScriptEntry().getPlayer(), attribute.getScriptEntry().getNPC()).getItemStack(), false))
                        .getAttribute(attribute.fulfill(1));
            else
                return new Element(count(null, false))
                        .getAttribute(attribute.fulfill(1));

        // <--[tag]
        // @attribute <in@inventory.stacks[<item>]>
        // @returns Element(Number)
        // @description
        // Returns the number of itemstacks that match an item if one is
        // specified, or the number of all itemstacks if one is not.
        // -->
        if (attribute.startsWith("stacks"))
            if (attribute.hasContext(1) && dItem.matches(attribute.getContext(1)))
                return new Element(count
                        (dItem.valueOf(attribute.getContext(1), attribute.getScriptEntry().getPlayer(), attribute.getScriptEntry().getNPC()).getItemStack(), true))
                        .getAttribute(attribute.fulfill(1));
            else
                return new Element(count(null, true))
                        .getAttribute(attribute.fulfill(1));

        // <--[tag]
        // @attribute <in@inventory.slot[<#>]>
        // @returns dItem
        // @description
        // Returns the item in the specified slot.
        // -->
        if (attribute.startsWith("slot")
                &&attribute.hasContext(1)
                && aH.matchesInteger(attribute.getContext(1))) {
            int slot = new Element(attribute.getContext(1)).asInt() - 1;
            if (slot < 0) slot = 0;
            if (slot > getInventory().getSize() - 1) slot = getInventory().getSize() - 1;
            return new dItem(getInventory().getItem(slot))
                    .getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <in@inventory.inventory_type>
        // @returns Element
        // @description
        // Returns the type of the inventory (e.g. "PLAYER", "CRAFTING", "HORSE").
        // -->
        if (attribute.startsWith("inventory_type"))
            return new Element(getInventory().getType().name())
                    .getAttribute(attribute.fulfill(1));

        // <--[tag]
        // @attribute <in@inventory.equipment>
        // @returns dList(dItem)
        // @description
        // Returns the equipment of an inventory.
        // -->
        if (attribute.startsWith("equipment")) {
            dList equipment = getEquipment();
            if (equipment == null)
                return null;
            else
                return equipment.getAttribute(attribute.fulfill(1));
        }

        if (inventory instanceof CraftingInventory) {
            CraftingInventory craftingInventory = (CraftingInventory) inventory;

            // <--[tag]
            // @attribute <in@inventory.matrix>
            // @returns dList(dItem)
            // @description
            // Returns the dItems currently in a crafting inventory's matrix.
            // -->
            if (attribute.startsWith("matrix")) {
                dList recipeList = new dList();
                for (ItemStack item : craftingInventory.getMatrix()) {
                    if (item != null)
                        recipeList.add(new dItem(item).identify());
                    else
                        recipeList.add(new dItem(Material.AIR).identify());
                }
                return recipeList.getAttribute(attribute.fulfill(1));
            }

            // <--[tag]
            // @attribute <in@inventory.result>
            // @returns dItem
            // @description
            // Returns the dItem currently in the result section of a crafting inventory.
            // -->
            if (attribute.startsWith("result")) {
                if (craftingInventory.getResult() != null)
                    return new dItem(craftingInventory.getResult()).getAttribute(attribute.fulfill(1));
                else
                    return null;
            }
        }

        // <--[tag]
        // @attribute <in@inventory.type>
        // @returns Element
        // @description
        // Always returns 'Inventory' for dInventory objects. All objects fetchable by the Object Fetcher will return the
        // type of object that is fulfilling this attribute.
        // -->
        if (attribute.startsWith("type")) {
            return new Element("Inventory").getAttribute(attribute.fulfill(1));
        }

        // Iterate through this object's properties' attributes
        for (Property property : PropertyParser.getProperties(this)) {
            String returned = property.getAttribute(attribute);
            if (returned != null) return returned;
        }

        return new Element(identify()).getAttribute(attribute);
    }

    private ArrayList<Mechanism> mechanisms = new ArrayList<Mechanism>();

    public void applyProperty(Mechanism mechanism) {
        if (idType == null)
            mechanisms.add(mechanism);
        else if (idType.equals("generic") || mechanism.matches("holder"))
            adjust(mechanism);
        else if (!(idType.equals("location") && mechanism.matches("title")))
            dB.echoError("Cannot apply properties to non-generic inventory!");
    }

    @Override
    public void adjust(Mechanism mechanism) {

        // Iterate through this object's properties' mechanisms
        for (Property property : PropertyParser.getProperties(this)) {
            property.adjust(mechanism);
            if (mechanism.fulfilled())
                break;
        }

        if (inventory instanceof CraftingInventory) {
            CraftingInventory craftingInventory = (CraftingInventory) inventory;

            // <--[mechanism]
            // @object dInventory
            // @name matrix
            // @input dList(dItem)
            // @description
            // Sets the items in the matrix slots of this crafting inventory.
            // @tags
            // <in@inventory.matrix>
            // -->
            if (mechanism.matches("matrix") && mechanism.requireObject(dList.class)) {
                List<dItem> items = mechanism.getValue().asType(dList.class).filter(dItem.class);
                ItemStack[] itemStacks = new ItemStack[9];
                for (int i = 0; i < 9 && i < items.size(); i++) {
                    itemStacks[i] = items.get(i).getItemStack();
                }
                craftingInventory.setMatrix(itemStacks);
                ((Player) inventory.getHolder()).updateInventory();
            }

            // <--[mechanism]
            // @object dInventory
            // @name result
            // @input dItem
            // @description
            // Sets the item in the result slot of this crafting inventory.
            // @tags
            // <in@inventory.result>
            // -->
            if (mechanism.matches("result") && mechanism.requireObject(dItem.class)) {
                craftingInventory.setResult(mechanism.getValue().asType(dItem.class).getItemStack());
                ((Player) inventory.getHolder()).updateInventory();
            }
        }

        if (!mechanism.fulfilled())
            mechanism.reportInvalid();

    }
}
TOP

Related Classes of net.aufdemrand.denizen.objects.dInventory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.