Expensive mode recipes for vanilla recipes

Place to get help with not working mods / modding interface.
Post Reply
rhynex
Long Handed Inserter
Long Handed Inserter
Posts: 63
Joined: Tue Apr 17, 2018 9:55 am
Contact:

Expensive mode recipes for vanilla recipes

Post by rhynex »

Hello,

I was testing a few things in lua and learnt it through trial-error and also copying other mods scripts. I decided to move forward and make some default game recipes more difficult to make by changing ingredients or adding more to them. For "Normal" difficulty games I dont want to change any recipe but for "Expensive" difficulty games I want to change some recipes. Some of the recipes I want to change do not have expensive mode, ex: Advanced Oil Processing.

I did the change with a dirty work but I am asking if there is a simplier and better way to do:

Code: Select all

data.raw.recipe["advanced-oil-processing"].normal=
{
    enabled=false,
    ingredients=
    {
        {type="fluid", name="water", amount=50},
        {type="fluid", name="crude-oil", amount=100}
    },
    results=
    {
        {type="fluid", name="heavy-oil", amount=10},
        {type="fluid", name="light-oil", amount=45},
        {type="fluid", name="petroleum-gas", amount=55}
    }
}
data.raw.recipe["advanced-oil-processing"].expensive =
{
    enabled=false,
    ingredients=
    {
        {type="fluid", name="sulfuric-acid", amount=20}, --- my change here
        {type="fluid", name="crude-oil", amount=100}
    },
    results=
    {
        {type="fluid", name="heavy-oil", amount=10},
        {type="fluid", name="light-oil", amount=45},
        {type="fluid", name="petroleum-gas", amount=55}
    }
}
I copy-pasted the default recipe ingredients to "normal.ingredients=". I dont want to do this for every recipe I want to change for expensive mode.
Is there an easy way to copy recipe ingredients to ".normal.ingredients=" and also results from to data.raw.recipe["advanced-oil-processing"].results to data.raw.recipe["advanced-oil-processing"].expensive.results ?

I dont want to change results of recipes but only inputs (amount or/and type).

Thanks

User avatar
darkfrei
Smart Inserter
Smart Inserter
Posts: 2904
Joined: Thu Nov 20, 2014 11:11 pm
Contact:

Re: Expensive mode recipes for vanilla recipes

Post by darkfrei »

Here is the code, just place into data.lua:

Code: Select all

-- make expensive

function double_ingredients (handler)
  if handler.ingredients then
    
    for i, ingredient in pairs (handler.ingredients) do
      if ingredient[2] then ingredient[2] = ingredient[2]*2 end
      if ingredient.amount then ingredient[2] = ingredient.amount*2 end
    end
  end
end

function double_time (handler)
  if handler.energy_required then
    if handler.energy_required then 
      handler.energy_required = handler.energy_required * 2 
    end
  else
    handler.energy_required = 1 -- default energy_required is 0.5
  end
end

for recipe_name, recipe in pairs (data.raw.recipe) do
  if not recipe.expensive then
    local normal = {}
    
    -- ingredients
    if recipe.ingredients then
      normal.ingredients = table.deepcopy(recipe.ingredients)
      recipe.ingredients = nil
    else
      log ('no ingredients by '..recipe_name)
    end
    
    -- result and results
    if recipe.result then
      normal.result = recipe.result -- not a table
      recipe.result = nil
    else
      log ('no result by '..recipe_name)
    end
    
    if recipe.results then
      normal.results = table.deepcopy(recipe.results)
      recipe.results = nil
    else
      log ('no results by '..recipe_name)
    end
    
    -- other stuff
    if recipe.enabled or recipe.enabled == false then
      normal.enabled = recipe.enabled -- not a table
      recipe.enabled = nil
    else
      log ('no enabled by '..recipe_name)
    end    
    
    if recipe.energy_required then
      normal.energy_required = recipe.energy_required -- not a table
      recipe.energy_required = nil
    else
      log ('no energy_required by '..recipe_name)
    end
    
    if recipe.result_count then
      normal.result_count = recipe.result_count -- not a table
      recipe.result_count = nil
    else
      log ('no result_count by '..recipe_name)
    end
    recipe.normal = normal
    recipe.expensive = table.deepcopy(normal)
    double_ingredients (recipe.expensive)
    double_time (recipe.expensive)
  end
