How do I implement a crafting recipe filter?

dancinggoat23

New member
This may be a simple question, but for some reason I'm not seeing the answer. How would I implement a custom crafting recipe filter? My game will use a 'lore' system that ensures that the player cannot see recipes if she lacks the knowledge to use them. I thought this would be done with a Filter, but now I'm not sure.

Note: I need something finer-grained than categories, as I need to filter on a recipe-by-recipe basis.
 
Last edited:
FilterSorter is a generic class. Simply make a new script and inherit the FilterSorter<CraftingRecipe> class.
Then override the function to filter how you want to.

There are two functions to override:

Code:
public abstract ListSlice<T> Filter(ListSlice<T> input, ref T[] outputPooledArray);
and
Code:
public abstract bool CanContain(T input);

Here is an example of the base classes for the ItemInfo filters and sorters:
What you can do is simply replace the ItemInfo by CraftingRecipe and you should have some solid base classes to work from.
Code:
/// <summary>
/// Filter forItem Infos.
/// </summary>
public abstract class ItemInfoFilterBase : ItemInfoFilterSorterBase
{
    /// <summary>
    /// Filter the item info.
    /// </summary>
    /// <param name="itemInfo">The item info.</param>
    /// <returns>True if the list can contain the item info.</returns>
    public abstract bool Filter(ItemInfo itemInfo);
    /// <summary>
    /// Filter the list.
    /// </summary>
    /// <param name="input">The input list.</param>
    /// <param name="outputPooledArray">The reference to an output array.</param>
    /// <returns>The filtered/sorted list.</returns>
    public override ListSlice<ItemInfo> Filter(ListSlice<ItemInfo> input, ref ItemInfo[] outputPooledArray
    {
        outputPooledArray.ResizeIfNecessary(ref outputPooledArray, input.Count);
        var count = 0;
        for (int i = 0; i < input.Count; i++) {
            if (Filter(input[i]) == false) { continue; }
            outputPooledArray[count] = input[i];
            count++;
        }
        return (outputPooledArray, 0, count);
    }
    /// <summary>
    /// Filter the item info.
    /// </summary>
    /// <param name="itemInfo">The item info.</param>
    /// <returns>True if the list can contain the item info.</returns>
    public override bool CanContain(ItemInfo input)
    {
        return Filter(input);
    }
}
public abstract class ItemInfoSorterBase : ItemInfoFilterSorterBase
{
    public abstract Comparer<ItemInfo> Comparer { get; }
    /// <summary>
    /// Virtual awake is overriden.
    /// </summary>
    protected virtual void Awake()
    { }
    /// <summary>
    /// Filter the list.
    /// </summary>
    /// <param name="input">The input list.</param>
    /// <param name="outputPooledArray">The reference to an output array.</param>
    /// <returns>The filtered/sorted list.</returns>
    public override ListSlice<ItemInfo> Filter(ListSlice<ItemInfo> input, ref ItemInfo[] outputPooledArray
    {
        outputPooledArray.ResizeIfNecessary(ref outputPooledArray, input.Count);
        var count = input.Count;
        for (int i = 0; i < input.Count; i++) {
            outputPooledArray[i] = input[i];
        }
        Array.Sort(outputPooledArray, 0, count, Comparer);
        return (outputPooledArray, 0, count);
    }
    /// <summary>
    /// Filter the item info.
    /// </summary>
    /// <param name="itemInfo">The item info.</param>
    /// <returns>True if the list can contain the item info.</returns>
    public override bool CanContain(ItemInfo input)
    {
        return true;
    }
}

You can find those in the source code and dig around to find some more examples.

If you struggle with this do let me know exactly what you are trying to filter and I'll see how I can help
 
i create script like this :
C#:
using Opsive.Shared.Utility;
using Opsive.UltimateInventorySystem.Crafting;
using Opsive.UltimateInventorySystem.UI.Grid;
using System.Collections.Generic;
using UnityEngine;

public class CraftingRecipeFilter : CraftingRecipeFilterBase
{
    [SerializeField] protected List<CraftingRecipe> showRecipes;
    [SerializeField] protected List<CraftingRecipe> hideRecipes;
    public override bool Filter(CraftingRecipe recipe)
    {
        Debug.Log(recipe.ToString());
        var show = showRecipes.Count == 0;
        for(int i = 0; i < showRecipes.Count; i++)
        {
            if (showRecipes[i].ID != recipe.ID) { continue; }
            show = true;
            break;
        }
        if (show == false) { return false; }

        for (int i = 0; i < hideRecipes.Count; i++)
        {
            if (hideRecipes[i].ID == recipe.ID) { return false; }
        }

        return true;
    }
}
public abstract class CraftingRecipeFilterSorterBase : FilterSorter<CraftingRecipe>
{
    public override string ToString()
    {
        return GetType().Name;
    }
}

public abstract class CraftingRecipeFilterBase : CraftingRecipeFilterSorterBase
{
    public abstract bool Filter(CraftingRecipe recipe);

