Copy the recipes (modded or otherwise) from another building

Started by faeldray, March 24, 2017, 08:48:55 PM

Previous topic - Next topic

faeldray

I apologize if this has already be answered before but I cannot seem to find anything on the topic.

The situation is this: I don't really like how campfires disappear when they run out of fuel. I understand realistically why it happens (no more wood = no more campfire) but having to redo their bills all the time because my pawns are busy with other things is a pain. So I came up with the mod idea of a firepit. Almost exactly the same as a campfire, it just requires a few stone/steel to build and doesn't disappear when the fuel runs out.

I know I can just copy the XML for the campfire, change what I want, and name it "firepit" to create a new building. But...there are other mods that add more recipes to a campfire that I'd really like to be able to use. I know I could just name my firepit as "campfire" and just override the vanilla campfire, but ideally I'd like to have the vanilla one available if someone wants to use that instead.

So is there any way to copy or inherit recipes from the vanilla campfire to my firepit without overriding it? It doesn't seem to be possible with straight XML but I thought maybe it would be with C#. If it can be done, I'd greatly appreciate it if someone could point me in the right direction.

Apfelkuchenbemme

Sooo, you already have your <Defs> in a xml, or did I misunderstand something?

You can add whatever mod you want by putting its folder under the path "Steam\steamapps\common\RimWorld\Mods".

Just be sure to maintain a proper folder structure, especially whatever <texPath> you've given to your texture.

For example, I have added a gun by putting its About.xml in the folder "About", its Defs.xml in the folder "Def", its texure in the folder "Textures" and the three said folders were put in the folder "TestTest".

The folder "TestTest" was then put in the path I have posted above and I can activate the mod under the register "Mods" on the main menu.

faeldray

Er, that's not quite what I asked. Let me try explaining it another way...

I've already created a functioning mod that replaces the vanilla campfire with the "firepit" idea (changed destroyOnNoFuel to false, change the building materials, changed the texture, etc.). I have the defName set to Campfire so that it overwrites the vanilla one. I did this because I wanted it to also have any recipes that other mods would add (like fertilizer from Vegetable Garden, dried meat from Medieval Times, and so on). But ideally, I want the firepit to be a separate item from the campfire and not overwrite it. I'm just wondering if there's a way to do that AND still have the campfire recipes from other mods available on the firepit.

Apfelkuchenbemme

#3
Nonono, this is exactly what you've asked!

For example:

Have you ever thought to yourself "Man, It'd be great to have a pistol with 150 range and the option to attack the ground with it! I want it to otherwise function exactly like a pistol and I don't care that I can't draw for ****!".

Fear not, download the zip I have attached and put the folder it contains under your path "Steam\steamapps\common\RimWorld\Mods".

The moment you have smacked together a mod similarily to this ^and you have "created" a gun / building / whatever with your own <defName> for it, it will be there as another gun / building / whatever. (This is also how you "install" mods manually)

[attachment deleted by admin due to age]

faeldray

But if I change the <defName> from "Campfire" to "Firepit" (for example), then the firepit does not have the campfire recipes from mods.

dburgdorf

Are you creating this mod for yourself, or to distribute?

The reason I ask is that if it's just something you're using yourself, you can do what you want with XML. Simply copy the relevant recipe defs into your new mod's folder, edit those defs to include both the basic campfire and your new firepit as "users," and then make sure your mod loads after all the other mods whose defs you've altered. But obviously, that won't work in a mod you want to distribute, as you won't know which particular mods the end user actually has installed.

If you're looking to create something for distribution, you'd need to include a bit of C# code to copy campfire recipes to the firepit. I don't know off the top of my head how to do it, as recipe database storage isn't something I've had reason to look for in the source code, but it should be possible, and I wouldn't expect it even to be especially complex.
- Rainbeau Flambe (aka Darryl Burgdorf) -
Old. Short. Grumpy. Bearded. "Yeah, I'm a dorf."



Buy me a Dr Pepper?

Apfelkuchenbemme

#6
Bloody hell forgive me, I have misunderstood you!

The problem is, you can only inherit things via ParentName="...", if said "..." has been defined with Name="..." in the <ThingDef>.

So if you add Name="Campfire" in the <Thingdef> of the campfire in the Building_Temperature.xml, so it ends up like:

<ThingDef ParentName="BuildingBase" Name="Campfire">

you should be able to define your firepit by using ParentName="Campfire". Which of course brings us to the point dburgdorf has raised. The reason that your game has no idea what you mean if you put "ParentName="Campfire"" is that "ParentName" doesn't take whatever <defName> has been given to an object, but it looks for the "Name" that has been given to an object in its <ThingDef>.

As far as I can tell, this is defined under the Verse.XmlInheritance in the Assembly-CSharp.dll. However I have no idea how you would be able to change that behaviour in order to also take a <defName> into account in terms of inheritance.

faeldray