end
Attachments
make_expensive.lua
(2.05 KiB) Downloaded 44 times

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Expensive mode recipes for vanilla recipes

Post by eradicator »

You should write a function for it. Here's a basic example how that could look like:

Define the function once:

Code: Select all

local function overwrite_expensive(name,ingredients)
  local recipe = data.raw.recipe[name]
  recipe.normal = {
    enabled = false,
    ingredients = recipe.normal.ingredients or recipe.ingredients,
    results = recipe.normal.results or recipe.results,
    result = recipe.normal.result or recipe.result,
  },
  recipe.expensive = {
    enabled = false,
    ingredients = ingredients,
    results = table.deepcopy(recipe.results or recipe.normal.results),
    result   = table.deepcopy(recipe.result  or recipe.normal.result),
    }
  end 
Use it many times:

Code: Select all

overwrite_expensive(
  "advanced-oil-processing",
  {
    {type="fluid", name="sulfuric-acid", amount=20}, --- my change here
    {type="fluid", name="crude-oil", amount=100}
  }
  )
[Edit: added normal because expensive overwrites direct and normal if normal is not present.]
Last edited by eradicator on Wed Nov 21, 2018 8:28 pm, edited 6 times in total.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

rhynex
Long Handed Inserter
Long Handed Inserter
Posts: 63
Joined: Tue Apr 17, 2018 9:55 am
Contact:

Re: Expensive mode recipes for vanilla recipes

Post by rhynex »

thanks a lot guys, both solutions are nice, I did something derived using the solutions you presented.

Code: Select all

local function overwrite_expensive(name,myingredients)
    -- get game recipe
    local recipe = data.raw.recipe[name]

    -- already has expensive mode?
    if recipe.expensive then
        recipe.expensive.ingredients = myingredients
    -- no expensive mode, need to copy stuff
    else
        -- copy normal recipe from game's default
        recipe.normal ={
	    enabled = recipe.enabled,
            energy_required = recipe.energy_required,
	    result = recipe.result,
	    results = recipe.results,
	    result_count = recipe.result_count,
	    ingredients = recipe.ingredients
        }
	-- copy expensive recipe from game's default but use my ingredients
        recipe.expensive ={
	    enabled = recipe.enabled,
            energy_required = recipe.energy_required,
	    result = recipe.result,
	    results = recipe.results,
	    result_count = recipe.result_count,
	    ingredients = myingredients
        }
    end

    -- reset stuff
    recipe.results = nil
    recipe.result = nil
    recipe.ingredients =nil
end 

overwrite_expensive( "advanced-oil-processing",
{
    {type="fluid", name="sulfuric-acid", amount=20},
    {type="fluid", name="crude-oil", amount=100}
})

overwrite_expensive( "processing-unit",
{
    {"arithmetic-combinator",2},
    {"decider-combinator",2},
    {"advanced-circuit", 2},
    {"electronic-circuit",2},
    {type="fluid", name="sulfuric-acid", amount=5},
})

overwrite_expensive( "production-science-pack",
{
     {"electric-engine-unit", 1},
     {"electric-furnace", 1},
     {"productivity-module", 1}
})
above workds like a charm.

cheers

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Expensive mode recipes for vanilla recipes

Post by eradicator »

From a quick look that will instantly break as soon as a recipe you're altering already has a .normal= node, but no direct definition. There are three possible locations for recipe definitions

E.g. ingredients can be:
recipe.ingredients (direct)
recipe.normal.ingredients (normal node)
recipe.expensive.ingredients (expensive node)

In that case recipe.ingredients will be treated as "normal" difficulty. If both recipe.ingredients and recipe.normal.ingredients exist i think the engine will ignore the recipe.normal node (or is it the other way around?). In any case there's no need to create a .normal= node, a recipe with direct and expensive nodes is perfectly valid.
[Edit: see below post.]

Additionally by removing the direct definition you might break someone elses mod if they doesn't properly check for direct/normal.

(Yes, recipe modding is far more complicated and annoying to do right that one might expect :p.)
Last edited by eradicator on Wed Nov 21, 2018 8:23 pm, edited 1 time in total.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

