Ludeon Forums

RimWorld => Mods => Help => Topic started by: JaxelT on August 16, 2016, 05:05:48 PM

Title: [Solved] How to override ThoughtUtility?
Post by: JaxelT on August 16, 2016, 05:05:48 PM
Hello all, this is my first mod, and I'm trying to add a new thought. It's supposed to trigger when a colonist kills a humanlike pawn who's hostile to the colony. However, I can't get the game to actually run the code that triggers it. I tried making my own ThoughtUtility, and it quickly became clear that it wasn't paying any attention to it. So then I tried overriding ThoughtUtility, and that didn't seem to do anything either. Here's my code. Anyone have any advice?


public static void GiveThoughtsForPawnDied(Pawn victim, DamageInfo? dinfo, Hediff hediff)
        {
            if (PawnGenerator.IsBeingGenerated(victim) || Current.ProgramState != ProgramState.MapPlaying)
                return;
            bool flag1 = dinfo.HasValue && dinfo.Value.Def == DamageDefOf.ExecutionCut || hediff != null && (hediff.def == HediffDefOf.Euthanasia || hediff.def == HediffDefOf.ShutDown);
            bool flag2 = victim.IsPrisonerOfColony && !PrisonBreakUtility.IsPrisonBreaking(victim) && !victim.InAggroMentalState;
            if (!victim.RaceProps.Humanlike)
                return;
            if (dinfo.HasValue && dinfo.Value.Def.externalViolence && dinfo.Value.Instigator != null)
            {
                Pawn pawn = dinfo.Value.Instigator as Pawn;
                if (pawn != null && !pawn.Dead && (pawn.needs.mood != null && pawn.story != null))
                {
                    pawn.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOf.KilledHumanlikeBloodlust, (Pawn)null);
                    if (victim.Faction.HostileTo(pawn.Faction) && !flag1 && !flag2)
                        pawn.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOfAdrenaline.KilledHumanlikeEnemy, (Pawn)null);
                }
            }
            List<Pawn> allPawnsSpawned = Find.MapPawns.AllPawnsSpawned;
            for (int index = 0; index < allPawnsSpawned.Count; ++index)
            {
                Pawn p = allPawnsSpawned[index];
                if (p.needs.mood != null && !flag1 && (p.MentalStateDef != MentalStateDefOf.SocialFighting || ((MentalState_SocialFighting)p.MentalState).otherPawn != victim))
                {
                    if (victim.Spawned && (p.Position.InHorDistOf(victim.Position, 12f) && GenSight.LineOfSight(victim.Position, p.Position, false) && (p.Awake() && p.health.capacities.CapableOf(PawnCapacityDefOf.Sight))))
                    {
                        if (p.Faction == victim.Faction)
                            p.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOf.WitnessedDeathAlly, (Pawn)null);
                        //else
                        //    p.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOf.WitnessedDeathNonAlly, (Pawn)null);
                        if (p.relations.FamilyByBlood.Contains<Pawn>(victim))
                            p.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOf.WitnessedDeathFamily, (Pawn)null);
                        p.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOf.WitnessedDeathBloodlust, (Pawn)null);
                    }
                    else if (victim.Faction == Faction.OfPlayer && victim.Faction == p.Faction && victim.HostFaction != p.Faction)
                        p.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOf.KnowColonistDied, (Pawn)null);
                    if (flag2 && p.Faction == Faction.OfPlayer && !p.IsPrisoner)
                        p.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOf.KnowPrisonerDiedInnocent, (Pawn)null);
                }
            }
        }



namespace AdrenalineRush
{
    [DefOf]
    public static class ThoughtDefOfAdrenaline
    {
        public static ThoughtDef KilledHumanlikeEnemy;
    }
}
Title: Re: How to override ThoughtUtility?
Post by: RawCode on August 16, 2016, 09:56:28 PM
In C# you may override only methods with "override" modifier, static methods cannot have such modifier.
You may need to read MSDN article about method modifiers to understand system fully.

Only way is using hooks, most "novice" friendly implementation is shipped with CCL modification, that can be found in "released mods" branch of forum.
Title: Re: How to override ThoughtUtility?
Post by: JaxelT on August 17, 2016, 06:37:43 AM
Quote from: RawCode on August 16, 2016, 09:56:28 PM
In C# you may override only methods with "override" modifier, static methods cannot have such modifier.
You may need to read MSDN article about method modifiers to understand system fully.

Only way is using hooks, most "novice" friendly implementation is shipped with CCL modification, that can be found in "released mods" branch of forum.

I know how to override and that you cannot do it on static classes. Visual Studio said as much. When I said I overrode ThoughtUtility, I meant I replaced it with my own version in the DLL, but RimWorld just seemed to ignore it.

So I have to use CCL for my purposes? Well, that's dismaying. A new Thought seems like it should be a very simple and basic thing to implement. Still, thanks for your help! I'll look into it.

