Adding a field to an existing core class

Started by Lockdown, August 23, 2016, 12:45:12 PM

Previous topic - Next topic

Lockdown

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?

1000101

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.
(2*b)||!(2*b) - That is the question.
There are 10 kinds of people in this world - those that understand binary and those that don't.

Powered By

Lockdown

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?

JaxelT

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.

1000101

Not sure what happened to your reply about ThingComps, but that is the best solution.
(2*b)||!(2*b) - That is the question.
There are 10 kinds of people in this world - those that understand binary and those that don't.

Powered By

Lockdown

#5
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.

1000101

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.
(2*b)||!(2*b) - That is the question.
There are 10 kinds of people in this world - those that understand binary and those that don't.

Powered By

Lockdown

#7
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.

1000101

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>()
(2*b)||!(2*b) - That is the question.
There are 10 kinds of people in this world - those that understand binary and those that don't.

Powered By

Lockdown

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 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>

1000101

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 regarding xml and abstracts.
(2*b)||!(2*b) - That is the question.
There are 10 kinds of people in this world - those that understand binary and those that don't.

Powered By

CallMeDio

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
QuoteYou may need a rubber duck.  Also, try some caveman debugging.

Released mods: No Mood Loss on Prisoner Sold or Died

Lockdown

#12
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.

1000101

#13
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 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 for a specific example.
(2*b)||!(2*b) - That is the question.
There are 10 kinds of people in this world - those that understand binary and those that don't.

Powered By

Lockdown

Wow, CCL is such a Swiss army knife, I love it. :D