Replace a specific recipe result

Place to get help with not working mods / modding interface.
User avatar
oldskoolmatt
Inserter
Inserter
Posts: 21
Joined: Wed Nov 11, 2020 10:23 am
Contact:

Replace a specific recipe result

Post by oldskoolmatt »

Hello lads, basically what i need to do is this:

Code: Select all

	type = "recipe",
	name = "MY_RANDOM_RECIPE",
--------[...]--------
	results =
	  {
	  {type = "item", name = "iron-plate", amount = 2},
	  {type = "item", name = "copper-plate", amount = 2},
	  {type = "item", name = "processing-unit", amount = 2} 
	  },
	  main_product= "iron-plate",
	},
in the results section i want to replace every single "processing-unit" with 2x iron-plate and 2x copper-plate, which in this case as the amount of processing units is x2 the correct result would be as if it was written like this:

Code: Select all

	results =
	  {
	  {type = "item", name = "iron-plate", amount = 2},
	  {type = "item", name = "copper-plate", amount = 2},
	  {type = "item", name = "iron-plate", amount = 4},
	  {type = "item", name = "copper-plate", amount = 4} 
	  },
As the example "processing-unit" would be called many times in other recipes, what would be the best solution to avoid changing it all by hand and recalculating the exact values?
Last edited by oldskoolmatt on Sat Apr 09, 2022 10:07 am, edited 1 time in total.
Image
PFQNiet
Filter Inserter
Filter Inserter
Posts: 289
Joined: Sat Sep 05, 2020 7:48 pm
Contact:

Re: [MODDING HELP] replace a specific recipe result

Post by PFQNiet »

Should be fairly straightforward.

Code: Select all

local function process(recipe)
  if recipe.result == "processing-unit" then
    recipe.result = nil
    recipe.results = {type="item", name="processing-unit", amount=recipe.result_count or 1}
    recipe.result_count = nil
  end
  if recipe.results then
    for i,result in pairs(recipe.results) do
      if result.type == "item" and result.name == "processing-unit" then
        table.remove(recipe.results,i)
        table.insert(recipe.results, {type="item", name="iron-plate", amount=2*result.amount})
        table.insert(recipe.results, {type="item", name="copper-plate", amount=2*result.amount})
      end
    end
  end
end

for _,recipe in pairs(data.raw.recipe) do
  if recipe.normal or recipe.expensive then
    if recipe.normal then process(recipe.normal) end
    if recipe.expensive then process(recipe.expensive) end
  else
    process(recipe)
  end
end
That should replace any processing-unit results in all recipes with 2 iron + 2 copper plates. I haven't tested, but I assume the game engine just adds together duplicate result entries, so it won't cause issues if iron or copper plates are also results of the recipe.

If you want, you can use similar code for replacing the ingredients as well as the results.
User avatar
oldskoolmatt
Inserter
Inserter
Posts: 21
Joined: Wed Nov 11, 2020 10:23 am
Contact:

Re: [MODDING HELP] replace a specific recipe result

Post by oldskoolmatt »

PFQNiet wrote: Sun Nov 15, 2020 12:53 pm Should be fairly straightforward.

Code: Select all

local function process(recipe)
  if recipe.result == "processing-unit" then
    recipe.result = nil
    recipe.results = {type="item", name="processing-unit", amount=recipe.result_count or 1}
    recipe.result_count = nil
  end
  if recipe.results then
    for i,result in pairs(recipe.results) do
      if result.type == "item" and result.name == "processing-unit" then		--------lua:9
        table.remove(recipe.results,i)
        table.insert(recipe.results, {type="item", name="iron-plate", amount=2*result.amount})
        table.insert(recipe.results, {type="item", name="copper-plate", amount=2*result.amount})
      end
    end
  end
end

for _,recipe in pairs(data.raw.recipe) do
  if recipe.normal or recipe.expensive then
    if recipe.normal then process(recipe.normal) end		--------lua:20
    if recipe.expensive then process(recipe.expensive) end
  else
    process(recipe)
  end
end
That should replace any processing-unit results in all recipes with 2 iron + 2 copper plates. I haven't tested, but I assume the game engine just adds together duplicate result entries, so it won't cause issues if iron or copper plates are also results of the recipe.

