Ejemplo n.º 1
0
        public WorkTab(ModContentPack mod)
        {
            // Changing priorities
            var type = AccessTools.TypeByName("WorkTab.PriorityManager");

            MP.RegisterSyncMethod(AccessTools.PropertySetter(type, "ShowPriorities"));

            type = AccessTools.TypeByName("WorkTab.Pawn_Extensions");
            MP.RegisterSyncMethod(AccessTools.Method(type, "SetPriority", new[] { typeof(Pawn), typeof(WorkTypeDef), typeof(int), typeof(List <int>) }));
            MP.RegisterSyncMethod(AccessTools.Method(type, "SetPriority", new[] { typeof(Pawn), typeof(WorkTypeDef), typeof(int), typeof(int), typeof(bool) }));
            MP.RegisterSyncMethod(AccessTools.Method(type, "SetPriority", new[] { typeof(Pawn), typeof(WorkGiverDef), typeof(int), typeof(List <int>) }));
            MP.RegisterSyncMethod(AccessTools.Method(type, "SetPriority", new[] { typeof(Pawn), typeof(WorkGiverDef), typeof(int), typeof(int), typeof(bool) }));
            // This one not needed as it calls SetPriority, but it'll
            // end up calling it numerous times - let's just do it in one command.
            MP.RegisterSyncMethod(type, "DisableAll");

            // Technically we don't have to do this, as pasting calls SetPriority...
            // But well, it ends up being called almost 2000 times in vanilla with DLCs alone...
            // So I felt like it'll be smarter to sync it as a single command instead of potentially
            // couple thousand with mods.
            type = AccessTools.TypeByName("WorkTab.PawnColumnWorker_CopyPasteDetailedWorkPriorities");
            copyPasteColumnWorkerConstructor = AccessTools.DeclaredConstructor(type);
            clipboardField = AccessTools.StaticFieldRefAccess <Dictionary <WorkGiverDef, int[]> >(AccessTools.Field(type, "clipboard"));
            var method = AccessTools.Method(type, "PasteTo");

            pasteToMethod = AccessTools.MethodDelegate <PasteTo>(method);
            MpCompat.harmony.Patch(method,
                                   prefix: new HarmonyMethod(typeof(WorkTab), nameof(PrePasteTo)));

            MP.RegisterSyncMethod(typeof(WorkTab), nameof(SyncedPasteTo));

            // We don't really need to sync those, but not doing so will end up with
            // a bunch of unnecessary synced calls (one per colonists)
            // Sadly, there isn't a call like that in case of checkbox priorities - only numeric ones
            var types = new (string typeName, Type parameterType)[]
Ejemplo n.º 2
0
 /*********
 ** Public methods
 *********/
 public static ConstructorInfo DeclaredConstructor(Type type, Type[] parameters = null)
 {
     // Harmony 1.x matched both static and instance constructors
     return
         (AccessTools.DeclaredConstructor(type, parameters, searchForStatic: false)
          ?? AccessTools.DeclaredConstructor(type, parameters, searchForStatic: true));
 }
Ejemplo n.º 3
0
        public static void LatePatch()
        {
            // Ancient PD turret - toggle aiming at drop pods, enemies, explosive projectiles
            MpCompat.RegisterLambdaMethod("VFEAncients.Building_TurretPD", "GetGizmos", 1, 3, 5);

            var type = AccessTools.TypeByName("VFEAncients.CompGeneTailoringPod");

            // Start gene tailoring operation (after danger warning confirmation)
            MP.RegisterSyncMethod(type, "StartOperation");
            // Cancel operation (before starting it)
            MpCompat.RegisterLambdaMethod(type, "CompGetGizmosExtra", 8);

            // (Dev) instant success/failure
            MpCompat.RegisterLambdaMethod(type, "CompGetGizmosExtra", 9, 10).SetDebugOnly();
            // (Dev) instant finish, random result not synced, as it calls CompleteOperation
            // would cause a tiny conflict, not worth bothering with it
            // (I think it would need to be done without SetDebugOnly, or it would cause issues)

            choosePowerDialogType = AccessTools.TypeByName("VFEAncients.Dialog_ChoosePowers");
            var powerDefType = AccessTools.TypeByName("VFEAncients.PowerDef");
            var tupleType    = typeof(Tuple <,>).MakeGenericType(powerDefType, powerDefType);

            onChosen = CompileCallOnChosen(powerDefType, tupleType);
            MP.RegisterSyncMethod(typeof(VanillaFactionsAncients), nameof(SyncedChoosePower));
            MP.RegisterSyncWorker <Window>(SyncDialogChoosePower, choosePowerDialogType);
            MpCompat.harmony.Patch(AccessTools.DeclaredConstructor(choosePowerDialogType, new[] { typeof(List <>).MakeGenericType(tupleType), typeof(Pawn), typeof(Action <>).MakeGenericType(tupleType) }),
                                   postfix: new HarmonyMethod(typeof(VanillaFactionsAncients), nameof(PostDialogConstructor)));
            MpCompat.harmony.Patch(AccessTools.Method(choosePowerDialogType, nameof(Window.DoWindowContents)),
                                   transpiler: new HarmonyMethod(typeof(VanillaFactionsAncients), nameof(ReplaceButtons)));
        }
Ejemplo n.º 4
0
        private static void EmitConvertArgumentToManaged(ILGenerator il, Type managedParamType, out LocalBuilder variable)
        {
            variable = null;

            if (managedParamType.IsValueType)             // don't need to convert blittable types
            {
                return;
            }

            void EmitCreateIl2CppObject()
            {
                Label endLabel     = il.DefineLabel();
                Label notNullLabel = il.DefineLabel();

                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Brtrue_S, notNullLabel);

                il.Emit(OpCodes.Pop);
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Br_S, endLabel);

                il.MarkLabel(notNullLabel);
                il.Emit(OpCodes.Newobj, AccessTools.DeclaredConstructor(managedParamType, new[] { typeof(IntPtr) }));

                il.MarkLabel(endLabel);
            }

            void HandleTypeConversion(Type originalType)
            {
                if (originalType == typeof(string))
                {
                    il.Emit(OpCodes.Call, IL2CPPToManagedStringMethodInfo);
                }
                else if (originalType.IsSubclassOf(typeof(Il2CppObjectBase)))
                {
                    EmitCreateIl2CppObject();
                }
            }

            if (managedParamType.IsByRef)
            {
                Type directType = managedParamType.GetElementType();

                variable = il.DeclareLocal(directType);

                il.Emit(OpCodes.Ldind_I);

                HandleTypeConversion(directType);

                il.Emit(OpCodes.Stloc, variable);
                il.Emit(OpCodes.Ldloca, variable);
            }
            else
            {
                HandleTypeConversion(managedParamType);
            }
        }