e: Ah, I see, you're simply pointing me to CCL as an example of using hooks.
Title: Re: How to override ThoughtUtility?
Post by: JaxelT on August 17, 2016, 07:59:49 AM
OK, I have looked into the code for detour injecting in CCL and tried to adapt it to my own needs. It's a very confusing mess of C# and I have no idea what's actually necessary and what's not to inject something and where the spiderweb of dependencies ends. I've almost wholesale copied more than 3x as many classes as were in my mod from CCL to try and get it to inject my classes. Detours, DetourInjector, IInjector, SpecialInjector, MHD_SpecialInjectors, Data, ModHelperDef... Can someone who's already done this please lay out what specifically I have to do to get this to work? Because sifting through this undocumented, uncommented maze of programming definitely feels like more trouble than it's worth to try to get one simple Thought to trigger in the game.

e: After looking around the forums a bit and finding an example in another mod, I figured out how to use CCL to hook detours into the game. However, when I run it I get an error (a step in the right direction) and it tells me 'Cannot cast from source type to destination type.' Here's the code:


namespace AdrenalineRush.Detour
{
    internal static class _ThoughtUtility
    {
        internal static void _GiveThoughtsForPawnDied(Pawn victim, DamageInfo? dinfo, Hediff hediff)
        {
            if (PawnGenerator.IsBeingGenerated(victim) || Current.ProgramState != ProgramState.MapPlaying)
                return;
            bool flag1 = dinfo.HasValue && dinfo.Value.Def == DamageDefOf.ExecutionCut || hediff != null && (hediff.def == HediffDefOf.Euthanasia || hediff.def == HediffDefOf.ShutDown);
            bool flag2 = victim.IsPrisonerOfColony && !PrisonBreakUtility.IsPrisonBreaking(victim) && !victim.InAggroMentalState;
            if (!victim.RaceProps.Humanlike)
                return;
            if (dinfo.HasValue && dinfo.Value.Def.externalViolence && dinfo.Value.Instigator != null)
            {
                Pawn pawn = dinfo.Value.Instigator as Pawn;
                if (pawn != null && !pawn.Dead && (pawn.needs.mood != null && pawn.story != null))
                {
                    pawn.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOf.KilledHumanlikeBloodlust, (Pawn)null);
                    if (victim.Faction.HostileTo(pawn.Faction) && !flag1 && !flag2)
                        pawn.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOfAdrenaline.KilledHumanlikeEnemy, (Pawn)null);
                }
            }
            List<Pawn> allPawnsSpawned = Find.MapPawns.AllPawnsSpawned;
            for (int index = 0; index < allPawnsSpawned.Count; ++index)
            {
                Pawn p = allPawnsSpawned[index];
                if (p.needs.mood != null && !flag1 && (p.MentalStateDef != MentalStateDefOf.SocialFighting || ((MentalState_SocialFighting)p.MentalState).otherPawn != victim))
                {
                    if (victim.Spawned && (p.Position.InHorDistOf(victim.Position, 12f) && GenSight.LineOfSight(victim.Position, p.Position, false) && (p.Awake() && p.health.capacities.CapableOf(PawnCapacityDefOf.Sight))))
                    {
                        if (p.Faction == victim.Faction)
                            p.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOf.WitnessedDeathAlly, (Pawn)null);
                        //else
                        //    p.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOf.WitnessedDeathNonAlly, (Pawn)null);
                        if (p.relations.FamilyByBlood.Contains<Pawn>(victim))
                            p.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOf.WitnessedDeathFamily, (Pawn)null);
                        p.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOf.WitnessedDeathBloodlust, (Pawn)null);
                    }
                    else if (victim.Faction == Faction.OfPlayer && victim.Faction == p.Faction && victim.HostFaction != p.Faction)
                        p.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOf.KnowColonistDied, (Pawn)null);
                    if (flag2 && p.Faction == Faction.OfPlayer && !p.IsPrisoner)
                        p.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOf.KnowPrisonerDiedInnocent, (Pawn)null);
                }
            }
        }
    }
}



using CommunityCoreLibrary;
using CommunityCoreLibrary.Controller;
using CommunityCoreLibrary.Detour;
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Verse;

namespace AdrenalineRush
{
    public class DetourInjector : SpecialInjector
    {
        public override bool Inject()
        {
            MethodInfo method1 = typeof(ThoughtUtility).GetMethod("GiveThoughtsForPawnDied", BindingFlags.Static | BindingFlags.Public);
            MethodInfo method2 = typeof(Detour._ThoughtUtility).GetMethod("_GiveThoughtsForPawnDied", BindingFlags.Static | BindingFlags.NonPublic);
            if (!Detours.TryDetourFromTo(method1, method2))
            {
                return false;
            }
            return true;
        }
    }
}


e2: I did it! It works! Thanks, RawCode & skullywag.