How to craft items with using time?

It should be possible, it'll require custom code though.

The crafting documenetation is here:

examples of creating custom crafting processors can be found here:



So the way I would go about it is keep a timer on the custom crafting processor and I would define the waiting time on the recipes using a custom Recipe type.

Then in the crafting processor you can override the craft function such that it removes the ingredients from the character automatically and then waits the amount of seconds defined on the custom recipe before adding it either to the character directly or in a chest such that the player can pick it up later.

Of course you'll need to create your own crafting UI to display the recipes being crafted and the time left.
If you need any help with that let me know I'm happy to help.
 
Can I get a tip for add a CraftingIngredients for time?

I added System.TimeSpan type. But, I can't see that in the inspector.
 
I figured out that I'll work a lot to add new type... :(

Can I get some an information for add the type?
 
Last edited:
Adding a new custom recipe means inheriting the base Recipe type and adding varaibles to it. I'm not sure TimeSpan is a serializable value. Try using floats.

As said before creating a custom crafting processor and recipe requires quite a bit of custom code. I'll try to guide you if you need help.

You can check the CraftingRecipeWithCurrency script for an example of a custom crafting recipe
C#:
    /// <summary>
    /// Crafting Recipe with currency included.
    /// </summary>
    [OverrideCraftingIngredients(typeof(CraftingIngredientsWithCurrency))]
    public class CraftingRecipeWithCurrency : CraftingRecipe
    {
        /// <summary>
        /// Makes sure that the ingredients are of the correct type.
        /// </summary>
        protected override void DeserializeIngredientsInternal()
        {
            if (m_Ingredients.GetType() == typeof(CraftingIngredientsWithCurrency)) { return; }

            var previousIngredients = m_Ingredients;
            m_Ingredients = new CraftingIngredientsWithCurrency();
            ReflectionUtility.ObjectCopy(previousIngredients, m_Ingredients);
        }
    }

For creating one with time you could do something like this:

Code:
public class CraftingRecipeWithTime : CraftingRecipe
    {
        [SerializeField] protected float m_CraftTime;

        public float CraftTime => m_CraftTime;
    }
(You could inherit from CraftingRecipeWithCurrency instead of CraftingRecipe if you want currency)

Once you have the crafting recipe coded you can head to the editor and set it in your crafting category. It should automatically appera in that drop down.
1597236781077.png

And then you'll see you time field under the "Other tab" within your recipes that are using that crafting category
1597236840235.png

Then in your custom processor when you override the CraftInternal function you can use a:

Code:
var craftinRecipeWithTime = recipe as CraftingRecipeWithTime;
if(craftinRecipeWithTime != null){
    var craftTime = craftinRecipeWithTime.CraftTime;

    //Do the crafting using time
}

Let me know if you need more help with this or if you have ideas on how to improve the crafting system
 
Following up on this thread as I wanted to add "crafting over time" functionality to my game as well! I was able to get a custom processor up and running with a custom recipe type that factors in time.

Amusingly enough, the part I'm stuck on is how to actually wait before returning the new CraftingResult. Since the crafting processor doesn't use Monobehavior, I can't use a Coroutine, but even if I could, I can't have a Coroutine that uses WaitForSeconds() and then also returns "return new CraftingResult(output, true);"

Any suggestions?
 
Even if you do not have monobehaviour you can still run asynchronous code.

Option 1: get reference to a monobehaviour and run a coroutine on that. Simply pass in an Action as parameter of the coroutine and invoke it after x seconds

Option 2: Use the C# async Tasks. They are a bit more complicated than coroutines but they can be used everywhere and not just on Monobehaviors. They also allow you to do things coroutines don't.

Option 3: Use Opsive Schedule singleton. That might be the simplest solution for one off wait call.

I personally use all of these, they each have their pros and cons.
If you plan to allow pausing/cancelling the crafting process half way I would look into solution 2. Option 1 may also work as long as you keep reference of the coroutine when you start it
 
Starting off with Option 1 for now:

I got part of the way there, I think. I'm unsure of what "Action" to pass through to my coroutine, however. I originally thought that that return new CraftingResult(output, true); is what was crafting the item and putting it into the inventory, but it is actually var output = CreateCraftingOutput(recipe, inventory, quantity);

How would I got about passing CreateCraftingOutput as an Action? All my attempts have yielded lots of visual studio errors that I can't figure out :)

This is a bit out of my wheelhouse since I'm just learning - sorry for the noob question again!
 
So I just realised you can't return the crafting result since the Craft function isn't async. That's not a problem since we never actually use the crafting result. it's there for people who whish to use it, but I don't think it is your case.

Here I made an example with Scheduler.Schedule. But you could easily replace it by your own monobehavior.
Code:
//var output = CreateCraftingOutput(recipe, inventory, quantity);

// Example using the scheduler to call the function after 2 seconds.
// () => {} is called a lambda function. You can learn more about lambda and delegates online.
Scheduler.Schedule(2f, () => CreateCraftingOutput(recipe, inventory, quantity));

// You can't know the result right now because the crafting hasn't finished.
// You could predict the result if you need to, but i don't think you will. So just return success with no crafting output.
return new CraftingResult(null, true);

I hope that helps.

If in the future you wish to return the CraftingResult after the crafting is done it is possible but you'll need to create a few additional async functions both in the Crafting Menu and in the processor.
 
Beautiful! Got it working with a nice little progress bar. There's obviously a ton to do to make it extendable, but I do have another question.

How would I pull the item definition of the output so I can display a sprite on the progress bar of the item being crafted (see red circle in image)?

The farthest I got was with Debug.Log(recipe.DefaultOutput.MainItemAmount); which isn't far :) Is there is a way to access the item definition of the output so I could access its Attributes if I wanted to?
 

Attachments

  • Screen Shot 2020-09-24 at 8.55.17 PM (2).png
    Screen Shot 2020-09-24 at 8.55.17 PM (2).png
    300.3 KB · Views: 26
Hello there! I just got this asset today and love it! though I feel a bit dumb... trying to make a smelting system like Minecraft/Terraria etc but im still learning to code so bit stuck... i made a recipetype for itScreenshot 2022-04-19 210115.png

and set up a recipe
Screenshot 2022-04-19 210225.png

but having a hard time trying to figure out what to put in the processor...

to craft the time and show a progress bar, also not sure how id do the UI, i know its alot to ask but... would anyone be willing to help step by step? /shy

Screenshot 2022-04-19 210502.png
 
Just like cptscrimshaw asked before above. What you need is to run a loop asynchonsouly. Here I explained three solutions

You'll need a custom UI for this. You can either write it from scratch or inherit Crafting Menu and override the different functions

In steps
1) Start crafting process if can start (don't actually craft yet, just remove the items)
2) Run a wait time loop aysnchonrously using coroutine
3) Use an image with a fill amount showing the percentage progress
4) Actually craft the item after the time has passed

It is up to you if you want to only have item item crafting at any time and if you can stop the crafting mid way.

Note that this requires a faire amount of custom code. So start simple and add things bit by bit as things start to work and make sense.
 
Just like cptscrimshaw asked before above. What you need is to run a loop asynchonsouly. Here I explained three solutions

You'll need a custom UI for this. You can either write it from scratch or inherit Crafting Menu and override the different functions

In steps
1) Start crafting process if can start (don't actually craft yet, just remove the items)
2) Run a wait time loop aysnchonrously using coroutine
3) Use an image with a fill amount showing the percentage progress
4) Actually craft the item after the time has passed

It is up to you if you want to only have item item crafting at any time and if you can stop the crafting mid way.

Note that this requires a faire amount of custom code. So start simple and add things bit by bit as things start to work and make sense.
hiii, Thanks! think i managed to get the processor working using the default craft UI atm after trying for a while, it removes the ingredients then waits then gives the item in Inventory, not sure if this is fully correct but seems to work so far...

C#:
using Opsive.Shared.Game;
using Opsive.Shared.Utility;
using Opsive.UltimateInventorySystem.Core.DataStructures;
using Opsive.UltimateInventorySystem.Core.InventoryCollections;
using Opsive.UltimateInventorySystem.Crafting;
using Opsive.UltimateInventorySystem.Crafting.Processors;

public class SmeltingProcessor : SimpleCraftingProcessor
{
    private float craftTime;
    private float progressPercent;

    protected override CraftingResult CraftInternal(CraftingRecipe recipe, IInventory inventory,
        ListSlice<ItemInfo> selectedIngredients, int quantity)
    {
        if (CanCraftInternal(recipe, inventory, selectedIngredients, quantity) == false)
        {
            return new CraftingResult(null, false);
        }
        else
        {
            if (RemoveIngredients(inventory, selectedIngredients) == false)
            {
                return new CraftingResult(null, false);
            }          

            if(recipe is SmeltingRecipe smeltingRecipe)
            {
                craftTime = smeltingRecipe.CraftTime;
            }

            Scheduler.Schedule(craftTime, () => CreateCraftingOutput(recipe, inventory, quantity));

            return new CraftingResult(null, true);
        }
    }
}

The only thing I'm not sure about now is how to build and connect the custom UI ;-;


I'd like to drag the ore from inventory to the left slot then it will start the craft and go to the right slot when done, i did a simple UI with 4 Images but then realized probably must use UI Designer to create it to allow dragging onto the slot, i tried to use the default craft UI and delete stuff and keep 3 in the grid, 1 for input, progress and output but it didn't really work and made a mess... sorry for being dumb ;-;
 

Attachments

  • Screenshot 2022-04-20 145013.png
    Screenshot 2022-04-20 145013.png
    380.7 KB · Views: 5
  • Screenshot 2022-04-20 165906.png
    Screenshot 2022-04-20 165906.png
    135.3 KB · Views: 4
No worries.
I think you are just trying to take things a bit too quickly.
If you want to drag and drop the items to craft you'll definetly need a completely custom crafting menu.
What I would recommend is that you have a look at writing a custom ItemViewSlotContainer:

The ItemViewSlots are the UI where you can drag and drop items inside and they'll show an ItemView. The ITemViewSlotContainer is a component that groups multiple ItemViewSlots toghther. The InventoryGrid, Hotbars, Equipment UI, etc... are all inheriting from that class.

Start by making a ui that remembers what items you've set in those slots. Then once you have that, make a button to start the crafting on the Crafter. For that you'll need to know the recipe from the the items set there. There is a utility function you can use to do that but it is not very performant (we only use it in the editor), so I would recommend you write you own and cache the result. There should be some other forum post that explain that in more detail:
 
Top