[LIB] Harmony v1.2.0.1

Started by Brrainz, January 13, 2017, 04:59:21 PM

Previous topic - Next topic

LWM

I am 90% sure Harmony is not the first tool you want.  Are these not defined in the XML files anywhere?

...could what you want be defined in XML files?  There are a bunch of ways to attach something to a ThingDef - the wiki has some helpful info on that.

If it's something you want to attach to *all* animals, it gets a little trickier, because you can't change an object definition via Harmony.  One way I see ppl deal with it is to create a GameElement/MapElement/etc that keeps a list (a HashTable, a Dictionary, whatever C# object is most appropriate - don't remember off the top of my head) and registers items to it.  Then use Harmony to add checking that to various methods.

I'm sure there are other approaches.

--LWM


elfstone

Hi,
this is quite awesome, I just started creating mods for rimworld today, and with harmony I was able to achieve my first mod ideas (being able to see when i last visited a settlement for trading).
I released my mod to the steam workshop with Creative Commons License Attribution-ShareAlike 4.0, but since i have to include the 0Harmony.dll, I wonder whether I need to include the MIT-Licence file of Harmony as well..

Does anyone know?

LWM

I prefer to release under the Limited Gnu Public License - it specifically covers a library that works in a larger system (that might be - and in this case is - closed source (i.e., RimWorld)).  I'm happy enough to redistribute the Harmony dll if I need to.  I suppose it's worth mentioning "Harmony is released under an MIT license"?

Nationality

#288
Hi, I'm trying to make a patch that works with the mod Psychology. Maybe someone can tell me what's going on? The Psychology code *always* runs after my own, no matter what I do.
    [HarmonyPatch(typeof(InteractionWorker_RomanceAttempt), nameof(InteractionWorker_RomanceAttempt.RandomSelectionWeight))]
    public static class InteractionWorker_RomanceAttempt_SelectionWeightPatch
    {
        [HarmonyPriority(Priority.VeryLow)]
        [HarmonyPostfix]
        public static void PsychologyException(ref float __result, Pawn initiator, Pawn recipient)
        {
            //Don't hit on people in mental breaks... unless you're really freaky.
            if (recipient.InMentalState && PsycheHelper.PsychologyEnabled(initiator) && PsycheHelper.Comp(initiator).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Experimental) < 0.8f)
            {
                __result = 0f;
                return;
            }
            //Pawns won't hit on their spouses.
            if (LovePartnerRelationUtility.LovePartnerRelationExists(initiator, recipient))
            {
                __result = 0f;
                return;
            }
            //Codependents won't romance anyone if they are in a relationship
            if (LovePartnerRelationUtility.HasAnyLovePartner(initiator) && initiator.story.traits.HasTrait(TraitDefOfPsychology.Codependent))
            {
                __result = 0f;
                return;
            }
            //Only lechers will romance someone that has less than +5 opinion of them
            if (recipient.relations.OpinionOf(initiator) < 5 && !initiator.story.traits.HasTrait(TraitDefOfPsychology.Lecher))
            {
                __result = 0f;
                return;
            }
            float attractiveness = initiator.relations.SecondaryRomanceChanceFactor(recipient);
            int opinion = initiator.relations.OpinionOf(recipient);
            float romanceChance = 1.15f;
            if (!PsycheHelper.PsychologyEnabled(initiator))
            {
                //Vanilla: Straight women are 15% as likely to romance anyone.
                romanceChance = (!initiator.story.traits.HasTrait(TraitDefOf.Gay)) ? ((initiator.gender != Gender.Female) ? romanceChance : romanceChance * 0.15f) : romanceChance;
            }
            else
            {
                //Psychology: A pawn's likelihood to romance is based on how Aggressive and Romantic they are.
                float personalityFactor = Mathf.Pow(20f, PsycheHelper.Comp(initiator).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Aggressive)) * Mathf.Pow(12f, (1f - PsycheHelper.Comp(initiator).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Romantic)));
                romanceChance = personalityFactor * 0.02f;
            }
            //A pawn with +50 or more opinion of their lover will not hit on other pawns unless they are lecherous or polygamous (and their lover is also polygamous).
            float existingLovePartnerFactor = 1f;
            Pawn pawn = LovePartnerRelationUtility.ExistingMostLikedLovePartner(initiator, false);
            if (pawn != null && !initiator.story.traits.HasTrait(TraitDefOfPsychology.Lecher) && (!initiator.story.traits.HasTrait(TraitDefOfPsychology.Polygamous) && !pawn.story.traits.HasTrait(TraitDefOfPsychology.Polygamous)))
            {
                float value = (float)initiator.relations.OpinionOf(pawn);
                existingLovePartnerFactor = Mathf.InverseLerp(50f, -50f, value);
            }
            float attractivenessFactor = Mathf.InverseLerp(0.25f, 1f, attractiveness);
            float opinionFactor = Mathf.InverseLerp(-5f, 100f, (float)opinion)*2f;
            //People who have hit on someone in the past and been rejected because of their sexuality will rarely attempt to hit on them again.
            float knownSexualityFactor = (PsycheHelper.PsychologyEnabled(initiator) && PsychologyBase.ActivateKinsey() && PsycheHelper.Comp(initiator).Sexuality.IncompatibleSexualityKnown(recipient) && !initiator.story.traits.HasTrait(TraitDefOfPsychology.Lecher)) ? 0.05f : (PsycheHelper.PsychologyEnabled(initiator) ? (initiator.gender == recipient.gender ? (initiator.story.traits.HasTrait(TraitDefOf.Gay) && recipient.story.traits.HasTrait(TraitDefOf.Gay) ? 1f : 0.15f) : (!initiator.story.traits.HasTrait(TraitDefOf.Gay) && !recipient.story.traits.HasTrait(TraitDefOf.Gay) ? 1f : 0.15f)) : 1f);
            //Only lechers will try to romance someone in a stable relationship.
            float recipientLovePartnerFactor = 1f;
            Pawn pawn2 = LovePartnerRelationUtility.ExistingMostLikedLovePartner(recipient, false);
            if (pawn2 != null && !initiator.story.traits.HasTrait(TraitDefOfPsychology.Lecher))
            {
                int value = recipient.relations.OpinionOf(pawn2);
                recipientLovePartnerFactor = Mathf.InverseLerp(5f, -100f, (float)value);
            }
           
            __result = romanceChance * existingLovePartnerFactor * attractivenessFactor * opinionFactor * knownSexualityFactor * recipientLovePartnerFactor;
            Log.Message("Psychology: " + __result.ToString());
        }
    }