If you want, you can use similar code for replacing the ingredients as well as the results.
Thanks for the answer mate!
Anyways the code returns an error and i'm not quite sure why cause it looks correct to me, i'll try some editing and post the eventual solution:

Code: Select all

[..]lua:9:attempt to index local "result" (a number value)

stack traceback:
lua:9: in function "process"
lua:20:in main chunk
UPDATE, I GOT IT WORKING:

Code: Select all

local function process(recipe)
    if recipe.result == "processing-unit" and recipe.name ~= "processing-unit" then	--------Added check name not equal (~=) to avoid original recipe being messed up
    recipe.result = nil
    recipe.results = {type="item", name="processing-unit", amount=recipe.result_count or 1}
    recipe.result_count = nil
  end
  if recipe.results then
    for i,result in pairs(recipe.results) do
      if recipe.results and recipe.name ~= "processing-unit" then			--------Added check name not equal (~=) to avoid original recipe being messed up
        table.remove(recipe.results,i)
        table.insert(recipe.results, {type="item", name="iron-plate", amount=2*result.amount})
        table.insert(recipe.results, {type="item", name="copper-plate", amount=2*result.amount})
      end
    end
  end
end

for _,recipe in pairs(data.raw.recipe) do
    process(recipe)
end
not sure tho on why the recipe.normal or recipe.expensive statements caused havoc...
Image
Pi-C
Smart Inserter
Smart Inserter
Posts: 1742
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: [MODDING HELP] replace a specific recipe result

Post by Pi-C »

oldskoolmatt wrote: Sun Nov 15, 2020 1:30 pm
PFQNiet wrote: Sun Nov 15, 2020 12:53 pm Should be fairly straightforward.
Not quite. This will work for a recipe that you have just defined. But don't take for granted that it will work when other mods have had a chance to mess with it!

The problem is, recipe.results is expected to be a table of ProductPrototype. recipe.results.type is an optional property and will default to "item" if not set. So you can't rely on it being present.

I'd make the code more flexible:

Code: Select all

  if recipe.results then
    local name, amount, amount_min, amount_max, probability, catalyst_amount
    for i,result in pairs(recipe.results) do
      -- Long version, may also take other properties like amount_min or probability!
      if   result.type and result.type == "item" and result.name == "processing-unit" then
        name = result.name
        amount = result.amount
        amount_min = result.amount_min
        amount_max = result.amount_max
        probability = result.probability_amount
      -- Short version
      elseif not result.type and result[1] == "processing-unit" then
        name = result[1]
        amount = result[2]
      -- Result is not a processing-unit
      else
        name = nil
      end

      if name then
        table.remove(recipe.results,i)
        table.insert(recipe.results, {type="item", name="iron-plate", amount=2*amount, amount_min = amount_min, amount_max = amount_max, probability = probability, catalyst_amount = catalyst_amount})
        table.insert(recipe.results, {type="item", name="copper-plate", amount=2*amount, amount_min = amount_min, amount_max = amount_max, probability = probability, catalyst_amount = catalyst_amount})
      end
    end
  end
end
Also, looking at what oldskoolmatt expects as new results:

Code: Select all

results =
	  {
	  {type = "item", name = "iron-plate", amount = 2},
	  {type = "item", name = "copper-plate", amount = 2},
	  {type = "item", name = "iron-plate", amount = 4},
	  {type = "item", name = "copper-plate", amount = 4} 
	  },
I'm not sure (and can't check right now -- not at home) if this will work. Will the game automatically add the 2 iron-plates from the original recipe to the 4 iron-plates from the new result? Will {type = "item", name = "iron-plate", amount = 4} overwrite {type = "item", name = "iron-plate", amount = 2}? In this case, you'd have to go over the other results that are not replaced, check if any of them has the same name as the new results, add the amounts, and remove both of the old results.

Recipes may look simple, but having a "should be straightforward" attitude about them spells T-R-O-U-B-L-E according to my book. :-)
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
User avatar
Deadlock989
Smart Inserter
Smart Inserter
Posts: 2529
Joined: Fri Nov 06, 2015 7:41 pm

Re: [MODDING HELP] replace a specific recipe result

Post by Deadlock989 »

Pi-C wrote: Sun Nov 15, 2020 3:15 pm I'm not sure (and can't check right now -- not at home) if this will work. Will the game automatically add the 2 iron-plates from the original recipe to the 4 iron-plates from the new result? Will {type = "item", name = "iron-plate", amount = 4} overwrite {type = "item", name = "iron-plate", amount = 2}? In this case, you'd have to go over the other results that are not replaced, check if any of them has the same name as the new results, add the amounts, and remove both of the old results.

