0
\$\begingroup\$

I'm currently working on a crafting system - this systems works fine when the desired item has only one requirement. If there is more then one requirement the system brakes.

Here's a short description of how the system should work:

  • Every object has a monobehaviour-script which holds an scriptable object.
  • This scriptable object contains all the statistics and has a list of required items.
  • The warehouse has a monobehaviour-script and this has a list of content items.
  • To craft a new item it should compare the content with the requirements.
  • Based on the output the object will spawn.

The my problem is getting the right output.

Item Class:

[System.Serializable]
public class Item
{
    public ScripableTradegood scripableTradegood;
    public int amount;
}

Warehouse:

[Header("Warehouse Content")]
public List<Item> warehouseContent;

Scriptable Object:

[Header("Requirements")]
public List<Item> tradegoodRequirements;

Here the problem:

// Loop through the requirements
    foreach (Item requirement in tradegoodRequirements)
    {
        // For each requirement check content in warehouse
        foreach (Item content in warehouse.warehouseContent)
        {
            // When content is not requirement skip the check
            if (content.scripableTradegood != requirement.scripableTradegood) continue;

            // When right content was found
            if (content.scripableTradegood == requirement.scripableTradegood)
            {

                // If required content amount isnt present return
                if (content.amount < requirement.amount) return;

                /*
                 * When the tradegood has only one requirement - I could just substract the requirements and exit out of the function.
                 * But when if I have more then one requirement - how can I check if all requirements a present before substracting them?
                 * Logically I would have to leave the second loop and continue to check the requirements from the first loop - But how can I achieve that? 
                 * */
            }
        }
    }

Where is my mistake? Is there something wrong with my approach?

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

The problem is that you did not distinguish whether the inner loop terminated with or without finding the item type you were looking for. You could fix that by introducing a new variable bool requirementFound which you set to false before that loop, and then set to true when finding the resource.

Then, after that loop, you can check if requirementFound is still false. When it is, then the loop didn't find any of that item type, which means the recipe can't be crafted. When it is true, that resource is available, so we can proceed with the next.

foreach (Item requirement in tradegoodRequirements)
{
    foreach (Item content in warehouse.warehouseContent)
    {
        var requirementFound = false;
        if (content.scripableTradegood != requirement.scripableTradegood) continue;

        // When right content was found
        if (content.scripableTradegood == requirement.scripableTradegood)
        {
            // If required content amount isnt present return
            if (content.amount < requirement.amount) return;
        } else {
           requirementFound = true;
           break;
        }

        if(requirementFound == false) {
             //resource is unvailable
             return;
        }
    }
}

However, there is a more concise way of writing this if you want to use Linq. If you add the using System.Linq directive to your script, then Lists (and all other collections) gain a whole bunch of useful new methods. Among them is the method .Contains(predicateFunction). Which in plain English tells you "Does this collection contain anything where this function returns true?". So instead of the whole inner loop you can do this:

if (warehouse.warehouseContents.Contains(
   (content) => { 
       content.scripableTradegood == requirement.scripableTradegood 
       && content.amount >= requirement.amount ;
   })
) {
    // This item is available in sufficient quantity
} else {
    // this item is not available in sufficient quantity
}

And then there is another useful method .All which tells you "Does this function return true for everything in this collection?". So you can substitute that whole code with this:

if( 
    tradegoodRequirements.All(
       (requirement) => {
           warehouse.warehouseContents.Contains(
               (content) => { 
                  content.scripableTradegood == requirement.scripableTradegood 
                   && content.amount >= requirement.amount;
               }
           );
        }
    )
) {
      // recipe is craftable
}

By the way: I assume you do ensure that the content of a warehouse can not contain multiple stacks of the same item type, right? Because if you allow that, then you can not just return; after you found a stack with an insufficient amount. There might be a second stack which is large enough. Or perhaps all the stacks together are large enough. That would complicate things a lot.

\$\endgroup\$
1
  • \$\begingroup\$ Hey Philipp - thanks for your answer I've learned a lot today! :) Unfortunately the Any-Method did not lead to the disered result - same for the Contains-Method which I actualy tried before a few days later. Both functions kept stucking at the first element or ignoring the requirements completle. And btw. - yes the warehouse can hve mutliple stacks. Soa s an conclusion - I will try to make up an another system and get better coding skills! Big THANKS and greetings :) \$\endgroup\$ Aug 18, 2022 at 16:11
0
\$\begingroup\$

I wanted to share the new crafting system I came up with.

The user have to add tradegoods to a workbench. This workbench has 3 slots and contains a list of recepies. Those recepies are scriptable tradegoods because every tradegood has list of requirements inside.

Every time a user adds an item to a workbench slot I call this function and compare two strings:

public void CheckRecepies()
{
    // Resets the string every time a player adds something to the workbench
    workbenchContent = string.Empty;

    // Check the workbench content a create the string
    foreach (Item content in workbenchSlots)
    {
        // If current slot is not empty then add
        if (content.scriptableTradegood != null)
        {
            // Setup the workbench content string
            workbenchContent += content.scriptableTradegood.name + content.amount;
        }
    }

    // IF THE STRING IS FILLED CHECK REQUIREMENTS

    // Loop through the tradegoods and get the requirements
    foreach (ScriptableTradegood tradegood in recepies)
    {
        // Reset the recepie string for every new tradegood
        recepieContent = string.Empty;

        // Get requirements from tradegood
        foreach (Item recepie in tradegood.tradegoodRequirements)
        {
            // Setup the recepie content string
            recepieContent += recepie.scriptableTradegood.name + recepie.amount;
        }

        // COMPARE TWO STRINGS IF STRINGS ARE EQUAL WORKBENCH FULLFILLES REQUIREMENTS 
        if (workbenchContent == recepieContent)
        {
            // Produce
            Debug.Log(recepieContent);
        }
    }
}

So far the system is working great for my purposes.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .