Forum rules - please read before posting.

Craft without drag and drop - Maybe selecting multiple Items?

Hello everyone again! I'm enjoying a lot working with AC and have managed to perform super customized things just with the help of the forum :smile:

Sadly I can't find any clues on the following topic:

I'm looking for a way to Craft Recipes without having to drag&drop inventory items into the "Ingredients" Grid.

For a Simplified interface, I'd rather like to be able to select multiple Inventory Items at once (I mean, for instance, to be able to go clicking on several items, causing them all to get selected, like forming an Array of selected items), and then to be able to craft a new item if the selected elements match any Recipe. (For instance, I could create a Button with an ActionList that uses Inventory: Crafting -> Create Recipe).

I have an Inventory Category of "Ingredients" and I would like to apply this functionality to those.

Does anyone has any idea if it's possible to avoid using the Crafting Grid for this matter? I really wouldn't need to use Recipes/Crafting at all if there is at least a way to have several selected items, since I could create an ActionList that removes the used "ingredients" and adds the new "resulting" item into the inventory. BUT I can't figure out how to provoke the items to appear selected (multiple), like changing the icon for its "Active Graphic".

THANKS SO MUCH!! =)

Comments

  • You will need to rely on a custom script to handle custom inventory behaviour, but it'll be considerably easier if you can allow for ingredient items to be placed in a crafting menu - even if they are moved there with single clicks to the inventory, as opposed to drag/dropping.

    The alternative - having multiple items be "selected" in the inventory and then perform crafting - would have to involve overriding AC's crafting calculations (that read the contents of the "crafting" menu element). It should be possible, but I'd advise seeing if the first method is enough beforehand.

    For this (clicking items to automatically move them to the ingredients box), find the Crafting menu's Inventory element and set its Inventory box type property to Custom Script. The following script, present in the scene, will then give you that behaviour. Rename the elements mentioned in the code if necessary:

    using UnityEngine;
    using AC;
    
    public class ClickIngredients : MonoBehaviour
    {
    
        private void OnEnable () { EventManager.OnMenuElementClick += OnMenuElementClick; }
        private void OnDisable () { EventManager.OnMenuElementClick -= OnMenuElementClick; }
    
        private void OnMenuElementClick (AC.Menu menu, MenuElement element, int slot, int buttonPressed)
        {
            if (menu.title == "Crafting" && element.title == "Inventory")
            {
                MenuInventoryBox inventoryBox = element as MenuInventoryBox;
                InvInstance clickedInstance = inventoryBox.GetInstance (slot);
                if (InvInstance.IsValid (clickedInstance))
                {
                    KickStarter.runtimeInventory.CraftingInvCollection.AddToEnd (clickedInstance);
                }
            }
        }
    
    }
    
  • Chris, thanks so much for the info! Will definitely try your code =)

    Two more questions regarding this:

    1) Since my Ingredients Menu will be available across scenes, should I make a prefab with your code and import it into every scene or just add it to each scene by hand or is there a way to automatically include it everywhere?

    2) Regarding the Crafting "Output" element: I understand I must click on the appearing crafted item in order for it to be included into the player's inventory. Now, is there a way I can just add a button that "crafts" the recipe (having put all the correct ingredients into the crafting slots) and automatically adds the new item into the inventory without needing to click on output box to "grab it"?

    I made an image for better understanding:

    https://drive.google.com/file/d/1Or3Txaci62LbyOUPOmPr3EVJlNsM5BAI/view?usp=sharing

  • Tried your code and works amazingly, can I add something to limit the amount of items than can be used? like 4 max? Your support is out of this world!

  • edited January 2022

    should I make a prefab with your code and import it into every scene or just add it to each scene by hand or is there a way to automatically include it everywhere?

    If you attach it to an object that survives scene changes, it'll appear in every scene automatically.

    Examples of such an object are the Player prefab (if spawning in via the Settings Manager), or the PersistentEngine prefab inside /Assets/AdventureCreator/Resources.

    Just be aware that the latter will be overwritten upon updating AC, so you'll either have to re-add the script to it, or omit it from being imported, when updating AC.

    is there a way I can just add a button that "crafts" the recipe

    Yes - the PerformCrafting function can be called from a custom script to give the same behaviour as clicking an "Output" crafting element.

    can I add something to limit the amount of items than can be used?

    Yes - the amount of ingredients currently active can be read with GetCount.

    This updated script should handle both of the above. It makes reference to two more elements - CreateButton and Crafting - so again, you'll need to check that these names match up with the button and crafting output slot in your menu.

    using UnityEngine;
    using AC;
    
    public class ClickIngredients : MonoBehaviour
    {
    
        public int ingredientLimit = 4;
    
        private void OnEnable () { EventManager.OnMenuElementClick += OnMenuElementClick; }
        private void OnDisable () { EventManager.OnMenuElementClick -= OnMenuElementClick; }
    
        private void OnMenuElementClick (AC.Menu menu, MenuElement element, int slot, int buttonPressed)
        {
            if (menu.title == "Crafting")
            {
                if (element.title == "Inventory")
                {
                    int numIngredients = KickStarter.runtimeInventory.CraftingInvCollection.GetCount (false);
                    if (numIngredients >= ingredientLimit)
                    {
                        return;
                    }
    
                    MenuInventoryBox inventoryBox = element as MenuInventoryBox;
                    InvInstance clickedInstance = inventoryBox.GetInstance (slot);
                    if (InvInstance.IsValid (clickedInstance))
                    {
                        KickStarter.runtimeInventory.CraftingInvCollection.AddToEnd (clickedInstance);
                    }
                }
                else if (element.title == "CreateButton")
                {
                    MenuCrafting crafting = menu.GetElementWithName ("Crafting") as MenuCrafting;
                    if (crafting.ActiveRecipe != null)
                    {
                        KickStarter.runtimeInventory.PerformCrafting (crafting.ActiveRecipe, false);
                    }
                }
            }
        }
    
    }
    
  • Hi again, Chris, everything worked perfectly, thanks so much for everything, you've been a life savior! Congrats for your outstanding support level!

  • I want to add a tiny update to the given code just in case others find it helpful (Chris, please correct my code if it's not accurate):

    Using the previous code, if the Crafting Output was empty (no Recipe matching the selected items' combination), the Console shows an error (not being able to find such object). So I added a verification like this:

    if(crafting.ActiveRecipe != null) {
      KickStarter.runtimeInventory.PerformCrafting (crafting.ActiveRecipe, false);
    }
    

    ^_^ Thanks again, Chris!!!! You're making game dev feel sweet!

  • edited January 2022

    Quite right - my mistake.

    I've updated the script above with the correction for the benefit of anyone who stumbles upon this.

  • Hi, @ChrisIceBox hope you are doing great! Thanks in advance for your help as usual :smile:

    I recently upgraded AC to Version 1.75.7 and saw in the Upgrade notice:

    The RuntimeInventory script's CraftingInvCollection has been replaced with the CraftingInvCollection[] array
    The RuntimeInventory script's CalculateRecipe and PerformCrafting functions now require an InvCollection to source ingredients from

    So, in my script, I'm getting the following errors in the console now:

    • Assets/opheliaschapter/CustomScripts/ClickIngredients.cs(28,71): error CS1061: 'RuntimeInventory' does not contain a definition for 'CraftingInvCollection' and no accessible extension method 'CraftingInvCollection' accepting a first argument of type 'RuntimeInventory' could be found (are you missing a using directive or an assembly reference?)

    • Assets/opheliaschapter/CustomScripts/ClickIngredients.cs(39,54): error CS1061: 'RuntimeInventory' does not contain a definition for 'CraftingInvCollection' and no accessible extension method 'CraftingInvCollection' accepting a first argument of type 'RuntimeInventory' could be found (are you missing a using directive or an assembly reference?)

    • Assets/opheliaschapter/CustomScripts/ClickIngredients.cs(48,67): error CS1503: Argument 1: cannot convert from 'AC.Recipe' to 'AC.InvCollection'

    • Assets/opheliaschapter/CustomScripts/ClickIngredients.cs(48,90): error CS1503: Argument 2: cannot convert from 'bool' to 'AC.Recipe'

    This is the custom script we previously created, with some minor tweaks:

    using UnityEngine;
    using AC;
    
    public class ClickIngredients : MonoBehaviour
    {
    
        public ActionListAsset actionListDidCraft;
        public ActionListAsset actionListFailedCraft;
        public ActionListAsset actionListInvDeselect;
        public ActionListAsset actionListIngLimit;
    
    
        public int ingredientLimit = 4;
    
        private void OnEnable () { EventManager.OnMenuElementClick += OnMenuElementClick; }
        private void OnDisable () { EventManager.OnMenuElementClick -= OnMenuElementClick; }
    
        private void OnMenuElementClick (AC.Menu menu, MenuElement element, int slot, int buttonPressed)
        {
            if (menu.title == "Ingredients")
            {
                if (element.title == "IngredientsBox")
                {
    
                    // Left click only, cause we want to retain the "Examine" default functionality @Juanma
                    if (buttonPressed == 1)
                    {
                        int numIngredients = KickStarter.runtimeInventory.CraftingInvCollection.GetCount (false);
                        if (numIngredients >= ingredientLimit)
                        {
                            actionListIngLimit.Interact();
                            return;
                        }
    
                        MenuInventoryBox inventoryBox = element as MenuInventoryBox;
                        InvInstance clickedInstance = inventoryBox.GetInstance (slot);
                        if (InvInstance.IsValid (clickedInstance))
                        {
                            KickStarter.runtimeInventory.CraftingInvCollection.AddToEnd (clickedInstance);
                            actionListInvDeselect.Interact();
                        }
                    }
                }
                else if (element.title == "CraftButton")
                {
                    MenuCrafting crafting = menu.GetElementWithName ("CraftingOutput") as MenuCrafting;
                    if(crafting.ActiveRecipe != null) {
                        KickStarter.runtimeInventory.PerformCrafting (crafting.ActiveRecipe, false);
                        actionListDidCraft.Interact();
                    } else {
                        actionListFailedCraft.Interact();
                    }
                }
            } else {
                return;
            }
        }
    
    }
    

    So I if you could help me update it it would be amazing! And I commented on this very thread so if anyone else wants to use something similar they can have it "updated" to work with AC v. 1.75.7+ as well.

    THANKS SO MUCH!! :)

  • No problem. Essentially the changes boil down to each Crafting element being in charge of its own set of items - so you just need to specify which InvCollection you want to make use of.

    There's a couple of ways to do this. If you want to reference a specific Crafting element of type Ingredients (here named "CraftingItems"):

    using UnityEngine;
    using AC;
    
    public class ClickIngredients : MonoBehaviour
    {
    
        public ActionListAsset actionListDidCraft;
        public ActionListAsset actionListFailedCraft;
        public ActionListAsset actionListInvDeselect;
        public ActionListAsset actionListIngLimit;
        public int ingredientLimit = 4;
    
    
        private void OnEnable () { EventManager.OnMenuElementClick += OnMenuElementClick; }
        private void OnDisable () { EventManager.OnMenuElementClick -= OnMenuElementClick; }
    
    
        private void OnMenuElementClick (AC.Menu menu, MenuElement element, int slot, int buttonPressed)
        {
            if (menu.title == "Ingredients")
            {
                InvCollection craftingInvCollection = (menu.GetElementWithName ("CraftingItems") as MenuCrafting).IngredientsInvCollection;
    
                if (element.title == "IngredientsBox")
                {
                    // Left click only, cause we want to retain the "Examine" default functionality @Juanma
                    if (buttonPressed == 1)
                    {
                        int numIngredients = craftingInvCollection.GetCount (false);
                        if (numIngredients >= ingredientLimit)
                        {
                            actionListIngLimit.Interact();
                            return;
                        }
    
                        MenuInventoryBox inventoryBox = element as MenuInventoryBox;
                        InvInstance clickedInstance = inventoryBox.GetInstance (slot);
                        if (InvInstance.IsValid (clickedInstance))
                        {
                            craftingInvCollection.AddToEnd (clickedInstance);
                            actionListInvDeselect.Interact();
                        }
                    }
                }
                else if (element.title == "CraftButton")
                {
                    MenuCrafting crafting = menu.GetElementWithName ("CraftingOutput") as MenuCrafting;
                    if (crafting.ActiveRecipe != null)
                    {
                        KickStarter.runtimeInventory.PerformCrafting (craftingInvCollection, crafting.ActiveRecipe, false);
                        actionListDidCraft.Interact();
                    }
                    else
                    {
                        actionListFailedCraft.Interact();
                    }
                }
            }
            else
            {
                return;
            }
        }
    }
    

    Or, if you only have one Crafting box in the game, you should be able to just use the first element in the CraftingInvCollections list. For that, replace:

    InvCollection craftingInvCollection = (menu.GetElementWithName ("CraftingItems") as MenuCrafting).IngredientsInvCollection;
    

    with:

    InvCollection craftingInvCollection = KickStarter.runtimeInventory.CraftingInvCollections[0];
    
  • Thanks so much for your fast response, Chris!! I already tested and it works flawlessly. You saved the day once again! Cheers!

  • Hi again, @ChrisIceBox hope you're doing well :smile:

    I updated AC from 1.76.1 to 1.78.2 (I sadly missed the previous versions since I was off for a little time).

    Regarding our script for custom crafting functionality, with this update when I hover over any empty crafting slot with the cursor, I get an infinite loop of the following console error (it stops if I move the cursor away from the empty crafting slot):

    NullReferenceException: Object reference not set to an instance of an object
    AC.MenuCrafting.GetHotspotLabelOverride (System.Int32 _slot, System.Int32 _language) (at Assets/AdventureCreator/Scripts/Menu/Menu classes/MenuCrafting.cs:403)
    AC.PlayerMenus.UpdateElements (AC.Menu menu, System.Int32 languageNumber, System.Boolean justDisplay) (at Assets/AdventureCreator/Scripts/Controls/PlayerMenus.cs:1636)
    AC.PlayerMenus.UpdateMenu (AC.Menu menu, System.Int32 languageNumber, System.Boolean justPosition, System.Boolean updateElements) (at Assets/AdventureCreator/Scripts/Controls/PlayerMenus.cs:1458)
    AC.PlayerMenus.UpdateAllMenus () (at Assets/AdventureCreator/Scripts/Controls/PlayerMenus.cs:2130)
    AC.StateHandler.Update () (at Assets/AdventureCreator/Scripts/Game engine/StateHandler.cs:245)
    

    I tested on another git branch of the project with AC 1.76.1 and I can confirm the error is not happening.

    Thanks so much for any help you can provide for troubleshooting this! :smile:

  • Oh dear, a silly mistake on my part. Thanks for bringing it to my attention, I'll see this addressed in the next release.

    To fix, open up AC's MenuCrafting script and replace line 399:

    if (!InvInstance.IsValid (invInstance))
    

    with:

    if (InvInstance.IsValid (invInstance))
    
  • Thanks so much!!

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.