Recipes may look simple, but having a "should be straightforward" attitude about them spells T-R-O-U-B-L-E according to my book. :-)
Strangely enough the game does allow it (from memory the same isn't true for ingredients). I guess it's useful for flexibility with results that have probabilities, so e.g. you can guarantee 1 result and then have an arbitrary probability of a second. Without probabilities you end up with things like this though, which is ugly:

Untitled2.jpg
Untitled2.jpg (19.29 KiB) Viewed 2250 times

All of the given advice so far also fails to take normal and expensive recipe data into account. The best way to handle it would be to look in root, normal and expensive, build a table indexed by ingredient name, add your new results to any existing ones or create a new entry if not, then overwrite the results in each of the three possible properties.
Pi-C
Smart Inserter
Smart Inserter
Posts: 1742
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: [MODDING HELP] replace a specific recipe result

Post by Pi-C »

Deadlock989 wrote: Sun Nov 15, 2020 3:21 pm
Pi-C wrote: Sun Nov 15, 2020 3:15 pm I'm not sure (and can't check right now -- not at home) if this will work. Will the game automatically add the 2 iron-plates from the original recipe to the 4 iron-plates from the new result? Will {type = "item", name = "iron-plate", amount = 4} overwrite {type = "item", name = "iron-plate", amount = 2}? In this case, you'd have to go over the other results that are not replaced, check if any of them has the same name as the new results, add the amounts, and remove both of the old results.
Strangely enough the game does allow it (from memory the same isn't true for ingredients). I guess it's useful for flexibility with results that have probabilities, so e.g. you can guarantee 1 result and then have an arbitrary probability of a second.
Ah, makes sense!
Without probabilities you end up with things like this though, which is ugly:
Yikes! :-)
All of the given advice so far also fails to take normal and expensive recipe data into account. The best way to handle it would be to look in root, normal and expensive, build a table indexed by ingredient name, add your new results to any existing ones or create a new entry if not, then overwrite the results in each of the three possible properties.
PFQNiet had a small loop where normal/expensive variants were checked -- right at the bottom of the posting I quoted. That's why I didn't mention these again. :-D
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
User avatar
oldskoolmatt
Inserter
Inserter
Posts: 21
Joined: Wed Nov 11, 2020 10:23 am
Contact:

Re: [MODDING HELP] replace a specific recipe result

Post by oldskoolmatt »

Pi-C wrote: Sun Nov 15, 2020 3:15 pm
oldskoolmatt wrote: Sun Nov 15, 2020 1:30 pm
PFQNiet wrote: Sun Nov 15, 2020 12:53 pm Should be fairly straightforward.
Not quite. This will work for a recipe that you have just defined. But don't take for granted that it will work when other mods have had a chance to mess with it!

The problem is, recipe.results is expected to be a table of ProductPrototype. recipe.results.type is an optional property and will default to "item" if not set. So you can't rely on it being present.

I'd make the code more flexible:

Code: Select all

  if recipe.results then
    local name, amount, amount_min, amount_max, probability, catalyst_amount
    for i,result in pairs(recipe.results) do
      -- Long version, may also take other properties like amount_min or probability!
      if   result.type and result.type == "item" and result.name == "processing-unit" then
        name = result.name
        amount = result.amount
        amount_min = result.amount_min
        amount_max = result.amount_max
        probability = result.probability_amount
      -- Short version
      elseif not result.type and result[1] == "processing-unit" then
        name = result[1]
        amount = result[2]
      -- Result is not a processing-unit
      else
        name = nil
      end

      if name then
        table.remove(recipe.results,i)
        table.insert(recipe.results, {type="item", name="iron-plate", amount=2*amount, amount_min = amount_min, amount_max = amount_max, probability = probability, catalyst_amount = catalyst_amount})
        table.insert(recipe.results, {type="item", name="copper-plate", amount=2*amount, amount_min = amount_min, amount_max = amount_max, probability = probability, catalyst_amount = catalyst_amount})
      end
    end
  end
end
Also, looking at what oldskoolmatt expects as new results:

Code: Select all

