Jump to content

Dont use AddIngredientValues in mods!


Recommended Posts

Conclusion of this thread:
Don't use AddIngredientValues in your mods if you want to add a new tag to an already existing thing, cause you will delete all old tags with this.
Instead you should use my function here:
http://forums.kleientertainment.com/topic/69732-addingredientvalues-waiter-mod/?do=findComment&comment=806505


@DarkXero or other experienced modders:
Do you know the Waiter mod? It adds alot new crock pot recipes, and also new tags for cooking.
I think I could improve the code of the mod, but since there are alot of people that subscribed to the mod, I want to make sure, that I'm right.

I wrote on the comments from Waiter mod:

Quote

I saw that the function AddIngredientValues() overwrites the tags from a prefab.

But this means waiter mod is incompatible to other mods and also to game changes by devs.
To prevent this you should use sth like this:

local cooking = require("cooking")
local ing = cooking.ingredients

local function tablemerge(t1, t2)
    for k, v in pairs(t2) do
        if (type(v) == "table") and (type(t1[k] or false) == "table") then
            tablemerge(t1[k], t2[k])
        else
            t1[k] = v
        end
    end
    return t1
end

function ingredientpostinit()
    --..
    -- seafood
    AddIngredientValues({"fish"}, tablemerge((ing["fish"] and ing["fish"].tags) or {},{seafood=1}),true) -- if third arg true, precook tag added for _cooked
    --..
    -- batwings
    AddIngredientValues({"batwing"}, tablemerge((ing["batwing"] and ing["batwing"].tags) or {},{wings=1,poultry=1,meat=0.5,monster=1}),true)
end


That way you get all the tags that already exist for the given prefab.

Am I right? I think this does work and I also implemented it this way to my copy of the mod and it does work.
But I'm uncertain if I can use the local "ing" without problems, regarding copy and deepcopy stuff.
Do I alter the cooking.ingredients already when I merge the tables?
Or are there other possible problems?


See also this thread, where I posted my previous solution:

 

Edited by Serpens
Link to comment
Share on other sites

I see no problems here.

There's no need to copy or deepcopy or whatever, if you are using the AddIngredientValues function.

That function will get the values of the table of the merge result, and construct a new table on the tags key of the ingredient value.

If you want to add a new tag to an existing food, you can just do:

local require = GLOBAL.require
local cooking = require("cooking")
local ingredients = cooking.ingredients

ingredients.fish.tags.seafood = 1

 

Link to comment
Share on other sites

8 minutes ago, DarkXero said:

I see no problems here.

There's no need to copy or deepcopy or whatever, if you are using the AddIngredientValues function.

That function will get the values of the table of the merge result, and construct a new table on the tags key of the ingredient value.

If you want to add a new tag to an existing food, you can just do:


local require = GLOBAL.require
local cooking = require("cooking")
local ingredients = cooking.ingredients

ingredients.fish.tags.seafood = 1

 

oh okay :D I thought for some reason we have to use the AddIngredientValues ... since this fn is also in the mod.lua file...
So every modder should not use the AddIngredientValues function, cause it overrides, instead just modify cooking.ingredients itself.

thank you :)

Link to comment
Share on other sites

10 minutes ago, Serpens said:

So every modder should not use the AddIngredientValues function, cause it overrides, instead just modify cooking.ingredients itself.

Well, the point is not to override old values.

It's supposed to provide a easy way to insert foods and tags with values.

Instead of having 5 lines for each mod, and 5 more lines for each tag, you cramp it all into 3 lines.

Link to comment
Share on other sites

8 minutes ago, DarkXero said:

Well, the point is not to override old values.

It's supposed to provide a easy way to insert foods and tags with values.

Instead of having 5 lines for each mod, and 5 more lines for each tag, you cramp it all into 3 lines.

"the point is, not to override old values" or " the point is not, to override values" ?

All I wanted to say is, that AddIngredientValues  should never be used in a mod, because it overrides old tags.
So if you want to add a "seafood" tag to fish and eel with help of AddIngredientValues, you have to use some kind of "tricks" like the code in my first post, or you have to write
AddIngredientValues({"fish", "eel"}, {meat=0.5, fish=1, seafood=1})
because just writing
AddIngredientValues({"fish", "eel"}, {seafood=1})
will delete the meat and fish tag.
But if another mod already added another tag, or the devs change the meat value from fish to 5 your mod will be incompatible.

-> so never use AddIngredientValues or with code like the one in first post

