Package net.aufdemrand.denizen.scripts.containers.core

Source Code of net.aufdemrand.denizen.scripts.containers.core.ItemScriptHelper

package net.aufdemrand.denizen.scripts.containers.core;

import net.aufdemrand.denizen.events.EventManager;
import net.aufdemrand.denizen.objects.*;
import net.aufdemrand.denizen.utilities.DenizenAPI;
import net.aufdemrand.denizen.utilities.debugging.dB;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.inventory.InventoryType.SlotType;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.inventory.CraftingInventory;
import org.bukkit.inventory.ItemStack;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ItemScriptHelper implements Listener {

    public static Map<String, ItemScriptContainer> item_scripts = new ConcurrentHashMap<String, ItemScriptContainer>(8, 0.9f, 1);

    public static Map<String, ItemScriptContainer> item_scripts_by_hash_id = new HashMap<String, ItemScriptContainer>();

    public ItemScriptHelper() {
        DenizenAPI.getCurrentInstance().getServer().getPluginManager()
                .registerEvents(this, DenizenAPI.getCurrentInstance());
    }

    // Remove all recipes stored by Denizen
    public static void removeDenizenRecipes() {
        ItemScriptContainer.specialrecipesMap.clear();
    }

    public static boolean isBound(ItemStack item) {
        return (isItemscript(item) && getItemScriptContainer(item).bound);
    }

    public static boolean isItemscript(ItemStack item) {
        return getItemScriptContainer(item) != null;
    }

    public static ItemScriptContainer getItemScriptContainer(ItemStack item) {
        if (item == null || !item.hasItemMeta() || !item.getItemMeta().hasLore())
            return null;
        for (String itemLore : item.getItemMeta().getLore()) {
            if (itemLore.startsWith(dItem.itemscriptIdentifier))
                return item_scripts.get(itemLore.replace(dItem.itemscriptIdentifier, ""));
            if (itemLore.startsWith(ItemScriptHashID))
                return item_scripts_by_hash_id.get(itemLore);
        }
        return null;
    }

    public static String ItemScriptHashID = ChatColor.RED.toString() + ChatColor.BLUE + ChatColor.BLACK;

    public static String createItemScriptID(ItemScriptContainer container) {
        String colors = createItemScriptID(container.getName());
        container.setHashID(colors);
        return colors;
    }
    public static String createItemScriptID(String name) {
        String script = name.toUpperCase();
        StringBuilder colors = new StringBuilder();
        colors.append(ItemScriptHashID);
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] bytes = script.getBytes("UTF-8");
            md.update(bytes, 0, bytes.length);
            String hash = new BigInteger(1, md.digest()).toString(16);
            for (int i = 0; i < 16; i++) {
                colors.append(ChatColor.COLOR_CHAR).append(hash.charAt(i));
            }
        }
        catch (Exception ex) {
            dB.echoError(ex);
            colors.append(ChatColor.BLUE);
        }
        return colors.toString();
    }

    // When special Denizen recipes that have itemscripts as ingredients
    // are being used, check crafting matrix for recipe matches whenever
    // clicks are made in CRAFTING or RESULT slots
    @EventHandler
    public void specialRecipeClick(InventoryClickEvent event) {

        // Proceed only if at least one special recipe has been stored
        if (ItemScriptContainer.specialrecipesMap.isEmpty())
            return;

        // Proceed only if this is a CraftingInventory
        if (!(event.getInventory() instanceof CraftingInventory))
            return;

        // Store the slot type that was clicked
        SlotType slotType = event.getSlotType();

        // Proceed only if a CRAFTING or RESULT slot was clicked
        if (slotType.equals(InventoryType.SlotType.CRAFTING) ||
            slotType.equals(InventoryType.SlotType.RESULT)) {

            CraftingInventory inventory = (CraftingInventory) event.getInventory();
            Player player = (Player) event.getWhoClicked();

            // If the RESULT slot was shift-clicked, emulate
            // shift click behavior for it
            if (slotType.equals(InventoryType.SlotType.RESULT) &&
                event.isShiftClick()) {
                emulateSpecialRecipeResultShiftClick(inventory, player);
            }
            // Otherwise check for special recipe matches
            else {
                processSpecialRecipes(inventory, player);
            }
        }
    }

    // When special Denizen recipes that have itemscripts as ingredients
    // are being used, check crafting matrix for recipe matches whenever
    // drags (which are entirely separate from clicks) are made in CRAFTING slots
    @EventHandler
    public void specialRecipeDrag(InventoryDragEvent event) {

        // Proceed only if at least one special recipe has been stored
        if (ItemScriptContainer.specialrecipesMap.isEmpty())
            return;

        // Proceed only if this is a CraftingInventory
        if (!(event.getInventory() instanceof CraftingInventory))
            return;

        // Check for special recipe matches if the drag involved a CRAFTING slot,
        // which can have an ID of between 1 and 9 in a CraftingInventory
        for (Integer slot : event.getInventorySlots()) {
            if (slot < 10) {

                CraftingInventory inventory = (CraftingInventory) event.getInventory();
                Player player = (Player) event.getWhoClicked();
                processSpecialRecipes(inventory, player);
                break;
            }
        }
    }

    // Compare a crafting matrix with all stored special recipes right
    // after a click or drag has been made in it
    public void processSpecialRecipes(final CraftingInventory inventory, final Player player) {

        // Run a task 1 tick later than the event from which this method
        // was called, to check the new state of the CraftingInventory's matrix
        Bukkit.getScheduler().scheduleSyncDelayedTask(DenizenAPI.getCurrentInstance(),
        new Runnable() {
            @Override
            public void run() {
                // Store the current matrix
                ItemStack[] matrix = inventory.getMatrix();

                // Get the result of the special recipe that this matrix matches,
                // if any
                dItem result = getSpecialRecipeResult(matrix);

                // Proceed only if the result was not null
                if (result != null) {
                    Map<String, dObject> context = new HashMap<String, dObject>();
                    context.put("inventory", new dInventory(inventory));
                    context.put("item", result);

                    dList recipeList = new dList();
                    for (ItemStack item : inventory.getMatrix()) {
                        if (item != null)
                            recipeList.add(new dItem(item).identify());
                        else
                            recipeList.add(new dItem(Material.AIR).identify());
                    }
                    context.put("recipe", recipeList);

                    String determination = EventManager.doEvents(Arrays.asList
                            ("item crafted",
                                    result.identifySimple() + " crafted",
                                    result.identifyMaterial() + " crafted"),
                            null, new dPlayer(player), context);

                    if (determination.toUpperCase().startsWith("CANCELLED"))
                        return;
                    else if (dItem.matches(determination)) {
                        result = dItem.valueOf(determination);
                    }

                    // If this was a valid match, set the crafting's result
                    inventory.setResult(result.getItemStack());

                    // Update the player's inventory
                    //
                    // TODO: Replace with non-deprecated method once one
                    // is added to Bukkit
                    player.updateInventory();
                }
            }
        }, 0);
    }

    // Check if a CraftingInventory's crafting matrix matches a special
    // recipe and return that recipe's dItem result if it does
    public dItem getSpecialRecipeResult(ItemStack[] matrix) {

        // Iterate through all the special recipes
        master: for (Map.Entry<dItem, dList> entry :
                ItemScriptContainer.specialrecipesMap.entrySet()) {

            // Check if the two sets of items match each other
            for (int n = 0; n < 9; n++) {

                // Use dItem.valueOf on the entry values to ensure
                // correct comparison
                dItem valueN = dItem.valueOf(entry.getValue().get(n));
                dItem matrixN = matrix.length <= n || matrix[n] == null ? new dItem(Material.AIR): new dItem(matrix[n].clone());

                // If one's an item script and the other's not, it's a fail
                if (valueN.isItemscript() != matrixN.isItemscript())
                    continue master;
                // If they're both item scripts, and they are different scripts, it's a fail
                if (valueN.isItemscript() && matrixN.isItemscript()) {
                        if (!valueN.getScriptName().equalsIgnoreCase(matrixN.getScriptName()))
                    continue master;
                }
                // If they're both not item scripts, and the materials are different, it's a fail
                else if (!valueN.getMaterial().matchesMaterialData(matrixN.getMaterial().getMaterialData()))
                    continue master;
            }

            // If all the items match, return the special recipe's dItem key
            return entry.getKey();
        }

        return null;
    }

    // Because Denizen special recipes are basically fake recipes,
    // shift clicking the result slot will not work by itself and needs
    // to be emulated like below
    public void emulateSpecialRecipeResultShiftClick(CraftingInventory inventory, Player player) {

        // Store the crafting matrix
        ItemStack[] matrix = inventory.getMatrix();

        // Get the result of the special recipe that this matrix matches,
        // if any
        dItem result = getSpecialRecipeResult(matrix);

        // Proceed only if the result was not null
        if (result != null) {

            // In a shift click, the amount of the resulting dItem should
            // be based on the amount of the least numerous ingredient,
            // so track it
            int lowestAmount = 0;

            // Set lowestAmount to the amount of the first item found,
            // then set it to that of the ingredient with the lowest amount
            // found that isn't zero
            for (int n = 0; n < matrix.length - 1; n++) {
                if ((matrix[n].getAmount() > 0 &&
                     matrix[n].getAmount() < lowestAmount) || lowestAmount == 0) {
                        lowestAmount = matrix[n].getAmount();
                }
            }

            // Deduct that amount from every ingredient in the matrix,
            // but account for the fact that clicking the RESULT slot
            // will also deduct 1 from every ingredient by itself
            for (int n = 0; n < matrix.length - 1; n++) {
                if (matrix[n].getAmount() > 0) {
                    matrix[n].setAmount(matrix[n].getAmount() - lowestAmount + 1);
                }
            }

            // If the lowest amount is 1, there is no need to further
            // emulate a shift click at this point, and continuing the
            // emulation will actually cause bugs in the inventory's updating,
            // so only proceed if lowestAmount is higher than 1
            if (lowestAmount > 1) {

                // Get a clone of the recipe's result itemstack, so that
                // changing its amount now does not affect future craftings
                ItemStack resultStack = result.getItemStack().clone();

                // Set the itemstack's amount
                resultStack.setAmount(lowestAmount * resultStack.getAmount());

                // Set the crafting's result
                inventory.setResult(resultStack);

                // Update the player's inventory
                //
                // TODO: Replace with non-deprecated method once one
                // is added to Bukkit
                player.updateInventory();
            }
        }
    }

    @EventHandler
    public void boundInventoryClickEvent(InventoryClickEvent event) {
        // Proceed only if this is a CraftingInventory
        if (!(event.getInventory() instanceof CraftingInventory))
            return;

        // Proceed only if the click has a cursor item that is bound
        ItemStack item = event.getCursor();
        if (item == null || !isBound(item))
            return;

        // If they're trying to use the item as an ingredient, cancel it!
        if (event.getSlotType() == SlotType.CRAFTING) {
            removeBoundItems((CraftingInventory) event.getInventory(), (Player) event.getWhoClicked(), item);
        }
    }

    @EventHandler
    public void boundInventoryDragEvent(InventoryDragEvent event) {
        // Proceed only if this is a CraftingInventory
        if (!(event.getInventory() instanceof CraftingInventory))
            return;

        // Proceed only if the items are bound
        ItemStack item = event.getOldCursor();
        if (item == null || !isBound(item))
            return;

        // If they're trying to use the item as an ingredient, cancel it!
        // Manually check if the slot numbers are the same. If they are, then the player clicked
        // in the crafting matrix. If not, that means they clicked in their inventory and we should
        // ignore it.
        if (event.getInventorySlots().toArray()[0] == event.getRawSlots().toArray()[0]) {
            removeBoundItems((CraftingInventory) event.getInventory(), (Player) event.getWhoClicked(), item);
            // Manually set cursor to null to prevent empty-handed
            // duplication with event.getOldCursor()
            event.setCursor(null);
        }
    }

    public void removeBoundItems(final CraftingInventory inventory, final Player player, final ItemStack oldCursor) {

        Bukkit.getScheduler().scheduleSyncDelayedTask(DenizenAPI.getCurrentInstance(),
                new Runnable() {
                    @Override
                    public void run() {
                        // Store the crafting matrix
                        ItemStack[] matrix = inventory.getMatrix();

                        // Loop through items in the matrix
                        boolean removedItems = false;
                        for (int i = 0; i < matrix.length-1; i++) {
                            if (isBound(matrix[i])) {
                                matrix[i] = null;
                                removedItems = true;
                            }
                        }

                        // Add the edited matrix back to the inventory
                        inventory.setMatrix(matrix);
                        if (removedItems) {
                            player.getInventory().addItem(oldCursor);
                        }
                        player.updateInventory();

                    }
        }, 1);

    }

    @EventHandler
    public void boundDropItem(PlayerDropItemEvent event) {
        // If the item is bound, don't let them drop it!
        if (isBound(event.getItemDrop().getItemStack())) {
            event.setCancelled(true);
            event.getPlayer().updateInventory();
        }
    }
}
TOP

Related Classes of net.aufdemrand.denizen.scripts.containers.core.ItemScriptHelper

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.