Dynamically add a recipe based on research

Started by Rikiki, December 14, 2014, 05:57:51 PM

Previous topic - Next topic

Rikiki

Hi!

The old Alpha 7-way of adding a recipe to a workbench when a research is finished does not work anymore. :(
DefDatabase<ThingDef>.GetNamed("ElectronicWorkbench").recipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeBioBatteryCellsFromAnyVegetable"));
I am able to add the recipe in the database but the workbench does not seem to update its recipes list...

Do you know an elegant way to do it in Alpha 8? ???
Thanks!

Edit:
I also tried it the other way but with no success either... :'(

List<ThingDef> newUsers = new List<ThingDef>();
newUsers.Add(ThingDef.Named("ElectronicWorkbench"));
DefDatabase<RecipeDef>.GetNamed("MakeBioBatteryCellsFromAnyVegetable").recipeUsers = newUsers;


Looking further in the code, it seems not possible to "hack" the recipe list as it is cached in a private variable "private List<RecipeDef> allRecipesCached;"

RawCode

Original research at this part is pretty simple.

1) You should open standard bench definition (XML).
2) It have "recipes" field of type array\list that store available recipes.
3) Keyword for searching is "recipes" as match case && full word inside class "ThingDef".

4) Matching code is:


        public List<RecipeDef> AllRecipes
        {
            get
            {
                if (this.allRecipesCached == null)
                {
                    this.allRecipesCached = new List<RecipeDef>();
                    if (this.recipes != null)
                    {
                        for (int j = 0; j < this.recipes.Count; j++)
                        {
                            this.allRecipesCached.Add(this.recipes[j]);
                        }
                    }
                    List<RecipeDef> allDefsListForReading = DefDatabase<RecipeDef>.AllDefsListForReading;
                    for (int i = 0; i < allDefsListForReading.Count; i++)
                    {
                        if ((allDefsListForReading[i].recipeUsers != null) && allDefsListForReading[i].recipeUsers.Contains(this))
                        {
                            this.allRecipesCached.Add(allDefsListForReading[i]);
                        }
                    }
                }
                return this.allRecipesCached;
            }
        }


5) In order to add anything to bench, you must add recipe to database and invalidate (set to null) recipe cache.
This will cause cache rebuilding.

6) All fields have "this" field, but each def exists as single instance.

7) Final code is:


                ThingDef target = DefDatabase<ThingDef>.GetNamed("TableButcher");
                target.recipes.Add(DefDatabase<RecipeDef>.GetNamed("ButcherCorpseMechanoid"));
                typeof(ThingDef).GetField("allRecipesCached", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(target, null);


Tested in game - working from very first attempt.


Protip:
Fetch private cache variable and add recipe directly to it.

Rikiki

OK, thanks. :)
I don't really understand how System.Reflection can get access to a private member but I will use it tonight.

QuoteFetch private cache variable and add recipe directly to it.
Again, how can you modify this private member? ???
In Alpha 7, this was easily done as it was public, but now that it's private...

Anyway, thanks for your much appreciated help RawCode! :D

RawCode

If object is exposed you allowed to write to it.
Ever if field hosting object is private, if you have handle to object, you allowed to do with it anything you pleased.

Replace "recipes" with "AllRecipes" in my code and you will see it working without any reflection.