1.16.x Custom Crafting GUI

Discussion in 'Spigot Plugin Development' started by Shiny1Key, May 4, 2021 at 12:35 AM.

  1. Alright I need some help with this. I have scoured the forums and can not seem to find an a post that completes the answers.

    Much like on hypixel, I am making a custom GUI. It is in a chest inventory, with the slots. I am going through the recipes using my iterator, and found the information I want. But....I just can not for the life of me figure out where to go next. Here is the code I have:

    Code (Text):
    package net.kreavian.krymmo.utils;

    import net.kreavian.krymmo.KRYMMO;
    import org.bukkit.inventory.*;

    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;

    public class CustomCrafting {
        private static HashMap<Character, Integer> CraftingMatrix = new HashMap<>();

        public static void init() {
            CraftingMatrix.put('a', 10); CraftingMatrix.put('b', 11); CraftingMatrix.put('c', 12);
            CraftingMatrix.put('d', 19); CraftingMatrix.put('e', 20); CraftingMatrix.put('f', 21);
            CraftingMatrix.put('g', 28); CraftingMatrix.put('h', 29); CraftingMatrix.put('i', 30);
        }

        public static void checkRecipe(Inventory inventory) {
            Iterator<Recipe> recipeIterator = KRYMMO.getInstance().getServer().recipeIterator();

            while(recipeIterator.hasNext()) {
                Recipe recipe = recipeIterator.next();
                if(recipe instanceof ShapedRecipe) {
                    int rowindex = 0;

                    Map<Character, ItemStack> map = ((ShapedRecipe) recipe).getIngredientMap();
                    for(String row : ((ShapedRecipe) recipe).getShape()) {
                        int columnIndex = 0;
                        for(char ch : row.toCharArray()) {
                            ItemStack ingredient = map.get(ch);
                            ItemStack item = inventory.getItem(CraftingMatrix.get(ch));

                            System.out.println(ingredient + " <---> " + item);
                        }
                    }
                    //inventory.setItem(23, recipe.getResult());
                    System.out.println("------------------------------------------------");
                }
                if(recipe instanceof ShapelessRecipe) {

                }
            }
        }
    }
     
    this is working, Its spitting out all the server recipes and comparing them to whats in my inventory. among the many lines of output I find ONE and only ONE instance where it matches my recipe... here is the output:

    Code (Text):
    [19:26:55 INFO]: ------------------------------------------------
    [19:26:55 INFO]: ItemStack{DIAMOND x 1} <---> ItemStack{DIAMOND x 1}
    [19:26:55 INFO]: ItemStack{DIAMOND x 1} <---> ItemStack{DIAMOND x 1}
    [19:26:55 INFO]: ItemStack{DIAMOND x 1} <---> ItemStack{DIAMOND x 1}
    [19:26:55 INFO]: null <---> null
    [19:26:55 INFO]: ItemStack{STICK x 1} <---> ItemStack{STICK x 1}
    [19:26:55 INFO]: null <---> null
    [19:26:55 INFO]: null <---> null
    [19:26:55 INFO]: ItemStack{STICK x 1} <---> ItemStack{STICK x 1}
    [19:26:55 INFO]: null <---> null
    [19:26:55 INFO]: ------------------------------------------------
    So here is my question....Now what? I have tried breaking out of the loop when I find a match. Breaking out of my while statement..Nothing is working. My comparisons just do not seem to be firing properly despite, according to my console. I found the recipe.

    Could anyone please help point me in the direction of finalizing this and getting the recipe.getReult()

    Thank you in advanced!
     
  2. First off you probably don't want a static method.
    You should change the return type of the method to Itemstack and just return the output when it is found, that will break the loop.
     
  3. That def would help. Im sorry, where do I place the return statement though? I need to return after I check all rows match.
     
  4. Thats what im struggling on right now, I have the rows checking but I need to figure out if all rows match.

    I have tried a boolean and setting it to true/false when a row matches. Problem is if the last instance is true it will return true despite 1 or more rows not matching

    Also tried doing a counter where to check if there are any rows that did not match and this did not go well either.
     
  5. Well, this code is pretty dirty, but actually works.
    It gives out a list of available recipes to craft in workbench with your current items.

    Code (Java):

    // Inventory inventory = player.getInventory();

    Map<Material, Integer> inventoryCountMap = Maps.newHashMap();
    inventory.forEach(item -> {
        if(item == null || item.getType() == Material.AIR) return;
        inventoryCountMap.put(item.getType(), inventoryCountMap.getOrDefault(item.getType(), 0) + item.getAmount());
    });

    List<Recipe> recipes = Lists.newArrayList(Bukkit.recipeIterator());
    List<Recipe> availableToCraft = Lists.newArrayList();

    for(Recipe recipe : recipes) {
        Map<Material, Integer> recipeCountMap = Maps.newHashMap();

        // fill
        Collection<ItemStack> ingredients = null;
        if(recipe instanceof ShapedRecipe)
            ingredients = ((ShapedRecipe) recipe).getIngredientMap().values();
        else if(recipe instanceof ShapelessRecipe)
            ingredients = ((ShapelessRecipe) recipe).getIngredientList();

        if(ingredients == null) continue; // not recipe for workbench
        for(ItemStack ingredient : ingredients) {
            if(ingredient == null || ingredient.getType() == Material.AIR) continue;
            recipeCountMap.put(ingredient.getType(), recipeCountMap.getOrDefault(ingredient.getType(), 0) + ingredient.getAmount());
        }

        // match
        int matched = 0;
        for(Map.Entry<Material, Integer> requiredCount : recipeCountMap.entrySet()) {
            Material material = requiredCount.getKey();
            int amount = requiredCount.getValue();

            if(inventoryCountMap.getOrDefault(material, 0) < amount) break;
            matched++;
        }

        if(matched < recipeCountMap.size()) continue;
        availableToCraft.add(recipe);
    }

    // do anything with availableToCraft list
     
     
  6. Going to try this out thank you!, Am I mising something. Where were you finding the items in the slots? I have a custom crafting GUI filled with the items but also stuff like glass for the border.
     
  7. I used a map, that stores how much items of specific type do you have in inventory and you need to craft, then comparing both maps and... profit :D
     
  8. Come to think of it, Since i know my slots guess I can just modify that first inventory event to get it from the designated slots.
     
  9. OOOOOh I get it. So I can just add an additional statement to skip the item if it finds my glass or barrier.
     
  10. Sorry, i just realized what you want to do. No, my previous code won't work with your case, because it looks at the LIST of available crafts from the items in the inventory, not as a matrix comparasion.

    Well, there's an idea:

    store a map<integer, material>(index, item) for items inside 3x3 pane in gui
    fire next logic every time this pane updates:

    - go through all available recipes
    * if recipe is shaped: convert it to map<integer, material>(index, item) and compare with gui's map
    * if recipe is shapeless: use countmap technique from my code
     
    #10 nesclass_yt, May 4, 2021 at 3:16 AM
    Last edited: May 4, 2021 at 3:23 AM
  11. Im sorry not sure im following, So for each slot in my matrix add it to the map?

    And then in my iterator code add the ingredients to another map and compare the two maps?
     
  12. if i got you right - yes, that's actually what do you need todo
     
  13. upload_2021-5-3_22-35-15.png

    For added context this is the inventory i have, im taking the items from the slot.
     
  14. You have to store all items in this 3x3 in "index" map (0-diamond and so on), and lately compare it with recipe's "index" map
     
  15. Got it. Ok is there a different method im suppose to be calling?

    Map<Character, ItemStack> map = ((ShapedRecipe) recipe).getIngredientMap(); returns a Character, ItemStack?
     
  16. Ok. Change it around....I clearly went wrong somewhere as this isnt working XD

    Code (Text):
    package net.kreavian.krymmo.utils;

    import net.kreavian.krymmo.KRYMMO;
    import org.bukkit.Material;
    import org.bukkit.inventory.*;

    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;

    public class CustomCrafting {
        private static HashMap<Character, Integer> CraftingMatrix = new HashMap<>();

        public static void init() {
            CraftingMatrix.put('a', 10); CraftingMatrix.put('b', 11); CraftingMatrix.put('c', 12);
            CraftingMatrix.put('d', 19); CraftingMatrix.put('e', 20); CraftingMatrix.put('f', 21);
            CraftingMatrix.put('g', 28); CraftingMatrix.put('h', 29); CraftingMatrix.put('i', 30);
        }

        public static void checkRecipe(Inventory inventory) {
            Iterator<Recipe> recipeIterator = KRYMMO.getInstance().getServer().recipeIterator();

            Collection<ItemStack> inventoryMap = null;
            inventoryMap.add(inventory.getItem(CraftingMatrix.get('a')));
            inventoryMap.add(inventory.getItem(CraftingMatrix.get('b')));
            inventoryMap.add(inventory.getItem(CraftingMatrix.get('c')));

            inventoryMap.add(inventory.getItem(CraftingMatrix.get('d')));
            inventoryMap.add(inventory.getItem(CraftingMatrix.get('e')));
            inventoryMap.add(inventory.getItem(CraftingMatrix.get('f')));

            inventoryMap.add(inventory.getItem(CraftingMatrix.get('g')));
            inventoryMap.add(inventory.getItem(CraftingMatrix.get('h')));
            inventoryMap.add(inventory.getItem(CraftingMatrix.get('i')));

            while(recipeIterator.hasNext()) {
                Recipe recipe = recipeIterator.next();
                if(recipe instanceof ShapedRecipe) {
                    Collection<ItemStack> map = ((ShapedRecipe) recipe).getIngredientMap().values();
                    if(inventoryMap.containsAll(map)) {
                        System.out.println("Found a matching recipe!");
                    }
                    /*
                    for(String row : ((ShapedRecipe) recipe).getShape()) {
                        for(char ch : row.toCharArray()) {
                            ItemStack ingredient = map.get(ch);
                            ItemStack item = inventory.getItem(CraftingMatrix.get(ch));

                            System.out.println(ingredient + " <---> " + item);
                        }
                    }

                     */
                    //inventory.setItem(23, recipe.getResult());
                    System.out.println("------------------------------------------------");
                }
                if(recipe instanceof ShapelessRecipe) {

                }
            }
        }
    }
     
     
  17. Alright I dont get this. Why doesnt this work??

    Code (Text):
    package net.kreavian.krymmo.utils;

    import net.kreavian.krymmo.KRYMMO;
    import org.bukkit.inventory.*;

    import java.util.*;

    public class CustomCrafting {
        private static HashMap<Character, Integer> CraftingMatrix = new HashMap<>();

        public static void init() {
            CraftingMatrix.put('a', 10); CraftingMatrix.put('b', 11); CraftingMatrix.put('c', 12);
            CraftingMatrix.put('d', 19); CraftingMatrix.put('e', 20); CraftingMatrix.put('f', 21);
            CraftingMatrix.put('g', 28); CraftingMatrix.put('h', 29); CraftingMatrix.put('i', 30);
        }

        public static void checkRecipe(Inventory inventory) {
            Iterator<Recipe> recipeIterator = KRYMMO.getInstance().getServer().recipeIterator();

            while(recipeIterator.hasNext()) {
                Recipe recipe = recipeIterator.next();
                ArrayList<Boolean> matchingRecipe = new ArrayList<>();
                if(recipe instanceof ShapedRecipe) {
                    Map<Character, ItemStack> map = ((ShapedRecipe) recipe).getIngredientMap();
                    for(String row : ((ShapedRecipe) recipe).getShape()) {
                        for(char ch : row.toCharArray()) {
                            ItemStack ingredient = map.get(ch);
                            ItemStack item = inventory.getItem(CraftingMatrix.get(ch));

                            System.out.println(ingredient + " <---> " + item);
                            matchingRecipe.add(item == ingredient);
                        }
                    }
                    //inventory.setItem(23, recipe.getResult());
                    System.out.println(areAllTrue(matchingRecipe));
                    System.out.println("------------------------------------------------");
                }
                if(recipe instanceof ShapelessRecipe) {

                }
            }
        }

        public static boolean areAllTrue(ArrayList<Boolean> array) {
            for(boolean b : array) if(!b) return false;
            return true;
        }
    }
     
    Output:

    Code (Text):
    [00:01:36 INFO]: ------------------------------------------------
    [00:01:36 INFO]: ItemStack{DIAMOND x 1} <---> ItemStack{DIAMOND x 1}
    [00:01:36 INFO]: ItemStack{DIAMOND x 1} <---> ItemStack{DIAMOND x 1}
    [00:01:36 INFO]: ItemStack{DIAMOND x 1} <---> ItemStack{DIAMOND x 1}
    [00:01:36 INFO]: null <---> null
    [00:01:36 INFO]: ItemStack{STICK x 1} <---> ItemStack{STICK x 1}
    [00:01:36 INFO]: null <---> null
    [00:01:36 INFO]: null <---> null
    [00:01:36 INFO]: ItemStack{STICK x 1} <---> ItemStack{STICK x 1}
    [00:01:36 INFO]: null <---> null
    [00:01:36 INFO]: false
    [00:01:36 INFO]: ------------------------------------------------
     
  18. Further Inspection shows that my equal is not working:

    Code (Text):
    [00:04:19 INFO]: ------------------------------------------------
    [00:04:19 INFO]: ItemStack{DIAMOND x 1} <---> ItemStack{DIAMOND x 1}
    [00:04:19 INFO]: ItemStack{DIAMOND x 1} <---> ItemStack{DIAMOND x 1}
    [00:04:19 INFO]: ItemStack{DIAMOND x 1} <---> ItemStack{DIAMOND x 1}
    [00:04:19 INFO]: null <---> null
    [00:04:19 INFO]: ItemStack{STICK x 1} <---> ItemStack{STICK x 1}
    [00:04:19 INFO]: null <---> null
    [00:04:19 INFO]: null <---> null
    [00:04:19 INFO]: ItemStack{STICK x 1} <---> ItemStack{STICK x 1}
    [00:04:19 INFO]: null <---> null
    [00:04:19 INFO]: false
    [00:04:19 INFO]: [false, false, false, true, false, true, true, false, true]
    [00:04:19 INFO]: ------------------------------------------------
     
  19. Alright im making progress....

    Code (Text):
    package net.kreavian.krymmo.utils;

    import net.kreavian.krymmo.KRYMMO;
    import org.bukkit.inventory.*;

    import java.util.*;

    public class CustomCrafting {
        private static HashMap<Character, Integer> CraftingMatrix = new HashMap<>();

        public static void init() {
            CraftingMatrix.put('a', 10); CraftingMatrix.put('b', 11); CraftingMatrix.put('c', 12);
            CraftingMatrix.put('d', 19); CraftingMatrix.put('e', 20); CraftingMatrix.put('f', 21);
            CraftingMatrix.put('g', 28); CraftingMatrix.put('h', 29); CraftingMatrix.put('i', 30);
        }

        public static void checkRecipe(Inventory inventory) {
            Iterator<Recipe> recipeIterator = KRYMMO.getInstance().getServer().recipeIterator();

            while(recipeIterator.hasNext()) {
                Recipe recipe = recipeIterator.next();
                ArrayList<Boolean> matchingRecipe = new ArrayList<>();
                if(recipe instanceof ShapedRecipe) {
                    Map<Character, ItemStack> map = ((ShapedRecipe) recipe).getIngredientMap();
                    for(String row : ((ShapedRecipe) recipe).getShape()) {
                        for(char ch : row.toCharArray()) {
                            ItemStack ingredient = map.get(ch);
                            ItemStack item = inventory.getItem(CraftingMatrix.get(ch));
                            if(item != null && ingredient != null) {
                                matchingRecipe.add(item.equals(ingredient));
                            }
                        }
                    }
                    if(areAllTrue(matchingRecipe)) {
                        System.out.println("Found the recipe!");
                        inventory.setItem(23, recipe.getResult());
                        return;
                    }
                }
                if(recipe instanceof ShapelessRecipe) {

                }
            }
        }

        public static boolean areAllTrue(ArrayList<Boolean> array) {
            for(boolean b : array) if(!b) return false;
            return true;
        }
    }
     
    This code works great minus 1 issue....It returns a diamond hoe when I add my return statement in there grrrr.....But its getting there.
     
  20. HAHAHAHAA! I HAVE DONE IT!

    Code (Text):
    package net.kreavian.krymmo.utils;

    import net.kreavian.krymmo.KRYMMO;
    import org.bukkit.inventory.*;

    import java.util.*;

    public class CustomCrafting {
        private static HashMap<Character, Integer> CraftingMatrix = new HashMap<>();

        public static void init() {
            CraftingMatrix.put('a', 10); CraftingMatrix.put('b', 11); CraftingMatrix.put('c', 12);
            CraftingMatrix.put('d', 19); CraftingMatrix.put('e', 20); CraftingMatrix.put('f', 21);
            CraftingMatrix.put('g', 28); CraftingMatrix.put('h', 29); CraftingMatrix.put('i', 30);
        }

        public static boolean craftItem(Inventory inventory) {
            Iterator<Recipe> recipeIterator = KRYMMO.getInstance().getServer().recipeIterator();
            int totalInventoryItems = 0;
            for(Map.Entry<Character,Integer> entry : CraftingMatrix.entrySet()) {
                if(inventory.getItem(entry.getValue()) != null) {
                    totalInventoryItems++;
                }
            }

            while(recipeIterator.hasNext()) {
                Recipe recipe = recipeIterator.next();
                ArrayList<Boolean> matchingRecipe = new ArrayList<>();
                int totalItems = 0;
                if(recipe instanceof ShapedRecipe) {
                    Map<Character, ItemStack> map = ((ShapedRecipe) recipe).getIngredientMap();
                    for(String row : ((ShapedRecipe) recipe).getShape()) {
                        for(char ch : row.toCharArray()) {
                            ItemStack ingredient = map.get(ch);
                            ItemStack item = inventory.getItem(CraftingMatrix.get(ch));
                            if(item != null && ingredient != null) {
                                matchingRecipe.add(item.equals(ingredient));
                                totalItems++;
                            }
                        }
                    }
                    if(areAllTrue(matchingRecipe) && totalInventoryItems == totalItems) {
                        inventory.setItem(23, recipe.getResult());
                        return true;
                    }
                }
                if(recipe instanceof ShapelessRecipe) {

                }
            }
            return false;
        }

        public static boolean areAllTrue(ArrayList<Boolean> array) {
            for(boolean b : array) if(!b) return false;
            return true;
        }
    }
     
    Now, I shall move to shapeless recipes next! Will update this thread when the shapeless code works.
     
    • Winner Winner x 1