Here is my own code I want to run after, with the intention of using the _result produced by Psychology.

[HarmonyPatch(typeof(InteractionWorker_RomanceAttempt), nameof(InteractionWorker_RomanceAttempt.RandomSelectionWeight))]
    [HarmonyPriority(Priority.Last)]
    public static class GRSelectionWeightPatch
    {
        [HarmonyPostfix]
        public static void GradualRomanceFlirtWeight(ref float __result, Pawn initiator, Pawn recipient)
        {
            float flirtFactor = 0.5f;
            int numberOfGoodFlirts = 0;
            int numberOfBadFlirts = 0;
            List<Thought_Memory> memoryList = initiator.needs.mood.thoughts.memories.Memories;

            for (int i = 0; i < memoryList.Count; i++)
            {
                Thought_Memory curMemory = memoryList[i];
                if (curMemory.def == ThoughtDefOfGR.RomanticInterest && curMemory.otherPawn == recipient)
                {
                    numberOfGoodFlirts++;
                }
                else if (curMemory.def == ThoughtDefOfGR.RomanticDisinterest && curMemory.otherPawn == recipient)
                {
                    numberOfBadFlirts++;
                }
            }

            flirtFactor += (numberOfGoodFlirts * goodFlirtBonus);
            flirtFactor += (numberOfBadFlirts * badFlirtPenalty);
            flirtFactor = Mathf.Max(flirtFactor, 0f);
            float newChance = __result * flirtFactor;
            Log.Message(initiator.Name + "flirts with " + recipient.Name + ". FlirtFactor is " + flirtFactor.ToString() + ". Good flirts: " + numberOfGoodFlirts.ToString() + ". Bad flirts: " + numberOfBadFlirts.ToString() + ". Predicted chance: " + newChance.ToString());

            __result = newChance;

        }
        private const float goodFlirtBonus = 1000f;
        private const float badFlirtPenalty = 0.5f;

    }