Link to comment
Share on other sites

8 minutes ago, Serpens said:

But if another mod already added another tag, or the devs change the meat value from fish to 5 your mod will be incompatible.

-> so never use AddIngredientValues or with code like the one in first post

Yes.

I mean that you use AddIngredientValues in case you add 5 new prefabs like "banana_loca", "squirty_grenade", "flavored_bazooka", "delicious_broccoli", "kindred_pumpkin".

Like

AddIngredientValues({"banana_loca", "squirty_grenade", "flavored_bazooka", "delicious_broccoli", "kindred_pumpkin"}, {veggie = 1})

It is not for overwriting.

Link to comment
Share on other sites

8 hours ago, DarkXero said:

Yes.

I mean that you use AddIngredientValues in case you add 5 new prefabs like "banana_loca", "squirty_grenade", "flavored_bazooka", "delicious_broccoli", "kindred_pumpkin".

Like


AddIngredientValues({"banana_loca", "squirty_grenade", "flavored_bazooka", "delicious_broccoli", "kindred_pumpkin"}, {veggie = 1})

It is not for overwriting.

Of course it is overwriting. Or what do I wrong?
Just add
AddIngredientValues({"fish", "eel"}, {seafood=1})
to a mod and fish won't have any meat or fish tag anymore. It will only be seafood. I already tested this.

You can also see in the cooking component the function itself, that it is overwriting.
Cause it makes
tags = { }
and then enters the new tags, like seafood. So all previous tags are deleted.

I doubt that you can be wrong... so where is my mistake?

 

EDIT:
Ah, I did not read your post carfully enough... the important word is new prefabs .. so when they are new, you can't overwrite anything... ok.
Thank you :)

 

Edited by Serpens
Link to comment
Share on other sites

9 hours ago, DarkXero said:

I see no problems here.

There's no need to copy or deepcopy or whatever, if you are using the AddIngredientValues function.

That function will get the values of the table of the merge result, and construct a new table on the tags key of the ingredient value.

If you want to add a new tag to an existing food, you can just do:


local require = GLOBAL.require
local cooking = require("cooking")
local ingredients = cooking.ingredients

ingredients.fish.tags.seafood = 1

 

So if i use this code on an old food and some other mod add things to this same food it will be compatible ?

Link to comment
Share on other sites

@Lumina:
yes, if the other modder also do it that way, and not use the AddIngredientValues function, which should ONLY be used when adding a complete new thing.
That's what I denounce here (the function should have the name "SetIngredientValues:
http://forums.kleientertainment.com/klei-bug-tracker/dont-starve-together/addingredientvalues-does-not-add-but-overrides-r1608/

 

Link to comment
Share on other sites

@DarkXero

I tried your code but it seems that it isn't working. The game didn't crash, but if, for example, use this :

 

        local require = GLOBAL.require
        local cooking = require("cooking")
        local ingredients = cooking.ingredients
        ingredients.blue_cap.tags.champignons = 1
        ingredients.blue_cap_cooked.tags.champignons = 1

        ingredients.green_cap.tags.champignons = 1
        ingredients.green_cap_cooked.tags.champignons = 1
        ingredients.red_cap.tags.champignons = 1
        ingredients.red_cap_cooked.tags.champignons = 1

and a recipe with :

tags.champignons and tags.champignons >=1.5 

the recipe don't work, even if it was fine with the

		AddIngredientValues({"blue_cap"}, {champignons=1})

I have no error in the log, it just seems to not take in account the tags.

 

Did i forgot something ? Make a mistake ?
       

Link to comment
Share on other sites

44 minutes ago, Lumina said:

Did i forgot something ? Make a mistake ?

So what are you doing and what is supposed to be happening?

By not using AddIngredientValues, you leave the veggie = 0.5, which may generate a different recipe with a higher priority.

I did

local require = GLOBAL.require
local cooking = require("cooking")
local ingredients = cooking.ingredients

ingredients.berries.tags.meat = 1

and then dumped 4 berries in a crock pot. It gave me a meaty stew.

Link to comment
Share on other sites

@DarkXero

I apologize : the code was fine but i tested it in a normal game, and forgot i have precisely the " Waiter" mod enabled. So all the tag i was putting on mushroom were overrided by this mod. Took me a little time to understand, since the tag on berrie was working fine. As far as i can tell in a quick test with only my mod, all seems to work fine.

Thanks for your help and the code. Sorry for disturbing you for something not related to your code.

 

If i wasn't already convinced that avoiding overriding is really important, i would be now.

