Custom Crafter

In version 1.0 I had set up a custom craftinginteractable that referenced a list of crafting recipes that the player had learned and used that to populate the recipe list of the crafting menu. I tried this with version 1.1 by creating a custom Crafter.cs script, but for whatever reason when I try to drag it into Crafting Menu Opener in the inspector, it won't take it. This leads me to believe that there is now a different way of doing this. I looked at the documentation (https://opsive.com/support/documentation/ultimate-inventory-system/crafting/) and it mentions at the bottom that you can edit the list of recipes the crafter accepts (List<CraftingRecipes> recipes = m_Crafter.CraftinRecipes;), but I wasn't able to wrap my head around where I should actually be updating it.

Previously, I did the following in the CraftingInteractableBehavior script:
Code:
    protected void SetCraftingRecipes()
        {
            var recipeList = new List<CraftingRecipe>(m_MiscellanousRecipes);
            var characterRecipeList = player.GetComponent<CharacterCraftingData>().myRecipes;

            for (int i = 0; i < m_RecipeCategories.Length; i++)
            {
                var pooledArray = GenericObjectPool.Get<CraftingRecipe[]>();
                var recipesCount = m_RecipeCategories[i].GetAllChildrenElements(ref pooledArray);
                var characterRecipesCount = characterRecipeList.Count;
                for (int j = 0; j < recipesCount; j++)
                {
                    for (int k = 0; k < characterRecipesCount; k++)
                    {
                        if (pooledArray[j] == characterRecipeList[k])
                        {
                            recipeList.Add(pooledArray[j]);
                        }
                    }
                }
                GenericObjectPool.Return(pooledArray);
            }

            m_Recipes = recipeList.ToArray();
        }

This leaves me with two questions:
  1. Unfortunately, any edits I do to a custom copy of the Crafter script result in the Crafting Menu Opener not accepting the new Crafter component in the inspector. Any suggestions on what could be causing this? Strangely, my updated Crafter component script looks quite different in the inspector (even if I just change 1 line). Does this have to do with the script existing within my Assets/Scripts folder and the rest of the UIS scripts existing in the Assets/Opsive/... folder? Included is an image with both versions of the Crafter script added as components.
  2. Is there an easier, better way to have a crafter only accept a specific list of recipes that I give it in v1.1?
Screen Shot 2020-11-27 at 10.13.37 PM.png
 
So for your use case you do not even need to create a custom Crafter. But if you did you should inherit the Crafter class, instead of duplicating it.

Example
Code:
public class CustomCrafter : Crafter
{
    [SerializeField] protected string m_CustomField;
}
1606726431154.png
As you can see it keeps the custom inspector.

Custom inspector reference Monobehaviors by the type, so you would need to duplicate the inspector as well if you wanted to make a crafter from scratch.

This is also the reason why you cannot set your crafter in the UI.

Now for setting you own recipes, maybe you could do it each time you open the crafting panel? There are two ways you can listen to the event of a panel opening.
Code:
// Use the Display Panel C# event
craftingMenuDisplayPanel.OnOpen += HandlePanelOpened

// Use the Event Handler, this gets called for all panels, so you can check the panel name to see if the crafting menu panel was opened
EventHandler.RegisterEvent<OpenClosePanelInfo>(m_PanelOwner, EventNames.c_GameObject_OnPanelOpenClose_OpenClosePanelInfo, PanelOpenedOrClosed);

In the first option you need a reference the the crafting menu display panel.

In the second option you need a reference to the panel owner (the player game object) and the name of the panel being opened. You can set that name in the crafting menu display panel inspector
1606727292652.png

Another completely different solution is to create a custom Crafting Menu (instead of Crafter).
Then you can change the OnOpen method to get the crafting recipes from where you want. This will solve issues from the OnOpen event being called first on the menu before it is calling your own event

(That's actually going to be a problem so I would highly advise you create a custom Crafting Menu, I'll think of a better solution at some point).
 
What would you recommend as the best way to get started creating a custom Crafting Menu? I tried inheriting from CraftingMenu, but that provided me with a whole mess of things that didn't want to work :). I really only need to change a small bit of code to get it working with my own list, but I'd really like to avoid editing any of the base scripts. Addtionally, I would like to be able to utilize the Crafter feature where you can select a specific category to craft (for different crafting stations in the game).

(Quick side question; is there a way to get the Opsive scripts to recognize my own classes? I was digging into this a bit and doing some research on assembiles, but couldn't really find enough detailed information on getting that working).

Thanks, and sorry for the newbie questions - just wrapping my head around the new code.
 
I will think of a solution during the week. It'll probably require some changes in UIS.

As for using your code in UIS that's not something you should try to do. It is possible if you add your assembly definition in ours, but that change will be consistantly reset each time you update the asset.
You should use UIS within your code not the other way around. If for some reason you can't achieve what you want without that, then that means UIS needs to be updated to allow it.

My initial idea will be to change the Crafting Menu such that it requests the recipes from the Crafter through a method instead of using the public getter of the list of recipes. But I need to think it through a bit more and test to make sure that is the best solution.
 
I've made the change, but it may take a few days for us to upload the fix.

You can try it out and let me know if it fixes the issue first.

In the CraftingMenu.cs script replace the Draw Recipes method by this:

Code:
protected void DrawRecipes()
{
    m_CraftingRecipeGrid.SetElements(m_Crafter.GetRecipes());
    m_CraftingRecipeGrid.Draw();
}

In the Crafter.cs script add those methods:

Code:
/// <summary>
/// Get the recipes.
/// </summary>
/// <returns>A list of the recipes.</returns>
public virtual ListSlice<CraftingRecipe> GetRecipes()
{
    return m_CraftingRecipes;
}
/// <summary>
/// Add a recipe to the list of recipes.
/// </summary>
/// <param name="recipe">The recipe to add.</param>
public virtual void AddRecipe(CraftingRecipe recipe)
{
    if(m_CraftingRecipes.Contains(recipe)){ return; }
    m_CraftingRecipes.Add(recipe);
}
/// <summary>
/// Remove a recipe.
/// </summary>
/// <param name="recipe">The recipe to remove.</param>
public virtual void RemoveRecipe(CraftingRecipe recipe)
{
    if(m_CraftingRecipes.Contains(recipe)){ return; }
    m_CraftingRecipes.Remove(recipe);
}
/// <summary>
/// Set all the recipes.
/// </summary>
/// <param name="recipes">The recipes to set.</param>
public virtual void SetRecipes(ListSlice<CraftingRecipe> recipes)
{
    m_CraftingRecipes.Clear();
    for (int i = 0; i < recipes.Count; i++) {
        m_CraftingRecipes.Add(recipes[i]);
    }
}


Now in your custom crafter that inherits the Crafter class you can override the GetRecipes function and return whatever list of recipes you want.
I hope that does the trick :)
 
Beautiful! This works great, thanks!

Code:
    public override ListSlice<CraftingRecipe> GetRecipes()
    {
        playerRecipes = Statics.player.transform.GetComponent<CharacterCraftingData>().playerRecipes;
        foreach (var recipe in m_CraftingRecipes)
        {
            if (playerRecipes.Find(r => r == recipe))
            {
                playerRecipesToAdd.Add(recipe);
            }
        }

        playerRecipesToAdd = playerRecipesToAdd.Distinct().ToList();

        return playerRecipesToAdd;
    }

For whatever reason something about how the crafter is initialized kept creating duplicates in the list. I fixed it with Linq, but thought you'd like to know about it.
 
Top