Ejemplo n.º 5
0
        public MethodBase ParseMethod()
        {
            // Parse & retrieve the base type.
            Type type = ParseType();

            expectToken("::");

            // Parse the method name
            string name = getToken("method name");

            // Parse parameters, if specified.
            Type[] parameters = null;
            if (!end())
            {
                expectToken("(");
                parameters = ParseTypes();
                expectToken(")");
            }

            // Retrieve the method.
            MethodBase method;

            if (name == ".ctor")
            {
                if (parameters == null)
                {
                    // If no parameters are specified, assume there is a unique constructor.
                    var array = type.GetConstructors();
                    if (array.Length != 1)
                    {
                        throw new Exception($"Found {array.Length} matching constructors for \"{sig}\".");
                    }
                    method = array[0];
                }
                else
                {
                    method = AccessTools.DeclaredConstructor(type, parameters);
                }
                if (method == null)
                {
                    throw new Exception($"Failed to find constructor \"{sig}\".");
                }
            }
            else
            {
                method = AccessTools.DeclaredMethod(type, name, parameters);
                if (method == null)
                {
                    throw new Exception($"Failed to find method \"{sig}\".");
                }
            }
            return(method);
        }
Ejemplo n.º 6
0
        public static MethodBase GetOriginalMethod(HarmonyMethod attr)
        {
            if (attr.declaringType == null)
            {
                return(null);
            }

            if (attr.methodType == null)
            {
                attr.methodType = MethodType.Normal;
            }

            switch (attr.methodType)
            {
            case MethodType.Normal:
                if (attr.methodName == null)
                {
                    return(null);
                }

                return(AccessTools.DeclaredMethod(
                           attr.declaringType,
                           attr.methodName,
                           attr.argumentTypes));

            case MethodType.Getter:
                if (attr.methodName == null)
                {
                    return(null);
                }

                return(AccessTools.DeclaredProperty(attr.declaringType, attr.methodName)
                       .GetGetMethod(true));

            case MethodType.Setter:
                if (attr.methodName == null)
                {
                    return(null);
                }

                return(AccessTools.DeclaredProperty(attr.declaringType, attr.methodName)
                       .GetSetMethod(true));

            case MethodType.Constructor:
                return(AccessTools.DeclaredConstructor(attr.declaringType, attr.argumentTypes));

            case MethodType.StaticConstructor:
                return(AccessTools.GetDeclaredConstructors(attr.declaringType)
                       .FirstOrDefault(c => c.IsStatic));
            }

            return(null);
        }