Quote from: dburgdorf on March 24, 2017, 09:42:30 PM
If you're looking to create something for distribution, you'd need to include a bit of C# code to copy campfire recipes to the firepit. I don't know off the top of my head how to do it, as recipe database storage isn't something I've had reason to look for in the source code, but it should be possible, and I wouldn't expect it even to be especially complex.

I would like to distribute it because I imagine there are other folks who might like the option for a non-disappearing campfire, especially if they like to begin as a tribe like I do. Besides, it would be a fun learning experience. :)

Do you happen to know if anyone has copied recipes using C# in their mod? I could probably figure it out if I had some examples to work with.

dburgdorf

Actually, I missed the obvious, as what Apfel suggested should work.  (Defining a name via defName and defining it via Name in the ThingDef tag are two different ways of doing the same thing.) Try defining your firepit with Campfire as a parent. If that doesn't work for some reason, we can investigate further.

OK, no, the inheritance doesn't seem to work, and even if it did, it probably wouldn't actually solve your problem, as it occurs to me that it would only give the firepit the traits of the campfire at the time the inheritance was processed, and not anything added later by mods.

After I posted my previous response about C# code, I took a quick look, and I think I spotted what I need. I can't guarantee getting to it tonight, but I should be able to get some usable code back to you sometime tomorrow morning.
- Rainbeau Flambe (aka Darryl Burgdorf) -
Old. Short. Grumpy. Bearded. "Yeah, I'm a dorf."



Buy me a Dr Pepper?

faeldray

Edit: Ah okay, just saw your edits, dburgdorf. I'll gladly take any code you can come up with, whenever you have a chance.

Ooooh. So if I'm understanding what you two are saying, I need to do something like this:

Create one ThingDef that will overwrite the vanilla campfire and allow it to be used as a parent object.

<ThingDef ParentName="BuildingBase" Name="Campfire">
    <defName>Campfire</defName>
    <!-- etc etc -->
</ThingDef>


And create another ThingDef for the firepit, using campfire as the parent.

<ThingDef ParentName="Campfire">
    <defName>Firepit</defName>
    <!-- etc etc -->
</ThingDef>


Is that about right? (I already tried doing the second set of code on its own and that didn't work.)

dburgdorf

If you overwrite the default campfire definition as in your example, the inheritance will work. But as I said, I don't think it'll actually solve your problem, since anything added to the campfire by mods after the inheritance is processed as part of the firepit's initial creation, won't be added to the firepit.

(But by all means, double-check that. I've already demonstrated tonight the folly of saying how things work without testing first to make sure I'm right!)
- Rainbeau Flambe (aka Darryl Burgdorf) -
Old. Short. Grumpy. Bearded. "Yeah, I'm a dorf."



Buy me a Dr Pepper?

faeldray

I tested it out and you were right. The inheritance worked but the modded recipes weren't there, just the vanilla ones. I also managed to duplicate a whole bunch of attributes at first. :o The firepit had 2 listings for fuel, 2 bill tabs, the vanilla recipes were doubled, etc. So that was interesting but I at least managed to fix that by getting rid of all of the duplicate attributes.

BlackSmokeDMax

Not sure if it is from another mod or something made exclusively for the pack, but the HardcoreSK mod pack has I believe just what you are aiming to make here.

They have a campfire ring that takes like 10 or 15 stone blocks to make. Then has bills that remain, because even when fuel runs out, the ring is still there. It also has a multiple fuel source menu, so not perfect for vanilla.

Maybe take a glance through there and see if you can find where it is in their pack. May be able to see how they did things to some idea of how to get yours up and running.

faeldray

Quote from: BlackSmokeDMax on March 25, 2017, 06:56:27 AM
They have a campfire ring that takes like 10 or 15 stone blocks to make. Then has bills that remain, because even when fuel runs out, the ring is still there. It also has a multiple fuel source menu, so not perfect for vanilla.

I can create the firepit and make it not disappear/lose its bills just fine, the problem is I want to copy/inherit the recipes that other mods add to the campfire. dburgdorf PMed me some C# code to try so I'm giving that a whirl.

RawCode

Quotedburgdorf PMed me some C# code
great community oriented behavior, PMing code instead sharing it with community will definely allow other users to find answers!*


*no, this is unacceptable actually.

static public void Main(string[] ignored)
{
Console.WriteLine("_yacil entry");

ThingDef dest = DefDatabase<ThingDef>.GetNamed("CraftingSpot",false);
ThingDef src = DefDatabase<ThingDef>.GetNamed("TableButcher",false);

//force game to cache data
object o = dest.AllRecipes;
o = src.AllRecipes;
if (o == null)
throw new Exception("to trick codeflow, in other case it will eliminate calls");

FieldInfo ff = typeof(ThingDef).GetField("allRecipesCached",(System.Reflection.BindingFlags)60);

object tmp = ff.GetValue(src);
ff.SetValue(dest,tmp);


Console.WriteLine("_yacil end");
}

run from [StaticConstructorOnStartup]