rhynex
Long Handed Inserter
Long Handed Inserter
Posts: 63
Joined: Tue Apr 17, 2018 9:55 am
Contact:

Re: Expensive mode recipes for vanilla recipes

Post by rhynex »

thanks for the info. I will probably not publish the mod for some time. I need a test run to see if it is really fun or not. expensive mod is already a challenge for me.

code works fine completely. normal mode recipes are not broken at all (only rocket part was broken because "hidden" was forgotten in my script posted)

recipe.ingredients (direct) << this is ignored if it normal/expensive exists
recipe.normal.ingredients (normal node)
recipe.expensive.ingredients (expensive node)

I remember game checks ".normal.ingredients" first even if ".ingredients" exist. I was not aware blue circuit had an expensive recipe. I changed the "normal" mod recipes first and realized how recipes worked. I decided to move all of the changes to expensive mode so I could also enjoy vanilla saves without restarting game after toggling my mod.

mod compatibility might be compromised I agree but if two mods are changing same game's default recipe then I would expect incompatibility. I do not have any other mod that changes game default mods

if someone does not check normal/expensive properly, is it my fault then ? :)

edit: I was idiot for not testing if game needed .normal for .expensive recipes, honestly I have no idea. maybe it will run fine. ex: Advanced Oil Processing

I would love to have some kind of complete recipe tree to be selected on map start to avoid mod complications. I used expensive mod for this reason (to be able to play both normal and expensive mod games without restarting, mod toggle)

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Expensive mode recipes for vanilla recipes

Post by eradicator »

rhynex wrote:
Wed Nov 21, 2018 3:13 pm
if someone does not check normal/expensive properly, is it my fault then ? :)
The rather more important question is: Do your potential users care if it's your fault or not? I could run my recipe normalizer on all recipes at startup if i wanted to, but i know it would break too many other mods, so i don't. (My normalizer converts all recipes to a full definition with normal/expensive and all ingredients/results as full tables instead of strings for items.) I try to do the minimal changes nessecary. And for a naive mod it's definetly unexpected to have a base recipe without direct definitions. Is that their bug? Sure. Can you avoid the bug on your side? Yes. It's a style question. And cross-compatibility is difficult.[Edit: see post below.]
rhynex wrote:
Wed Nov 21, 2018 3:13 pm
edit: I was idiot for not testing if game needed .normal for .expensive recipes,
It does not. [Edit: game will use expensive even in normal mode if normal doesn't exist] direct+expensive works fine. But many people don't know this and make stuff more complicated than it needs to be ;)
Last edited by eradicator on Wed Nov 21, 2018 8:26 pm, edited 2 times in total.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

rhynex
Long Handed Inserter
Long Handed Inserter
Posts: 63
Joined: Tue Apr 17, 2018 9:55 am
Contact:

Re: Expensive mode recipes for vanilla recipes

Post by rhynex »

eradicator wrote:
Wed Nov 21, 2018 4:34 pm
rhynex wrote:
Wed Nov 21, 2018 3:13 pm
edit: I was idiot for not testing if game needed .normal for .expensive recipes,
It does not. direct+expensive works fine. But many people don't know this and make stuff more complicated than it needs to be ;)
direct+expensive does not work fine. which game version are you checking this? can you give me a mod example?
just tested using below script. both normal and expensive mod requires electric engine unit

Code: Select all

data.raw.recipe["iron-axe"].expensive= 
{
  enabled = true,
  energy_required = data.raw.recipe["iron-axe"].energy_required,
  result = data.raw.recipe["iron-axe"].result,
  results = data.raw.recipe["iron-axe"].results,
  result_count = data.raw.recipe["iron-axe"].result_count,
  ingredients = 
  {
    {"electric-engine-unit", 123},
  }
}
edit: am I misunderstood? I do nto want to change normal difficulty recipes

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Expensive mode recipes for vanilla recipes

Post by eradicator »

You're right. Expensive overrides direct when normal isn't present. Must've changed somewhere down the line or my memory is shitty. (Or both :p.). Sorry for the fuzz. Will edit old posts so nobody else gets confused.
test
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

Post Reply

Return to “Modding help”