Ejemplo n.º 7
0
        private static void AfterMainMenuDraw(ILContext il)
        {
            ILCursor c = new ILCursor(il);

            c.GotoNext(MoveType.AfterLabel,
                       x => x.MatchCallOrCallvirt(typeof(GuiData), nameof(GuiData.endDraw))
                       );

            c.Emit(OpCodes.Ldarg_0);
            c.Emit(OpCodes.Newobj, AccessTools.DeclaredConstructor(typeof(DrawMainMenuEvent), new Type[] { typeof(MainMenu) }));
            c.Emit(OpCodes.Call, AccessTools.DeclaredMethod(typeof(EventManager <DrawMainMenuEvent>), nameof(EventManager <DrawMainMenuEvent> .InvokeAll)));
        }
Ejemplo n.º 8
0
        private static IEnumerable <CodeInstruction> WidgetPrefab_LoadFrom_Transpiler(IEnumerable <CodeInstruction> instructions, MethodBase method)
        {
            var instructionsList = instructions.ToList();

            IEnumerable <CodeInstruction> ReturnDefault(string place)
            {
                Utils.DisplayUserWarning("Failed to patch WidgetPrefab.LoadFrom! {0}", place);
                return(instructionsList.AsEnumerable());
            }

            var constructor = AccessTools.DeclaredConstructor(typeof(WidgetPrefab));

            var locals    = method.GetMethodBody()?.LocalVariables;
            var typeLocal = locals?.FirstOrDefault(x => x.LocalType == typeof(WidgetPrefab));

            if (typeLocal == null)
            {
                return(ReturnDefault("Local not found"));
            }

            var startIndex = -1;

            for (var i = 0; i < instructionsList.Count - 2; i++)
            {
                if (instructionsList[i + 0].opcode != OpCodes.Newobj || !Equals(instructionsList[i + 0].operand, constructor))
                {
                    continue;
                }

                if (!instructionsList[i + 1].IsStloc())
                {
                    continue;
                }

                startIndex = i;
                break;
            }

            if (startIndex == -1)
            {
                return(ReturnDefault("Pattern not found"));
            }

            // PrefabComponent.Load(path, xmlDocument);
            instructionsList.InsertRange(startIndex + 1, new List <CodeInstruction>
            {
                new CodeInstruction(OpCodes.Ldarg_2),
                new CodeInstruction(OpCodes.Ldloc_0),
                new CodeInstruction(OpCodes.Call, SymbolExtensions.GetMethodInfo(() => ProcessMovie(null !, null !)))
            });
Ejemplo n.º 9
0
        /// <summary>
        ///     启用事务追踪
        /// </summary>
        /// <param name="instance"></param>
        private static void SetupTransactionScopeDebug(HarmonyInstance instance)
        {
            var patchClass = typeof(SQLDebug);

            if (_debugConfig == null)
            {
                return;
            }
            if (_debugConfig.IsTraceCustomizeTransaction)
            {
                Type            ubfTransactionScopeType        = typeof(UBFTransactionScope);
                ConstructorInfo ubfTransactionScopeConstructor = AccessTools.DeclaredConstructor(
                    ubfTransactionScopeType, new[]
                {
                    typeof(TransactionOption)
                });
                var prefixUBFTransactionScopeConstructorMethod =
                    patchClass.GetMethod("PrefixUBFTransactionScopeConstructor");
                var PostfixUBFTransactionScopeConstructorMethod =
                    patchClass.GetMethod("PostfixUBFTransactionScopeConstructor");
                var patcherUBFTransactionScopeConstructor = new PatchProcessor(instance,
                                                                               new List <MethodBase> {
                    ubfTransactionScopeConstructor
                },
                                                                               new HarmonyMethod(prefixUBFTransactionScopeConstructorMethod),
                                                                               new HarmonyMethod(PostfixUBFTransactionScopeConstructorMethod));
                patcherUBFTransactionScopeConstructor.Patch();
            }
            if (_debugConfig.IsTraceBPSVTransaction)
            {
                Type transactionAttributeType          = typeof(TransactionAttribute);
                var  transactionAttributeProcessMethod = AccessTools.Method(transactionAttributeType, "Process",
                                                                            new[]
                {
                    typeof(object)
                });
                var prefixTransactionAttributeProcessMethod  = patchClass.GetMethod("PrefixTransactionAttributeProcess");
                var postfixTransactionAttributeProcessMethod =
                    patchClass.GetMethod("PostfixTransactionAttributeProcess");
                var patcherTransactionAttributeProcess = new PatchProcessor(instance,
                                                                            new List <MethodBase> {
                    transactionAttributeProcessMethod
                },
                                                                            new HarmonyMethod(prefixTransactionAttributeProcessMethod),
                                                                            new HarmonyMethod(postfixTransactionAttributeProcessMethod));
                patcherTransactionAttributeProcess.Patch();
            }
        }
Ejemplo n.º 10
0
        // Copied from Harmony.PatchProcessor
        public static MethodBase GetMethod(Type type, string methodName, MethodType methodType, Type[] args)
        {
            if (type == null)
            {
                return(null);
            }

            switch (methodType)
            {
            case MethodType.Normal:
                if (methodName == null)
                {
                    return(null);
                }
                return(AccessTools.DeclaredMethod(type, methodName, args));

            case MethodType.Getter:
                if (methodName == null)
                {
                    return(null);
                }
                return(AccessTools.DeclaredProperty(type, methodName).GetGetMethod(true));

            case MethodType.Setter:
                if (methodName == null)
                {
                    return(null);
                }
                return(AccessTools.DeclaredProperty(type, methodName).GetSetMethod(true));

            case MethodType.Constructor:
                return(AccessTools.DeclaredConstructor(type, args));

            case MethodType.StaticConstructor:
                return(AccessTools.GetDeclaredConstructors(type)
                       .Where(c => c.IsStatic)
                       .FirstOrDefault());
            }

            return(null);
        }
Ejemplo n.º 11
0
        public CorruptionCore(ModContentPack mod)
        {
            // ITab_Pawn_Soul - checkboxes to allow praying and show prayers
            var type = AccessTools.TypeByName("Corruption.Core.Soul.ITab_Pawn_Soul");

            pawnSoulITabSoulToShowGetter = MethodInvoker.GetHandler(AccessTools.PropertyGetter(type, "SoulToShow"));
            MP.RegisterSyncMethod(typeof(CorruptionCore), nameof(SyncFavourValue));
            MpCompat.harmony.Patch(AccessTools.Method(type, "FillTab"),
                                   prefix: new HarmonyMethod(typeof(CorruptionCore), nameof(PreFillTab)),
                                   postfix: new HarmonyMethod(typeof(CorruptionCore), nameof(PostFillTab)));

            type = AccessTools.TypeByName("Corruption.Core.Soul.CompSoul");
            compSoulFavourTrackerField = AccessTools.FieldRefAccess <object>(type, "FavourTracker");
            compSoulPrayerTrackerField = AccessTools.FieldRefAccess <object>(type, "PrayerTracker");

            type = AccessTools.TypeByName("Corruption.Core.Dialog_SetPawnPantheon");
            setPawnPantheonDialogConstructor = AccessTools.DeclaredConstructor(type);
            setPawnPantheonDialogSoulField   = AccessTools.FieldRefAccess <ThingComp>(type, "soul");
            MP.RegisterSyncMethod(type, "SelectionChanged");
            MP.RegisterSyncWorker <object>(SyncDialogSetPawnPantheon, type);

            type = AccessTools.TypeByName("Corruption.Core.Soul.Soul_FavourTracker");
            soulFavourTrackerFavoursField = AccessTools.FieldRefAccess <IList>(type, "Favours");

            type = AccessTools.TypeByName("Corruption.Core.Gods.Pawn_PrayerTracker");
            prayerTrackerCompSoulField    = AccessTools.FieldRefAccess <ThingComp>(type, "compSoul");
            prayerTrackerAllowPrayingSync = MP.RegisterSyncField(type, "AllowPraying");
            prayerTrackerShowPrayerSync   = MP.RegisterSyncField(type, "ShowPrayer");
            MP.RegisterSyncWorker <object>(SyncPawnPrayerTracker, type);
            MpCompat.harmony.Patch(AccessTools.Method(type, "AdvancePrayer"),
                                   prefix: new HarmonyMethod(typeof(CorruptionCore), nameof(PreAdvancePrayer)),
                                   postfix: new HarmonyMethod(typeof(CorruptionCore), nameof(PostAdvancePrayer)));

            type = AccessTools.TypeByName("Corruption.Core.Soul.FavourProgress");
            favourProgressFavourValueField = AccessTools.FieldRefAccess <float>(type, "favourValue");
        }
Ejemplo n.º 12
0
        internal static void InitializePatch(IPlatoHelper helper)
        {
            Plato = helper;
            if (_patched)
            {
                return;
            }

            _patched = true;
            var questionRaised = AccessTools.DeclaredConstructor(typeof(DialogueBox), new Type[] { typeof(string), typeof(List <Response>), typeof(int) });

            List <Type> questionLocationTypes = new List <Type>()
            {
                typeof(GameLocation),
                typeof(BusStop),
                typeof(Desert),
                typeof(JojaMart)
            };


            var channelSelected = AccessTools.DeclaredMethod(typeof(TV), "selectChannel");
            var tvAction        = AccessTools.DeclaredMethod(typeof(TV), "checkForAction");

            List <MethodInfo> questionAsked = new List <MethodInfo>(questionLocationTypes.Select(t => AccessTools.Method(t, "answerDialogue")));

            var performTouchAction = new[] {
                AccessTools.DeclaredMethod(typeof(GameLocation), "performTouchAction"),
                AccessTools.DeclaredMethod(typeof(MovieTheater), "performTouchAction"),
                AccessTools.DeclaredMethod(typeof(Desert), "performTouchAction")
            };

            var performAction = new[] {
                AccessTools.DeclaredMethod(typeof(GameLocation), "performAction"),
                AccessTools.DeclaredMethod(typeof(MovieTheater), "performAction"),
                AccessTools.DeclaredMethod(typeof(CommunityCenter), "performAction"),
                AccessTools.DeclaredMethod(typeof(FarmHouse), "performAction"),
                AccessTools.DeclaredMethod(typeof(ManorHouse), "performAction"),
                AccessTools.DeclaredMethod(typeof(LibraryMuseum), "performAction"),
                AccessTools.DeclaredMethod(typeof(Town), "performAction"),
            };

            var harmony = new Harmony($"Plato.QuestionPatches");

            harmony.Patch(questionRaised, prefix: new HarmonyMethod(AccessTools.Method(typeof(EventPatches), nameof(DialogueBox))));

            foreach (var method in questionAsked)
            {
                harmony.Patch(method, prefix: new HarmonyMethod(
                                  AccessTools.DeclaredMethod(typeof(EventPatches),
                                                             nameof(QuestionAsked),
                                                             null, new Type[] { method.DeclaringType })));
            }

            harmony.Patch(channelSelected, prefix: new HarmonyMethod(AccessTools.Method(typeof(EventPatches), nameof(SelectChannel))));
            harmony.Patch(tvAction, prefix: new HarmonyMethod(AccessTools.Method(typeof(EventPatches), nameof(SetIsTv))));
            harmony.Patch(tvAction, postfix: new HarmonyMethod(AccessTools.Method(typeof(EventPatches), nameof(UnsetIsTV))));

            harmony.Patch(AccessTools.Method(typeof(Event), nameof(Event.tryEventCommand)),
                          new HarmonyMethod(typeof(EventPatches), nameof(TryEventCommandPre)));

            harmony.Patch(AccessTools.Method(typeof(Event), nameof(Event.tryEventCommand)), null,
                          new HarmonyMethod(typeof(EventPatches), nameof(TryEventCommandPost)));

            foreach (var ta in performTouchAction)
            {
                harmony.Patch(ta, prefix: new HarmonyMethod(AccessTools.Method(typeof(EventPatches), nameof(PerformTouchAction))));
            }
            foreach (var a in performAction)
            {
                harmony.Patch(a, prefix: new HarmonyMethod(AccessTools.Method(typeof(EventPatches), nameof(PerformAction))));
            }

            var checkEventPreconditions = AccessTools.Method(typeof(GameLocation), "checkEventPrecondition");

            harmony.Patch(checkEventPreconditions, prefix: new HarmonyMethod(typeof(EventPatches), nameof(CheckEventConditions)));
        }
Ejemplo n.º 13
0
 /*********
 ** Public methods
 *********/
 public static ConstructorInfo DeclaredConstructor(Type type, Type[] parameters = null)
 {
     return(AccessTools.DeclaredConstructor(type, parameters, searchForStatic: true));
 }
 private static ConstructorInfo Il2CppConstuctor(Type type) => AccessTools.DeclaredConstructor(type, new Type[] { typeof(IntPtr) });
Ejemplo n.º 15
0
        // I was allowed to use PokéWorld as class name.
        // However, it caused issues with auto completion.
        public PokeWorld(ModContentPack mod)
        {
            var type = AccessTools.TypeByName("PokeWorld.CompPokemon");

            pokemonFormTrackerField  = AccessTools.FieldRefAccess <object>(type, "formTracker");
            pokemonLevelTrackerField = AccessTools.FieldRefAccess <object>(type, "levelTracker");
            pokemonMoveTrackerField  = AccessTools.FieldRefAccess <object>(type, "moveTracker");

            type = AccessTools.TypeByName("PokeWorld.CompProperties_Pokemon");
            propsFormsListField = AccessTools.FieldRefAccess <IList>(type, "forms");

            // Gizmos
            {
                type = AccessTools.TypeByName("PokeWorld.PutInBallUtility");
                MP.RegisterSyncMethod(type, "UpdatePutInBallDesignation"); // Only called from CompPokemon gizmo

                type = AccessTools.TypeByName("PokeWorld.PutInPortableComputerUtility");
                MP.RegisterSyncMethod(type, "UpdatePutInPortableComputerDesignation"); // Only called from CryptosleepBall gizmo

                type = AccessTools.TypeByName("PokeWorld.FormTracker");
                formTrackerCompField = AccessTools.FieldRefAccess <ThingComp>(type, "comp");

                type = AccessTools.Inner(type, "<>c__DisplayClass17_0");
                innerClassFormField   = AccessTools.FieldRefAccess <object>(type, "form");
                innerClassParentField = AccessTools.FieldRefAccess <object>(type, "<>4__this");
                MP.RegisterSyncMethod(type, "<ProcessInput>b__0");
                MP.RegisterSyncWorker <object>(SyncFormTrackerInnerClass, type, shouldConstruct: true);

                type = AccessTools.TypeByName("PokeWorld.LevelTracker");
                MpCompat.RegisterLambdaMethod(type, "GetGizmos", 1, 2);
                levelTrackerCompField = AccessTools.FieldRefAccess <ThingComp>(type, "comp");
                MP.RegisterSyncWorker <object>(SyncLevelTracker, type);

                // There's a bunch of gizmos in PokemonAttackGizmoUtility, but they don't seem like they need syncing.
                // In my testing they seemed fine. The way they're made I believe those should be handled by MP itself.
            }

            // ITab
            {
                type = AccessTools.TypeByName("PokeWorld.ITab_ContentsPokeball");
                MP.RegisterSyncMethod(type, "OnDropThing").SetContext(SyncContext.MapSelected);
                MP.RegisterSyncWorker <object>(NoSync, type, shouldConstruct: true);

                type = AccessTools.TypeByName("PokeWorld.ITab_ContentsStorageSystem");
                MP.RegisterSyncMethod(type, "InterfaceDrop").SetContext(SyncContext.MapSelected);
                var method = AccessTools.Method(type, "InterfaceDrop");
                storageTabConstructor = AccessTools.DeclaredConstructor(type);
                interfaceDropMethod   = MethodInvoker.GetHandler(method);
                MpCompat.harmony.Patch(method,
                                       prefix: new HarmonyMethod(typeof(PokeWorld), nameof(PreInterfaceDrop)));
                MP.RegisterSyncMethod(typeof(PokeWorld), nameof(SyncedInterfaceDrop)).SetContext(SyncContext.MapSelected);

                storageSystemType = AccessTools.TypeByName("PokeWorld.StorageSystem");

                type = AccessTools.TypeByName("PokeWorld.MoveTracker");
                MP.RegisterSyncMethod(type, "SetWanted"); // Only called from ITab_Pawn_Moves/MoveCardUtility checkbox
                moveTrackerCompField = AccessTools.FieldRefAccess <ThingComp>(type, "comp");
                MP.RegisterSyncWorker <object>(SyncMoveTracker, type);
            }

            // RNG
            {
                type = AccessTools.TypeByName("PokeWorld.ShinyTracker");
                shinyTrackerCompField = AccessTools.FieldRefAccess <ThingComp>(type, "comp");
                MpCompat.harmony.Patch(AccessTools.Method(type, "TryMakeShinyMote"),
                                       prefix: new HarmonyMethod(typeof(PokeWorld), nameof(PreTryMakeShinyMote)),
                                       postfix: new HarmonyMethod(typeof(PokeWorld), nameof(PostTryMakeShinyMote)));
            }
        }