Thanks also to @Serpens, because i wasn't knowing the issue with overriding before this post.

Link to comment
Share on other sites

if you use the waiter mod, you can also simply use the "mushrooms" tag, introduced by waiter.

and yes, like you noticed yourself now, AddIngredientValues is very evil if you use more than one mod that adds tags this way to exisiting things...
Also with AddIngredientValues({"blue_cap"}, {champignons=1}) you are deleting the veggie tag assigned by the game.

Link to comment
Share on other sites

40 minutes ago, Serpens said:

if you use the waiter mod, you can also simply use the "mushrooms" tag, introduced by waiter.

and yes, like you noticed yourself now, AddIngredientValues is very evil if you use more than one mod that adds tags this way to exisiting things...
Also with AddIngredientValues({"blue_cap"}, {champignons=1}) you are deleting the veggie tag assigned by the game.

Yes, i could, but if someone use my mod (if i made it public a day), without the waiter mod, my recipe will not work anymore. So, of course, i could use tag "mushrooms", and put something like "if mod waiter isnt enabled then  ingredients.blue_cap.tags.mushrooms = 1"** so it will work with and without the mod, i just don't know if it's very clean to do this this way. Is it ?

**(i don't remember the exact line, it's not this, but you got the idea.)

Link to comment
Share on other sites

you could simply check,
if ingredients.blue_cap.tags.mushrooms == nil then -- check if caps are already considered mushrooms
    ingredients.blue_cap.tags.champignons = 1
    -- ...
end

And instead if naming it champignons, you should still use "mushrooms" as tag. That way it will always work, if waiter is on, or if not.
If you name it champignon, you have to write 2 complete codes , one dealing with mushrooms and one with champignons.

Edited by Serpens
Link to comment
Share on other sites

Yes, it was what i was doing first, i will probably do it again. The "champignon" tag was mainly for test, but since it does have the same purpose than mushroom, it will be better to use the same tag than the waiter mod. I'll use specific tags for specifics purpose.

Thanks for the advices :)

So

if ingredients.blue_cap.tags.mushrooms == nil then -- check if caps are already considered mushrooms
    ingredients.blue_cap.tags.mushrooms = 1
    -- ...
end

 

Will be better than just

ingredients.blue_cap.tags.mushrooms = 1

?

Link to comment
Share on other sites

ingredients.blue_cap.tags.mushrooms = 1
will also work yes. In this case it is the best solution.
But there might be cases where you want to check, if waiter is active, and the "nil check" is an easy way to do it :)  (e.g if you want mushrooms value to be 0.5 if waiter is not active and otherwise to be unchanged <->  waiter could also change values some day)

Also everything will work, if you stay with champignon only. But since there are mods like "ShowMe" or "Craft Pot", that are showing all the tags, it is clearer to just have mushrooms and not also champignons and other tags for the same thing.

Edited by Serpens
Link to comment
Share on other sites

2 hours ago, Lumina said:

Ok, thanks :) Yes, it's better to avoid too many tags for the same use, i will try to be vigilant about this :)

Assuming you are referring to Waiter 101, I wasn't aware it was causing a Problem :(
I am working on fixing it to use this method next update

 

Link to comment
Share on other sites

8 minutes ago, MidrealmDM said:

Assuming you are referring to Waiter 101, I wasn't aware it was causing a Problem :(
I am working on fixing it to use this method next update

 

Don't worry, it's not something really problematic. Your mod is the main mod adding food and stuff like this, so problem of compatibility are hard to see i guess :)

And knowing that you couldn't use this method (addingredientvalues) for existing food isn't obvious.

 

Good luck with the changes. And i like your mod, by the way :)

Link to comment
Share on other sites

I wrote a small function that Inserts new tags, without deleting the old ones. It are just small changes to the original AddIngredientValues function:

local require = GLOBAL.require
local cooking = require("cooking")
local ingredients = cooking.ingredients

