How to simplify crafting recipe system

I have a crafter/fuser in my game. Players can then put three objects on three pads like so:


Then, they press a button or whatnot to start it. The three objects are then put into a table using GetPartsInPart() from that semi-transparent red block.

I then make sure the parts are not character parts, etc…

I have stored recipes like so:

local DiHydrogen = {"Hydrogen", "Hydrogen"}
local Water1 = {"Hydrogen", "Hydrogen", "Oxygen"}
local Water2 = {"DiHydrogen", "Oxygen"}

I then use the following formula to check if the parts in the red block are equal to the recipes above

local function AreTablesEqual(t1, t2)
	if #t1 ~= #t2 then
		--warn("Wasn't equal, returning false.")
		return false
	end
	
	table.sort(t1)
	table.sort(t2)

	for i, v in ipairs(t1) do
		if v ~= t2[i] then
			--warn("if "..v.." is not equal to "..t2[i])
			--warn("v ~= t2[i], returning false.")
			return false
		end
	end
	
	--warn("No problems detected. Returning true.")
	return true
end

This ensures that we make sure the tables are equal. The problem is this entire block of code:

if AreTablesEqual(FusingPartsTable, DiHydrogen) then
			
			warn("FusingPartsTable is equal to the Recipe for DiHydrogen!")
			
			-- Remove the FusingParts
			for i,v in pairs(InstanceFusingPartsTable) do
				v:Destroy()
			end
			
			-- Spawn DiHydrogen
			local Clone = Molecules:WaitForChild("DiHydrogen"):Clone()
			Clone.Parent = ElementsPlaced
			Clone.Position = Output1Part.Position + Vector3.new(0, ydisplacement, 0)
			
			if Clone:FindFirstChild("Owner") then
				Clone:FindFirstChild("Owner").Value = player.Name
			end
			
		end

(This makes sure that if they are equal, it deletes the fusing parts, and spawns the fused item and puts it on the black pad in the first image.)

I have to copy paste this for every recipe that I add, and change all of the values. How the heck do I make this more simple?

Edit: FusingPartsTable stores the names of the parts, while InstanceFusingPartsTable stores the ObjectValue of each part, so that I can destroy them when the fusing is done.

If a player place the elements that is the recipe for Water1, you dont want to specifically ask a function to compare the elements of that recipe vs the items the player placed.

You call a function to iterate whole table to check if any table matches. If any table matches you know the player has all elements for a specific recipe, and return the name of that recipe.

Once returned you check in your Molecules folder to Find the item Molecule got from the function. Or even the function could return to you a reference of the Molecule stored in your ServerStorage to clone it, set it and give it to player

In that way its dynamic, and you only need 1 piece of code for all recipes. Turn it into a moduleScript its always a good idea for systems that could be widely used, thats just an extra.

Start by adding all recipes into 1 table, and each entry is a table with the data of each recipe

1 Like

How do I do that with a module script? I did think about doing it that way at the start, but I didn’t know how to do such a thing.

local DiHydrogen = {"Hydrogen", "Hydrogen"}
local recipes = {DiHydrogen}

for i,v in pairs(recipes) do 
    print(i)
    print(v)
    print(recipes[i])
end

This just prints 1 and {[1] = "Hydrogen", [2] = "Hydrogen"}, and recipes[i] is the same. How do I make it print “DiHydrogen”

1 Like

just create a module script and reference it from any script that needs to use the function to get the right recipe, but right now thats not the important thing.

Just turn your 3 recipes table into one first, and iterate that table until find a match based on the elements the player is providing, if match return what molecule is, if not, “no matching receipe”

1 Like
local All_Recipes = {
	["DiHydrogen"] = {"Hydrogen", "Hydrogen"}
}

for name, dataElements in pairs(All_Recipes) do 
	warn(name)
	print(dataElements)
end
1 Like

Now I get this error.

Expected identifier when parsing variable name, got ‘[’

Yo got a typo.

Show the code you are testing, the relevant one.

Alongside, just copy this code and paste it in command bar and hit enter, so you can test that your nested table of recipes does fine

local All_Recipes = {
	["DiHydrogen"] = {"Hydrogen", "Hydrogen"}
}

for name, dataElements in pairs(All_Recipes) do 
	warn(name)
	print(dataElements)
end
1 Like

An extra thing so you can make the iterator recipe check function better, is to arrange the elements required for a recipe in a better format.

Example.
If Dyhydrogen requires 2 “Hydrogen”, you could have only a table like this:

local All_Recipes = {
	["DiHydrogen"] = {H = 2}
}

This seems worse in practice. I would need to give everything the chemical formula, and check everything and gosh that would be much worse.

I dont understand what you mean by “worse in practice”, Im pretty sure it will be more performance-wise and way easy to compare what the player placed in the slots with the recipes.

If you give the instructions to players for recipes, same way. This is way better than having lots of string repeated lots of times, as
{"Hydrogen", "Hydrogen", "Hydrogen", "Hydrogen", "Hydrogen", "Hydrogen", "Hydrogen", "Hydrogen", "Hydrogen", "Hydrogen", "Hydrogen", "Hydrogen", "Hydrogen", "Hydrogen"}

You forget there are only three inputs at a time.

Being able to have dynamic functions which are not hardcoded is the thing

And also impossible for me to add at my knowledge of scripting unfortunately. I must hardcode every recipe, and I don’t mind doing it.

1 Like

I understand, sorry for pushing. My mistake.
You can do it! trust and learn!

Oh well. No, I mean, if you hardcode the recipes its not bad. I mean, do not hardcode the functions that will read them, or you will end up with a thousand of lines of repeated functions that are impossible to manage, impossible to handle, impossible to expand, and a crazy amount of code that you will desire to throw to garbage

Yep. Everything has been added into one function CheckRecipes which then loops through the recipes. Then the function inside of that AreTablesEqual checks if the items and recipe is the same. If so then it returns the name of the recipe, and I clone that recipe from my pre-made parts in ReplicatedStorage. Thanks for your help!

1 Like

Ah, I still have a problem! Do you know how to fix this?

local recipes = {

	--[[ Molecules ]]
	["Water"] = {"Hydrogen", "Hydrogen", "Oxygen"},
	["Water"] = {"DiHydrogen", "Oxygen"},

}

I want to give water two ways of making it. One is with DiHydrogen (two hydrogen atoms bonded together into one part) and oxygen. The other is with two separate hydrogen atoms and an oxygen.

I need to have it be the same “Water” as it returns that name when it is checked. If it was “Water1” and “Water2” I would need two of the same water molecules but just named differently in ReplicatedStorage. That would be annoying.

You need to start to nest more stuff in your recipe table, and return specific data from it

local recipes = {
	{Elems = {"Hydrogen", "Hydrogen", "Oxygen"}, Name = "Water"},
	{Elems = {"DiHydrogen", "Oxygen"}, Name = "Water"},
}


-- Another example that I insist will make your coding easier
local recipes = {
	{Elems = {H = 2, O = 1}, Name = "Water"},
	{Elems = {DiH = 1, O = 1}, Name = "Water"},
}
1 Like

How do I check that now with those changes?

local recipes = {
	{Elems = {"Hydrogen", "Hydrogen", "Oxygen"}, Name = "Water"},
	{Elems = {"DiHydrogen", "Oxygen"}, Name = "Water"},
}