When I check the log, I always, ALWAYS get the Psychology log second, no matter what I set the priority. It's an extremely frustrating issue, totally mystifying, and I'm not sure what I can do.

EDIT: I'm using Hugslib, if that's important.

Nationality

Never mind, figured it out. [HarmonyPriority(Priority.Last)] was in the wrong location. I spent the better part of a day looking for this bug! Lessons learned.

LWM


Kiame

#291
Quote from: LWM on November 09, 2019, 08:50:26 PM
Where SHOULD it have been?

Patch order like HarmonyPriority(Priority.Last) goes on the patching method not the patching class.


    public static class GRSelectionWeightPatch
    {
        [HarmonyPostfix]
        [HarmonyPriority(Priority.Last)]
        public static void GradualRomanceFlirtWeight(ref float __result, Pawn initiator, Pawn recipient)
        {


This means when Harmony orders the patch order the above patch on the method GradualRomanceFlirtWeight will go last in the postfix load order.

AlexandrosPrice

Alright, so I'm having some trouble with a Harmony mod.

using RimWorld;
using Harmony;
using Verse.AI.Group;
using System.Reflection;
using System.Linq;

namespace RimGetReady
{
    public class RimGetReadyMain
    {
        public static void DoRimGetReadyMain()
        {

            HarmonyInstance.Create("com.Steam.RimWorld.Mod.RimGetReady").PatchAll(Assembly.GetExecutingAssembly());

        }
   
    }
    [HarmonyPatch(typeof(LordJob_DefendBase))]
    [HarmonyPatch("CreateGraph")]
    public class LordJob_DefendBase_Patch
    {
        public static void Postfix(ref StateGraph __result)
        {
            Transition trans = __result.transitions.Last();
            trans.triggers.Clear();
            trans.AddTrigger(new Trigger_TicksPassed(GenDate.TicksPerDay * 5));
        }
    }
}

I'm very new to C# and modding. I've been getting help from the forum and the Rimworld Discord. But I've run into a problem. When I test this in game it doesn't allow me to select world tiles unless they are occupied and zoomed out, it creates duplicate side-tabs(ex: Low Food), and it doesn't do what the code says to do. Anyone know what I'm doing wrong? I was testing in a clean environment, so there shouldn't be any conflicts.

DaStormDragon

Trying to make a patch that prevents ImmuneToAge races from suffering the reduced Lovin' chance from age, but I get: (IDE0051   Private member 'JobDriver_Lovin_GenerateRandomMinTicksToNextLovin.Postfix' is unused.) and the patch doesn't work.

using RimWorld;
using HarmonyLib;
using AlienRace;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using Verse;
using Verse.AI;

namespace Archai
{
    [StaticConstructorOnStartup]
    static class HarmonyPatches
    {
        static HarmonyPatches()
        {
            Harmony.DEBUG = true;
            var harmony = new Harmony("rimworld.dsd.archai");
            harmony.PatchAll(Assembly.GetExecutingAssembly());
            Log.Message("Test");
        }
    }

    [HarmonyPatch(typeof(JobDriver_Lovin), "GenerateRandomMinTicksToNextLovin")]
    public class JobDriver_Lovin_GenerateRandomMinTicksToNextLovin
    {
        static void Postfix(Pawn pawn, ref int __result)
        {
            float centerY = 1.5f;
            centerY = Rand.Gaussian(centerY, 0.3f);
            if (centerY < 0.5f)
            {
                centerY = 0.5f;
            }
            ThingDef_AlienRace thingDef_AlienRace = ((Thing)pawn).def as ThingDef_AlienRace;
            if (thingDef_AlienRace != null && thingDef_AlienRace.alienRace.generalSettings.immuneToAge)
            {
                __result = (int)(centerY * 2500f);
            }
        }
    }
}

LWM

You get that...when you build?  If so, you can ignore it.

Put a Log.Message() into your Postfix() and check for the message?  You should probably check the Harmony log to make sure it thinks you're patching what you think you are.

DaStormDragon

#295
Yep. Checked the log, right place.

float centerY = 1.5f;
            centerY = Rand.Gaussian(centerY, 0.3f);
            if (centerY < 0.5f)
            {
                centerY = 0.5f;
            }
            ThingDef_AlienRace thingDef_AlienRace = ((Thing)pawn).def as ThingDef_AlienRace;
            if (thingDef_AlienRace != null && thingDef_AlienRace.alienRace.generalSettings.immuneToAge)
            {
                __result = (int)(centerY * 2500f);
                Log.Message("Debug: Age curve overridden");
            }
            Log.Message("Debug: Age curve not overridden");

Tried Log.Message, showed up as not overridden when two humans did it but no go for the mod.

LWM

"no go" - nothing showed up in the log?

DaStormDragon

#297
I get the message that it isn't overridden when it's two humans, but the mod doesn't affect the alien races I want it to

DaStormDragon

#298
Dammit. Apparently I need to modify a bunch of other things aswell. Sorry.

DaStormDragon

Me again, sorry.
Tried this:

static void Postfix(Pawn otherPawn, Pawn pawn, ref float __result)
        {
            if (!LoadedModManager.RunningModsListForReading.Any(x => x.Name == "[RF] Rational Romance (Continued)"))
            {
                if (pawn.def is ThingDef_AlienRace alienProps && alienProps.alienRace.generalSettings.immuneToAge && pawn.ageTracker.AgeBiologicalYearsFloat >= 18 && otherPawn.ageTracker.AgeBiologicalYearsFloat >= 18)
                {
                    if (pawn.def != otherPawn.def || pawn == otherPawn)
                    {
                        __result = 0f;
                    }
                    if (pawn.story != null && pawn.story.traits != null)
                    {
                        if (pawn.story.traits.HasTrait(TraitDefOf.Asexual))
                        {
                            __result = 0f;
                        }
                        if (!pawn.story.traits.HasTrait(TraitDefOf.Bisexual))
                        {
                            if (pawn.story.traits.HasTrait(TraitDefOf.Gay))
                            {
                                if (otherPawn.gender != pawn.gender)
                                {
                                    __result = 0f;
                                }
                            }
                            else if (otherPawn.gender == pawn.gender)
                            {
                                __result = 0f;
                            }
                        }
                    }
                    float num = 0.2f;
                    float num4 = 0f;
                    if (otherPawn.RaceProps.Humanlike)
                    {
                        num4 = otherPawn.GetStatValue(StatDefOf.PawnBeauty);
                    }
                    float num5 = 1f;
                    if (num4 < 0f)
                    {
                        num5 = 0.3f;
                    }
                    else if (num4 > 0f)
                    {
                        num5 = 2.3f;
                    }
                    __result = num * num5;
                }
            }
        }

but got this error:

### Patch: System.Single RimWorld.Pawn_RelationsTracker::SecondaryLovinChanceFactor(Verse.Pawn otherPawn)
### Replacement: static System.Single RimWorld.Pawn_RelationsTracker::SecondaryLovinChanceFactor_Patch1(RimWorld.Pawn_RelationsTracker this, Verse.Pawn otherPawn)
### Exception from user "rimworld.dsd.archai", Harmony v2.0.0.8
### Original: System.Single RimWorld.Pawn_RelationsTracker::SecondaryLovinChanceFactor(Verse.Pawn otherPawn)
### Patch class: Archai.Pawn_RelationsTracker_LovinChance
### System.Exception: Parameter "pawn" not found in method System.Single RimWorld.Pawn_RelationsTracker::SecondaryLovinChanceFactor(Verse.Pawn otherPawn)
###   at HarmonyLib.MethodPatcher.EmitCallParameter (System.Reflection.MethodInfo patch, System.Collections.Generic.Dictionary`2[TKey,TValue] variables, System.Boolean allowFirsParamPassthrough) [0x00539] in <2161c8330234450b8141397c32c11571>:0
###   at HarmonyLib.MethodPatcher+<>c__DisplayClass31_0.<AddPrefixes>b__0 (System.Reflection.MethodInfo fix) [0x00069] in <2161c8330234450b8141397c32c11571>:0
###   at HarmonyLib.CollectionExtensions.Do[T] (System.Collections.Generic.IEnumerable`1[T] sequence, System.Action`1[T] action) [0x00014] in <2161c8330234450b8141397c32c11571>:0
###   at HarmonyLib.MethodPatcher.AddPrefixes (System.Collections.Generic.Dictionary`2[TKey,TValue] variables, System.Reflection.Emit.LocalBuilder runOriginalVariable) [0x00033] in <2161c8330234450b8141397c32c11571>:0
###   at HarmonyLib.MethodPatcher.CreateReplacement (System.Collections.Generic.Dictionary`2[System.Int32,HarmonyLib.CodeInstruction]& finalInstructions) [0x0015d] in <2161c8330234450b8141397c32c11571>:0
###   at HarmonyLib.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, HarmonyLib.PatchInfo patchInfo) [0x00057] in <2161c8330234450b8141397c32c11571>:0
###   at HarmonyLib.PatchClassProcessor.ProcessPatchJob (HarmonyLib.PatchJobs`1+Job[T] job) [0x0015d] in <2161c8330234450b8141397c32c11571>:0
### Exception from user "rimworld.dsd.archai", Harmony v2.0.0.8
### Original: System.Single RimWorld.Pawn_RelationsTracker::SecondaryLovinChanceFactor(Verse.Pawn otherPawn)
### Patch class: Archai.Pawn_RelationsTracker_LovinChance
### System.Exception: Parameter "pawn" not found in method System.Single RimWorld.Pawn_RelationsTracker::SecondaryLovinChanceFactor(Verse.Pawn otherPawn)
###   at HarmonyLib.MethodPatcher.EmitCallParameter (System.Reflection.MethodInfo patch, System.Collections.Generic.Dictionary`2[TKey,TValue] variables, System.Boolean allowFirsParamPassthrough) [0x00539] in <2161c8330234450b8141397c32c11571>:0
###   at HarmonyLib.MethodPatcher+<>c__DisplayClass31_0.<AddPrefixes>b__0 (System.Reflection.MethodInfo fix) [0x00069] in <2161c8330234450b8141397c32c11571>:0
###   at HarmonyLib.CollectionExtensions.Do[T] (System.Collections.Generic.IEnumerable`1[T] sequence, System.Action`1[T] action) [0x00014] in <2161c8330234450b8141397c32c11571>:0
###   at HarmonyLib.MethodPatcher.AddPrefixes (System.Collections.Generic.Dictionary`2[TKey,TValue] variables, System.Reflection.Emit.LocalBuilder runOriginalVariable) [0x00033] in <2161c8330234450b8141397c32c11571>:0
###   at HarmonyLib.MethodPatcher.CreateReplacement (System.Collections.Generic.Dictionary`2[System.Int32,HarmonyLib.CodeInstruction]& finalInstructions) [0x0015d] in <2161c8330234450b8141397c32c11571>:0
###   at HarmonyLib.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, HarmonyLib.PatchInfo patchInfo) [0x00057] in <2161c8330234450b8141397c32c11571>:0
###   at HarmonyLib.PatchClassProcessor.ProcessPatchJob (HarmonyLib.PatchJobs`1+Job[T] job) [0x0015d] in <2161c8330234450b8141397c32c11571>:0