--NOTE: If the thing already had a tag with the same name, you will still overwrite the old value, unless keepoldvalues is true. E.g if fish already had a tag seafood with value 0.5 and now you use this function with value 1, the result will be 1.
function InsertIngredientValues(names, tags, cancook, candry, keepoldvalues) -- if cancook or candry is true, the cooked/dried variant of the thing will also get the tags and the tags precook/dried.
	for _,name in pairs(names) do
        if ingredients[name] == nil then -- if it is not cookable already, it will be nil. Following code is just a copy of the normal AddIngredientValues function
            ingredients[name] = { tags= {}}

            if cancook then
                ingredients[name.."_cooked"] = {tags={}}
            end

            if candry then
                ingredients[name.."_dried"] = {tags={}}
            end

            for tagname,tagval in pairs(tags) do
                ingredients[name].tags[tagname] = tagval
                --print(name,tagname,tagval,ingtable[name].tags[tagname])

                if cancook then
                    ingredients[name.."_cooked"].tags.precook = 1
                    ingredients[name.."_cooked"].tags[tagname] = tagval
                end
                if candry then
                    ingredients[name.."_dried"].tags.dried = 1
                    ingredients[name.."_dried"].tags[tagname] = tagval
                end
            end
        else    -- but if there are already some tags, don't delete previous tags, just add the new ones. 
            for tagname,tagval in pairs(tags) do
                if ingredients[name].tags[tagname]==nil or not keepoldvalues then -- only overwrite old value, if there is no old value, or if keepoldvalues is not true (will be not true by default)
                    ingredients[name].tags[tagname] = tagval -- this will overwrite the old value, if there was one
                end
                --print(name,tagname,tagval,ingtable[name].tags[tagname])

                if cancook then
                    if ingredients[name.."_cooked"] == nil then
                        ingredients[name.."_cooked"] = {tags={}}
                    end
                    if ingredients[name.."_cooked"].tags.precook==nil or not keepoldvalues then
                        ingredients[name.."_cooked"].tags.precook = 1
                    end
                    if ingredients[name.."_cooked"].tags[tagname]==nil or not keepoldvalues then
                        ingredients[name.."_cooked"].tags[tagname] = tagval
                    end
                end
                if candry then
                    if ingredients[name.."_dried"] == nil then
                        ingredients[name.."_dried"] = {tags={}}
                    end
                    if ingredients[name.."_dried"].tags.dried==nil or not keepoldvalues then
                        ingredients[name.."_dried"].tags.dried = 1
                    end
                    if ingredients[name.."_dried"].tags[tagname]==nil or not keepoldvalues then    
                        ingredients[name.."_dried"].tags[tagname] = tagval
                    end
                end
            end
        end
	end
end

Just add this somewhere at the top of your script and after it insert your new tags with
InsertIngredientValues(names, tags, cancook, candry, keepoldvalues)
names is a list of prefabs and tags a list of the tags and values you want to add. So the same arguments like it was for the AddIngredientValues function.

This can also be used with new things and with things that were not cookable before.
Read the comments I made into the code for explanation of the arguments cancook, candry and keepoldvalues.

Link to comment
Share on other sites

An example of how to use the function:
The Waiter mod includes lines like:
 

AddIngredientValues({"fish", "eel"}, {meat=0.5, fish=1, seafood=1})
AddIngredientValues({"fish_cooked", "eel_cooked"}, {meat=0.5, fish=1, seafood=1, precook=1})

They can be changed now to:
 

InsertIngredientValues({"fish", "eel"}, {meat=0.5, fish=1, seafood=1},true,false,true)

since "keepoldvalues" is true, the meat and fish values won't be used, if tags with this name already exist. That way we can make sure to not overwrite game or other mods values. But if they do not exist before, the values 0.5 and 1 will be added.

Or even better to only insert the seafood tag to normal and cooked variant:

InsertIngredientValues({"fish", "eel"}, {seafood=1},true)

 

Examples for new things or things that were not cookable before:
 

Spoiler

-- since the Insert function includes the AddIngredient function, you can also use it for new things

-- AddIngredientValues({"batwing"}, {meat=0.5, poultry=1, wings=1, monster=0.5})
-- AddIngredientValues({"batwing_cooked"}, {meat=0.5, poultry=1, wings=1, monster=0.5, precook=1})
InsertIngredientValues({"batwing"}, {meat=0.5, poultry=1, wings=1, monster=0.5},true)

-- AddIngredientValues({"grapricot"}, {fruit=.5, grapes=1})
-- AddIngredientValues({"grapricot_cooked"}, {fruit=.5, citrus=1, grapes=1, precook=1})
InsertIngredientValues({"grapricot"}, {fruit=.5, grapes=1})
InsertIngredientValues({"grapricot_cooked"}, {fruit=.5, grapes=1, citrus=1, precook=1})

 

 


Edit:
If you use this function with new things or things that were not cookable before, don't forget to add also basic tags like "meat" or "veggie" ;)

Edited by Serpens
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
  • Create New...