Ludeon Forums

RimWorld => Mods => Help => Topic started by: Lockdown on August 23, 2016, 12:45:12 PM

Title: Adding a field to an existing core class
Post by: Lockdown on August 23, 2016, 12:45:12 PM
I'm making a mod that adds a food selector, similar to the medicine selector. Looking at how medicine is implemented, I would need to add an additional field to Pawn_PlayerSettings so that each pawn instance remembers their current food restriction. Given that it's not possible to detour the entire Pawn_PlayerSettings class in order to add an extra field, what would be the best option to add this functionality, while keeping compatibility with other mods in mind?
Title: Re: Adding a field to an existing core class
Post by: 1000101 on August 23, 2016, 01:25:53 PM
You'd have to inherit the existing class and detour all the methods which instantiate it so they instantiate your class instead.

Alternately, you could store this extra information in a ThingComp and add the ThingComp to the pawns.  This is what I do for Prisoners & Slaves (https://ludeon.com/forums/index.php?topic=20854.0).
Title: Re: Adding a field to an existing core class
Post by: Lockdown on August 23, 2016, 04:04:00 PM
Quote from: 1000101 on August 23, 2016, 01:25:53 PM
You'd have to inherit the existing class and detour all the methods which instantiate it so they instantiate your class instead.

For clarity, you mean the constructors, right? Those seem to require GetConstructor() instead of GetMethod(), so am I right in assuming I have to detour them manually instead of using CCL's TryDetourFromTo?
Title: Re: Adding a field to an existing core class
Post by: JaxelT on August 23, 2016, 08:44:36 PM
No, he means make a new class that inherits the old class, and detour the methods that create new instances of Pawn_PlayerSettings so they create instances of your class instead.
Title: Re: Adding a field to an existing core class
Post by: 1000101 on August 23, 2016, 09:25:08 PM
Not sure what happened to your reply about ThingComps, but that is the best solution.
Title: Re: Adding a field to an existing core class
Post by: Lockdown on August 23, 2016, 11:41:54 PM
Quote from: JaxelT on August 23, 2016, 08:44:36 PM
No, he means make a new class that inherits the old class, and detour the methods that create new instances of Pawn_PlayerSettings so they create instances of your class instead.

Oh, of course! Thanks for clarifying.

Quote from: 1000101 on August 23, 2016, 09:25:08 PM
Not sure what happened to your reply about ThingComps, but that is the best solution.

I deleted that reply. While I realize a ThingComp may suit the situation better, I was left with more questions than answers after looking into them, and decided to go with the inherited class option for the time being, as detours and manipulating core classes are mainly what I'm trying to get the hang of at the moment. I'll re-visit the ThingComp option once I have more time to go through it, but for the meantime I just wanted to make a functional mod out of the goal I set out for myself, even if it's not done the best way.
Title: Re: Adding a field to an existing core class
Post by: 1000101 on August 24, 2016, 12:46:58 AM
They way you are planning is the worst possible solution.  You shouldn't be replacing core classes for many reasons unless absolutely necessary and in this case it is not.  At best you need to detour a couple methods simply to access the data you need to store.  The storage mechanism is best suited as a ThingComp which is dead simple to implement.
Title: Re: Adding a field to an existing core class
Post by: Lockdown on August 24, 2016, 02:07:25 AM
Well, I already finished doing it the bad way. ;D And yes, I realize the potential for clusterfuck caused by mods overwriting classes they have no business messing with, but as said earlier, the main goal here is learning; this is not a mod I intend to release, at least not until it's done the proper way.

Regarding ThingComps, I'm not sure what to do with the one I've made. I see pawns have a public List<ThingComp> that they inherit from their base class, which includes all the comps bound to them, and I assume this is how I access each individual pawn's ration setting that's stored in the ThingComp. But, not quite sure how to add my own comp to the list without detouring anything; perhaps via XML? But there doesn't seem to be any XML def that corresponds to Verse.Pawn, where I could bind the comp. In any case, I've only looked at it briefly so far, so I'm probably missing something major.
Title: Re: Adding a field to an existing core class
Post by: 1000101 on August 24, 2016, 04:26:46 AM
CCL has a ThingComp injector which you can use to inject your comp into the pawns.

From code you just need to use pawn.TryGetComp<MyCompClass>()
Title: Re: Adding a field to an existing core class
Post by: Lockdown on August 25, 2016, 05:26:55 PM
Quote from: 1000101 on August 24, 2016, 04:26:46 AM
CCL has a ThingComp injector which you can use to inject your comp into the pawns.

From code you just need to use pawn.TryGetComp<MyCompClass>()

Thanks! I got the mod to work with a CCL-injected ThingComp, but only for humans so far. Seeing as I want pets to also have their food restricted by the selector, I thought of injecting the ThingComp into BasePawn instead of Human, so everything is covered. But I get an error (http://i.imgur.com/gArnnK2.png) if I do that. I'm guessing this might be because BasePawn is an abstract def, so is there any way to get around it or do I have to inject the thingComp into Human and every single non-abstract animal def?

This is the ModHelper.xml, if needed:
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<CommunityCoreLibrary.ModHelperDef>
<defName>RationControl</defName>
<ModName>RationControl</ModName>
<minCCLversion>0.14.0</minCCLversion>
<SpecialInjectors>
<li>RationControl.DetourInjector</li>
</SpecialInjectors>
<ThingComps>
<li>
<compProps>
<compClass>RationControl.RationControlThingComp</compClass>
</compProps>
<targetDefs>
<li>BasePawn</li>
</targetDefs>
</li>
</ThingComps>
</CommunityCoreLibrary.ModHelperDef>
</Defs>
Title: Re: Adding a field to an existing core class
Post by: 1000101 on August 25, 2016, 08:43:49 PM
That's because "BasePawn" isn't a def, it's an abstract which means that all the field values in it are copied into the defs which reference it as a parent before reading the values set in the def itself.  Further, once the xml is loaded the abstracts are discarded.  See this post (https://ludeon.com/forums/index.php?topic=19499.0) regarding xml and abstracts.
Title: Re: Adding a field to an existing core class
Post by: CallMeDio on August 25, 2016, 09:17:53 PM
In my opinion that post you linked should be a stick in the mod help forum,  I have a mod(unreleased) where I didn't defined the abstract, it works perfectly and don't cause problems to others but I will check it out for sure when I have time. Thanks 1000101
Title: Re: Adding a field to an existing core class
Post by: Lockdown on August 25, 2016, 09:52:49 PM
Well, bummer. So as suspected, I'll have to inject the thingComp into all the different animal defs, and if used together with another mod that adds more wildlife, a patch would be needed to include them.

Quote from: CallMeDio on August 25, 2016, 09:17:53 PM
In my opinion that post you linked should be a stick in the mod help forum,  I have a mod(unreleased) where I didn't defined the abstract, it works perfectly and don't cause problems to others but I will check it out for sure when I have time. Thanks 1000101

Yeah, same here. I made a couple XML mods for myself before I started tinkering with C#, and I had no idea it worked like that. I had assumed it was like Bethesda games where you can reference stuff from the base ESM instead of having to redefine it.
Title: Re: Adding a field to an existing core class
Post by: 1000101 on August 25, 2016, 10:25:16 PM
Quote from: Lockdown on August 25, 2016, 09:52:49 PMWell, bummer. So as suspected, I'll have to inject the thingComp into all the different animal defs, and if used together with another mod that adds more wildlife, a patch would be needed to include them.

Well, you could use a DefInjectionQualifier (https://github.com/RimWorldCCLTeam/CommunityCoreLibrary/blob/master/DLL_Project/Classes/Abstract/DefInjectionQualifier.cs) which means you don't (and in fact can't) specify the ThingDefs specifically.  You just need to implement your Test() method and return true if it's a def you want to inject into.

See the Mod Tweaks (https://github.com/RimWorldCCLTeam/CommunityCoreLibrary/tree/master/DLL_Project/CCLModTweaks/DefInjectionQualifiers) for a specific example.
Title: Re: Adding a field to an existing core class
Post by: Lockdown on August 25, 2016, 11:06:22 PM
Wow, CCL is such a Swiss army knife, I love it. :D