results =
	  {
	  {type = "item", name = "iron-plate", amount = 2},
	  {type = "item", name = "copper-plate", amount = 2},
	  {type = "item", name = "iron-plate", amount = 4},
	  {type = "item", name = "copper-plate", amount = 4} 
	  },
I'm not sure (and can't check right now -- not at home) if this will work. Will the game automatically add the 2 iron-plates from the original recipe to the 4 iron-plates from the new result? Will {type = "item", name = "iron-plate", amount = 4} overwrite {type = "item", name = "iron-plate", amount = 2}? In this case, you'd have to go over the other results that are not replaced, check if any of them has the same name as the new results, add the amounts, and remove both of the old results.

Recipes may look simple, but having a "should be straightforward" attitude about them spells T-R-O-U-B-L-E according to my book. :-)
Thanks mate, that looks good!
Now i'm stuck with the last of problems which is merely cosmetic, though the code works pretty well, the recipes are now ugly AF, they show both 2x iron plate and 4x iron plate as recipe output rather than six altogether as i originally thought, any way of merging the output without going onto data-final-fixes?
Last edited by oldskoolmatt on Sun Nov 15, 2020 3:58 pm, edited 1 time in total.
Image
User avatar
Deadlock989
Smart Inserter
Smart Inserter
Posts: 2529
Joined: Fri Nov 06, 2015 7:41 pm

Re: [MODDING HELP] replace a specific recipe result

Post by Deadlock989 »

Pi-C wrote: Sun Nov 15, 2020 3:31 pm PFQNiet had a small loop where normal/expensive variants were checked -- right at the bottom of the posting I quoted. That's why I didn't mention these again. :-D
I didn't see that, I only looked at yours and oldskoolmatt's where it was missing.

Working with recipes is pretty annoying, that's for sure.
User avatar
Deadlock989
Smart Inserter
Smart Inserter
Posts: 2529
Joined: Fri Nov 06, 2015 7:41 pm

Re: [MODDING HELP] replace a specific recipe result

Post by Deadlock989 »

oldskoolmatt wrote: Sun Nov 15, 2020 3:56 pm Now i'm stuck with the last of problems which is merely cosmetic, though the code works pretty well, the recipes are now ugly AF, they show both 2x iron plate and 4x iron plate rather than six altogether as i originally thought, any way of merging the output without going onto data-final-fixes?
You've got no option but to loop through the whole results table (in any one or more of the three different difficulty locations) and merge duplicated results together, taking both the simple and long form of result data into account and preserving everything like probabilities, amount_min/max etc. The way I would do that is to read all the ingredients into a new table indexed by ingredient name so you can add things together easily and then overwrite the results (for that difficulty), but there are multiple ways of going about it.
User avatar
oldskoolmatt
Inserter
Inserter
Posts: 21
Joined: Wed Nov 11, 2020 10:23 am
Contact:

Re: [MODDING HELP] replace a specific recipe result

Post by oldskoolmatt »

Deadlock989 wrote: Sun Nov 15, 2020 3:59 pm
oldskoolmatt wrote: Sun Nov 15, 2020 3:56 pm Now i'm stuck with the last of problems which is merely cosmetic, though the code works pretty well, the recipes are now ugly AF, they show both 2x iron plate and 4x iron plate rather than six altogether as i originally thought, any way of merging the output without going onto data-final-fixes?
You've got no option but to loop through the whole results table (in any one or more of the three different difficulty locations) and merge duplicated results together, taking both the simple and long form of result data into account and preserving everything like probabilities, amount_min/max etc. The way I would do that is to read all the ingredients into a new table indexed by ingredient name so you can add things together easily and then overwrite the results (for that difficulty), but there are multiple ways of going about it.
Pardon mate but on this game i'm kinda noob when it comes to modding (and LUA), can you be more clear please?
Image
User avatar
Deadlock989
Smart Inserter
Smart Inserter
Posts: 2529
Joined: Fri Nov 06, 2015 7:41 pm

Re: [MODDING HELP] replace a specific recipe result

Post by Deadlock989 »

oldskoolmatt wrote: Sun Nov 15, 2020 4:11 pm Pardon mate but on this game i'm kinda noob when it comes to modding (and LUA), can you be more clear please?
You chose to start with something surprisingly tricky. Don't have time to work through it right now, sorry, someone else might have a go.
Post Reply

Return to “Modding help”