Making InspirationHandler less random

Started by Ian_Suffix, March 06, 2018, 05:51:28 AM

Previous topic - Next topic

Ian_Suffix

I'm using Psychology as a springboard into making my first mod: a custom non-random Inspiration. Unfortunately, I've hit a roadblock: unlike the workers and handlers behind mental states or thoughts, the methods of InspirationHandler are non-virtual. CheckStartRandomInspiration() is even private. I assume this means that all inspirations, regardless of their effects, are triggered at random, and if they can jump a few hurdles they'll become a reality.

Now, if Psychology can override the default pawn class, I imagine it should be possible to override the entire InspirationHandler class and replace it with my own.

Is this where the Harmony library steps in? I've been at this for a while now, and I'd at least like to have the reassurance that I'm on the right track.

jamaicancastle

This is definitely a Harmony-able problem. The way Harmony works is that you make patches on existing methods - prefixes run before the method, postfixes run after, and transpilers actually modify the method itself as it's being run. Depending on the structure of the code, you could tinker with the checks that it does run on inspirations, or you could detour the whole start-random-inspiration function (an ugly approach but it will work).

Ian_Suffix

Thanks for the explanation! Now the contents of Psychology's Harmony namespace are becoming clear to me.

At the moment, I'm thinking of postfixing InspirationHandlerTick with a "CheckStartConditionalInspiration" method. If a custom cooldown is met (being static, would a patch class share one cooldown between all pawns?), it should TryStartInspiration with the first custom Inspiration whose pawn conditions are met (isFighting, something in backstory). I'll have to dig through Psychology some more to find out how I can serialize those conditions, as InspirationDef's requiredSkills, requiredCapacities, and requiredNonDisabledStats aren't quite enough.

Sorry to ramble through my entire thought process; I'm keeping track of my findings here.

jamaicancastle

You're right in that a static class (which patches have to be) can't have instanced members. There are three basic approaches to this issue:
- Use something that already exists on the pawn, in its mindstate or somewhere similar (I imagine the inspiration handler already has a refire time that you could piggyback off of).
- Add a comp to the pawn, which will allow you to store arbitrary values and/or code on a pawn-by-pawn basis.
- Or keep track of the pawns in a static dictionary - a list of pawns matched by a list of cooldown times or whatever other information you need.

Ian_Suffix

New idea: since I'm currently only interested in making one custom inspiration that starts up when a type of pawn is drafted, I can TryStartInspiration out of a Pawn_DraftController.Drafted postfix. First, I'll try to just fire off a log message when Drafted is set to True.

Ian_Suffix

#5
There's been a complication. TIL: Accessors are not properties, and accessor method names have a get_ set_ prefix.

Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for NinjaMode.NinjaModeInit ---> System.ArgumentException: No target method specified for class NinjaMode.Pawn_DraftController_Drafted_Patch

This, despite the fact I've clearly denoted the Drafted accessor in Pawn_DraftController.

    [HarmonyPatch(typeof(Pawn_DraftController), "Drafted")]
    public static class Pawn_DraftController_Drafted_Patch
    {
        static void Postfix(Pawn_DraftController __instance)
        {
            Log.Message("NinjaMode");
            Messages.Message(
                "NinjaMode_TestMessage".Translate(new object[] { __instance.pawn.NameStringShort }),
                MessageTypeDefOf.PositiveEvent);
        }


Does this mean accessors are off the table as targets of Harmony?

Spdskatr

Quote from: Ian_Suffix on March 08, 2018, 08:23:43 PM
There's been a complication.

Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for NinjaMode.NinjaModeInit ---> System.ArgumentException: No target method specified for class NinjaMode.Pawn_DraftController_Drafted_Patch

This, despite the fact I've clearly denoted the Drafted accessor in Pawn_DraftController.

    [HarmonyPatch(typeof(Pawn_DraftController), "Drafted")]
    public static class Pawn_DraftController_Drafted_Patch
    {
        static void Postfix(Pawn_DraftController __instance)
        {
            Log.Message("NinjaMode");
            Messages.Message(
                "NinjaMode_TestMessage".Translate(new object[] { __instance.pawn.NameStringShort }),
                MessageTypeDefOf.PositiveEvent);
        }


Does this mean accessors are off the table as targets of Harmony?

Try use get_Drafted.

Getters have method signature get_PropertyName. Guess what signature setters have.
My mods

If 666 is evil, does that make 25.8069758011 the root of all evil?

Ian_Suffix

Thanks, it's working now! There's a bit of a delay when a pawn is first drafted, but there's no getting around that, I suppose.