    public override ListSlice<CraftingRecipe> Filter(ListSlice<CraftingRecipe> input, ref CraftingRecipe[] outputPooledArray)
    {
        outputPooledArray.ResizeIfNecessary(ref outputPooledArray, input.Count);

        var count = 0;
        for (int i = 0; i < input.Count; i++)
        {
            if (Filter(input[i]) == false) { continue; }

            outputPooledArray[count] = input[i];
            count++;
        }

        return (outputPooledArray, 0, count);
    }

    public override bool CanContain(CraftingRecipe input)
    {
        return Filter(input);
    }
}

then I attach it to Crafting Recipe Grid Game Object, but it doesn't works. Did I miss something??
 
It all looks good to me.

Perhaps setting the component on the same game object is not enough.
I believe you need to specify it in the inspector.

For an InventoryGrid it would be here:
1690891066510.png

CraftingRecipeGrid should have a similar component where you can set your filtering

PS: You might need a MultiFilterSorter component to combine multiple filter/sorters
 
i've already done it, but it still doesn't work. it's look like the Filter() Function isn't called because when I debugged there, it can't be called.
 
That's odd.
Can you send me some screenshots of your inspectors or better even a small video (use streamable.com to share the video here in the forum).
Please do so, both at edit time and runtime. My guess is that something might be replacing the filter at runtime somehow.

If that doesn't help us figure it out. I will try to replicate your setup on my side and I'll try to figure it out from there
 
So I've been looking at this in alot more detail and I see what the issue is now.

The CraftingMenu has some set the CraftingTabData as the filterSorter. And that overrides the filter you set in the RecipeGrid.

The CraftingGrid tabs functions completely differently from the Inventory grid one.

I made some changes to the CraftingMenu so that it has options for filters and sorters. You can now combine multiple filters and sorters in addition to the tabs.

Please find the updated scripts below:
CraftingRecipeFilter and CraftingRecipeMultiFilter are new.

Thank you for your contribution and I hope this solves your issues
 

Attachments

  • CraftingMenuBase.cs
    11.8 KB · Views: 1
  • CraftingRecipeFilter.cs
    2.1 KB · Views: 1
  • CraftingRecipeMultiFilterSorter.cs
    1.8 KB · Views: 1
the filter has worked, but can't be combine with crafting tabs data, when I change the tab, the recipe can't be showed anymore.
even I didn't put crafting recipe filter, the tab recibe can't run since I change the CraftingMenuBase.
 
Last edited:
I'm sorry I should have tested more thouroughly.

Please change this function in the CraftingMenuBase script:
Code:
 /// <summary>
 /// Handle the tab change.
 /// </summary>
 /// <param name="previousIndex">The previous tab index.</param>
 /// <param name="newIndex">The new tab index.</param>
 /// <param name="draw">Should the recipes be drawn?</param>
 protected virtual void HandleTabChange(int previousIndex, int newIndex, bool draw)
 {
     if (previousIndex == newIndex) { return; }
     var tabToggles = m_CraftingRecipeGrid.TabControl.TabToggles;
     if (previousIndex >= 0 && previousIndex < tabToggles.Count) {
         var previousTabData = tabToggles[previousIndex]?.GetComponent<CraftingTabData>();
         if (previousTabData != null && previousTabData.CraftingFilter != null) {
             m_MultiFilterSorters.GridFilters.Remove(previousTabData.CraftingFilter);
         }
     }
     var craftingTabData = m_CraftingRecipeGrid.TabControl.CurrentTab.GetComponent<CraftingTabData>();
     if (craftingTabData == null) {
         Debug.LogWarning("The selected tab is either null or does not have an CraftingTabData", gameObject);
         return;
     }
     if (craftingTabData.CraftingFilter != null && m_MultiFilterSorters.GridFilters.Contains(craftingTabData.CraftingFilter) == false) {
         m_MultiFilterSorters.GridFilters.Add(craftingTabData.CraftingFilter);
     }
     if (draw) {
         DrawRecipes();
     }
 }

I've tested it in my example scene and it worked as expected
 
Top