Exemple #1
0
        public static void DysonSphereRelatedGameTick(FactorySystem __instance)
        {
            IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
            {
                CodeMatcher matcher = new CodeMatcher(instructions);

                List <CodeInstruction> instructionsList = instructions.ToList();
                List <CodeInstruction> code             = new List <CodeInstruction>();

                int startIdx = 0;
                int endIdx   = matcher.MatchForward(false,
                                                    new CodeMatch(OpCodes.Ldloc_0),
                                                    new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(GameHistoryData), nameof(GameHistoryData.miningCostRate)))
                                                    ).Pos;

                if (endIdx == instructionsList.Count)
                {
                    throw new InvalidOperationException("Cannot extract the dysonsphere part of FactorySystem.GameTick because the first indicator isn't present");
                }

                for (int i = startIdx; i < endIdx; i++)
                {
                    code.Add(instructionsList[i]);
                }



                startIdx = matcher.MatchForward(false,
                                                new CodeMatch(OpCodes.Ldarg_0),
                                                new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(FactorySystem), nameof(FactorySystem.ejectorCursor)))
                                                ).Pos;
                if (startIdx == instructionsList.Count)
                {
                    throw new InvalidOperationException("Cannot extract the dysonsphere part of FactorySystem.GameTick because the second indicator isn't present");
                }

                endIdx = matcher.MatchForward(false,
                                              new CodeMatch(OpCodes.Ldloc_0),
                                              new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(GameHistoryData), nameof(GameHistoryData.currentTech)))
                                              ).Pos;
                if (endIdx == instructionsList.Count)
                {
                    throw new InvalidOperationException("Cannot extract the dysonsphere part of FactorySystem.GameTick because the third indicator isn't present");
                }

                Debug.Log($"extracted dysonsphere part of FactorySystem.GameTick from {startIdx} to {endIdx}");
                for (int i = startIdx; i < endIdx; i++)
                {
                    code.Add(instructionsList[i]);
                }

                return(code.AsEnumerable());
            }

            // make compiler happy
            _ = Transpiler(null);
            return;
        }
Exemple #2
0
        public static IEnumerable <CodeInstruction> GameTick_Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher matcher = new CodeMatcher(instructions)
                                  .MatchForward(true,
                                                new CodeMatch(OpCodes.Ldarg_0),
                                                new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PlayerControlGizmo), "player")),
                                                new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_controller"),
                                                new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_modelVisible"),
                                                new CodeMatch(OpCodes.Brfalse));
            var jmpPos = matcher.Operand;

            matcher.Advance(1)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Func <PlayerControlGizmo, bool> >((PlayerControlGizmo _this) =>
            {
                return(Multiplayer.IsActive && _this.player.navigation.indicatorAstroId > 100000);
            }))
            .Insert(new CodeInstruction(OpCodes.Brtrue, jmpPos));

            matcher.MatchForward(true,
                                 new CodeMatch(OpCodes.Ldarg_0),
                                 new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PlayerControlGizmo), "player")),
                                 new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_navigation"),
                                 new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_indicatorAstroId"),
                                 new CodeMatch(OpCodes.Ldc_I4_0),
                                 new CodeMatch(OpCodes.Ble));
            jmpPos = matcher.Operand;
            matcher.Advance(1)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Func <PlayerControlGizmo, bool> >((PlayerControlGizmo _this) =>
            {
                return(Multiplayer.IsActive && _this.player.navigation.indicatorAstroId > 100000);
            }))
            .Insert(new CodeInstruction(OpCodes.Brtrue, jmpPos));

            matcher.MatchForward(true,
                                 new CodeMatch(OpCodes.Ldarg_0),
                                 new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PlayerControlGizmo), "player")),
                                 new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_navigation"),
                                 new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_indicatorAstroId"),
                                 new CodeMatch(OpCodes.Ldc_I4_0),
                                 new CodeMatch(OpCodes.Ble));
            jmpPos = matcher.Operand;
            matcher.Advance(1)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Func <PlayerControlGizmo, bool> >((PlayerControlGizmo _this) =>
            {
                return(Multiplayer.IsActive && _this.player.navigation.indicatorAstroId > 100000);
            }))
            .Insert(new CodeInstruction(OpCodes.Brtrue, jmpPos));

            return(matcher.InstructionEnumeration());
        }
Exemple #3
0
        public static IEnumerable <CodeInstruction> CargoPathPatch(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher matcher = new CodeMatcher(instructions)
                                  .MatchForward(false,
                                                new CodeMatch(OpCodes.Ldarg_0),
                                                new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(CargoPath), nameof(CargoPath.chunks))),
                                                new CodeMatch(OpCodes.Ldloc_S)
                                                );

            matcher.MatchForward(false, new CodeMatch(OpCodes.Stloc_S));
            object beginArg = matcher.Operand;

            matcher.Advance(1).MatchForward(false, new CodeMatch(OpCodes.Stloc_S));
            object speedArg = matcher.Operand;

            matcher.MatchForward(false,
                                 new CodeMatch(OpCodes.Ldc_I4_0),
                                 new CodeMatch(OpCodes.Stloc_S));

            matcher.Advance(2)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloca_S, beginArg))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloca_S, speedArg))
            .InsertAndAdvance(Transpilers.EmitDelegate <RefAction <CargoPath, int, int> >((CargoPath path, ref int begin, ref int speed) =>
            {
                // The change
                if (speed > 10)     // If the speed is greater than 10, the length judgment process is performed to prevent crossing the boundary
                {
                    for (int i = 10; i <= speed; i++)
                    {
                        if (begin + i + 10 >= path.bufferLength)     // About to leave the end of the conveyor belt
                        {
                            speed = i;
                            break;
                        }

                        if (path.buffer[begin + i] != 0)     // Not empty within the speed range
                        {
                            speed = i;
                            break;
                        }
                    }

                    if (speed < 10)
                    {
                        speed = 10;     // If the speed slows down to within a safe speed, set it to a safe speed
                    }
                }
            }));

            return(matcher.InstructionEnumeration());
        }
Exemple #4
0
            private static IEnumerable <CodeInstruction> TryPlayCustomTopicAdvTpl(IEnumerable <CodeInstruction> instructions)
            {
                var matcher = new CodeMatcher(instructions)
                              .MatchForward(true, new CodeMatch(OpCodes.Ldstr, "話題を振る"))
                              .MatchForward(true, new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(TalkScene), nameof(TalkScene.isNPC))))
                              .Advance(1)
                              .ThrowIfNotMatch("Brtrue not found", new CodeMatch(OpCodes.Brtrue));

                var startPos = matcher.Pos;

                // Figure out where the if else ends so we can jump there if we want to skip it
                // First go to the start of else, then step back and get the label at the end of if that skips over the else
                var elseLabel = ((Label)matcher.Operand);

                matcher.MatchForward(false, new CodeMatch(instruction => instruction.labels.Contains(elseLabel)))
                .Advance(-1)
                .ThrowIfNotMatch("Br not found", new CodeMatch(OpCodes.Br));
                var skipIfelseLabel = matcher.Operand;

                // Go back to the start of the if
                matcher.Advance(startPos - matcher.Pos).ThrowIfNotMatch("Brtrue not found 2", new CodeMatch(OpCodes.Brtrue));

                // Go back to the first if, then insert our if before it
                // Copy the `this` load instead of hardcoding it just in case (isNPC takes it)
                matcher.Advance(-2);
                var loadInstrCopy = new CodeInstruction(matcher.Opcode, matcher.Operand);

                matcher.Advance(1).Insert(
                    new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(TopicHooks), nameof(TryPlayCustomTopicAdvHook))),
                    new CodeInstruction(OpCodes.Brtrue, skipIfelseLabel),
                    loadInstrCopy);

                return(matcher.Instructions());
            }
Exemple #5
0
        public static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator il)
        {
            var matcher = new CodeMatcher(instructions, il);

            // Search for the first time we check if the player has opened a chest, this happens when adding the items in the opened inventory to the dictionary
            matcher.MatchForward(false, new CodeMatch(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.player))), new CodeMatch(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.myPlayer))), new CodeMatch(OpCodes.Ldelem_Ref), new CodeMatch(OpCodes.Ldfld, typeof(Player).GetField(nameof(Player.chest))), new CodeMatch(OpCodes.Ldc_I4_M1), new CodeMatch(OpCodes.Beq));

            matcher.Instruction.opcode  = OpCodes.Nop;
            matcher.Instruction.operand = null;
            matcher.Advance(1);

            var getItemCode = new CodeInstruction[]
            {
                new CodeInstruction(OpCodes.Ldarg_0),
                new CodeInstruction(OpCodes.Ldloca_S, 1),
                new CodeInstruction(OpCodes.Ldloca_S, 2),
                new CodeInstruction(OpCodes.Call, typeof(StorageUI).GetMethod(nameof(StorageUI.DoWithdrawItemForCraft), new Type[] { typeof(Recipe), typeof(Item).MakeByRefType(), typeof(int).MakeByRefType() })),

                // The old instruction we destroyed
                new CodeInstruction(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.player)))
            };

            matcher.InsertAndAdvance(getItemCode);

            return(matcher.InstructionEnumeration());
        }
Exemple #6
0
        public static IEnumerable <CodeInstruction> MultiplyUnlockText(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher matcher = new CodeMatcher(instructions)
                                  .MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(TechProto), nameof(TechProto.UnlockFunctions))))
                                  .MatchForward(false, new CodeMatch(instr => instr.IsStloc()));

            OpCode typeStlocOpcode  = matcher.Opcode.ToLoad();
            object typeStlocOperand = matcher.Operand;

            matcher.MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(TechProto), nameof(TechProto.UnlockValues))))
            .MatchForward(false, new CodeMatch(OpCodes.Stloc_S));

            object arg = matcher.Operand;

            matcher.Advance(1)
            .InsertAndAdvance(new CodeInstruction(typeStlocOpcode, typeStlocOperand))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloca_S, arg))
            .InsertAndAdvance(Transpilers.EmitDelegate <RefAction <int, int> >((int type, ref int value) =>
            {
                if (type == 18)
                {
                    value *= GigaStationsPlugin.droneCapacityMultiplier;
                }
                else if (type == 19)
                {
                    value *= GigaStationsPlugin.vesselCapacityMultiplier;
                }
            }));

            return(matcher.InstructionEnumeration());
        }
        public static IEnumerable <CodeInstruction> _OnUpdate_Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            try {
                int         pos1, pos2;
                CodeMatcher matcher = new CodeMatcher(instructions);

                matcher.MatchForward(false, new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(DysonNode), "color")));
                pos1 = matcher.Pos;
                matcher.MatchBack(false, new CodeMatch(OpCodes.Ldloc_S));
                pos2 = matcher.Pos;
                matcher.Advance(pos1 - pos2 + 1)
                .Insert(
                    new CodeInstruction(matcher.InstructionAt(pos2 - pos1 - 1)),
                    new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(UIDysonBrush_Paint_Transpiler), "SendNodePacket"))
                    );

                matcher.MatchForward(false, new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(DysonFrame), "color")));
                pos1 = matcher.Pos;
                matcher.MatchBack(false, new CodeMatch(OpCodes.Ldloc_S));
                pos2 = matcher.Pos;
                matcher.Advance(pos1 - pos2 + 1)
                .Insert(
                    new CodeInstruction(matcher.InstructionAt(pos2 - pos1 - 1)),
                    new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(UIDysonBrush_Paint_Transpiler), "SendFramePacket"))
                    );

                matcher.MatchForward(false, new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(DysonShell), "color")));
                pos1 = matcher.Pos;
                matcher.MatchBack(false, new CodeMatch(OpCodes.Ldloc_S));
                pos2 = matcher.Pos;
                matcher.Advance(pos1 - pos2 + 1)
                .Insert(
                    new CodeInstruction(matcher.InstructionAt(pos2 - pos1 - 1)),
                    new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(UIDysonBrush_Paint_Transpiler), "SendShellPacket"))
                    );

                return(matcher.InstructionEnumeration());
            }
            catch
            {
                NebulaModel.Logger.Log.Error("UIDysonBrush_Paint._OnUpdate_Transpiler failed. Mod version not compatible with game version.");
                return(instructions);
            }
        }
Exemple #8
0
        static IEnumerable <CodeInstruction> RedirectSaves(IEnumerable <CodeInstruction> instructions)
        {
            var codeMatch = new CodeMatcher(instructions);

            codeMatch = codeMatch.MatchForward(false, new CodeMatch(OpCodes.Ldstr, "save"));
            codeMatch = codeMatch.Repeat(matcher =>
            {
                matcher.SetOperandAndAdvance(folderRedirect + matcher.Operand);
            });
            return(codeMatch.InstructionEnumeration());
        }
Exemple #9
0
        public static void DestructObject(this PlanetFactory __instance, int objId, out int protoId)
        {
            // ReSharper disable once LocalFunctionCanBeMadeStatic
#pragma warning disable 8321
            IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction>?instructions, ILGenerator?generator)
            {
                if (instructions is null)
                {
                    return(new List <CodeInstruction>());
                }

                // IL length of the calls to PlayerAction_Build::NotifyDestruct(int32) and
                // instance void PlayerAction_Inspect::NotifyObjectDestruct(EObjectType, int32), minus NOPs
                const int callNotifyLength = 9;

                // IL length of the call to PlanetFactory::TakeBackItemsInEntity(Player, int32)
                const int callTakeItemsLength = 4;

                var codeMatcher = new CodeMatcher(instructions, generator);

                // Delete calls to the above methods, along with associated stack transforms.
                var output = codeMatcher.MatchForward(false, MatchLoadPlayer)            // IL_002a: ldarg.1
                             .SetOpcodeAndAdvance(OpCodes.Nop)                           // transform: ldarg.1 -> nop
                             .SetOpcodeAndAdvance(OpCodes.Nop)                           // transform: callvirt -> nop
                             .RemoveInstructions(callNotifyLength)                       // remove: opcode1 ... opcode9
                             .With(matcher => Console.WriteLine((string)"Currently at instruction {0}", (object)matcher.Instruction))
                             .MatchForward(false, MatchCallTakeItems)                    // IL_0050: call instance void PlanetFactory::TakeBackItemsInEntity(class Player, int32)
                             .MatchBack(false, MatchLoadThis)                            // IL_004d: ldarg.0
                             .RemoveInstructions(callTakeItemsLength)                    // remove: opcode1 ... opcode4
                             .MatchForward(false, MatchLoadPlayer)                       // IL_00a4: ldarg.1
                             .SetOpcodeAndAdvance(OpCodes.Nop)                           // transform: ldarg.1 -> nop
                             .SetOpcodeAndAdvance(OpCodes.Nop)                           // transform: callvirt -> nop
                             .RemoveInstructions(callNotifyLength)                       // remove: opcode1 ... opcode9
                             .InstructionEnumeration()
                             .ToList();

                output.ForEach(instruction => {
                    if (instruction.IsLdarg(2))
                    {
                        instruction.opcode = OpCodes.Ldarg_1;                         // transform: ldarg.2 -> ldarg.1
                    }
                    else if (instruction.IsLdarg(3))
                    {
                        instruction.opcode = OpCodes.Ldarg_2;                         // transform: ldarg.3 -> ldarg.2
                    }
                });

                return(output);
            }

            protoId = 0;
#pragma warning restore 8321
        }
Exemple #10
0
        public static IEnumerable <CodeInstruction> ChaControl_SetShapeBodyValue_RemoveHeightLock(IEnumerable <CodeInstruction> instructions)
        {
            var cm = new CodeMatcher(instructions);

            // find branch that overrides the male height
            cm.MatchForward(false, new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(ChaInfo), nameof(ChaInfo.sex))))
            .MatchForward(false, new CodeMatch(c => c.Branches(out _)));
            // turn it into an unconditional jump
            cm.InsertAndAdvance(new CodeInstruction(OpCodes.Pop))
            .SetOpcodeAndAdvance(OpCodes.Br);
            return(cm.Instructions());
        }
Exemple #11
0
        public static IEnumerable <CodeInstruction> ChaControl_Initialize_RemoveHeightLock(IEnumerable <CodeInstruction> instructions)
        {
            var cm = new CodeMatcher(instructions);

            // find branch that overrides the male height
            cm.MatchForward(false, new CodeMatch(OpCodes.Ldc_R4, 0.6f))
            .MatchBack(false, new CodeMatch(OpCodes.Call))
            .MatchBack(false, new CodeMatch(c => c.Branches(out _)));
            // turn it into an unconditional jump
            cm.InsertAndAdvance(new CodeInstruction(OpCodes.Pop))
            .SetOpcodeAndAdvance(OpCodes.Br);
            return(cm.Instructions());
        }
Exemple #12
0
        public static IEnumerable <CodeInstruction> AddColors(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher matcher = new CodeMatcher(instructions)
                                  .MatchForward(false,
                                                new CodeMatch(OpCodes.Ldloc_1),
                                                new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BeltComponent), nameof(BeltComponent.speed))),
                                                new CodeMatch(OpCodes.Ldc_I4_1)
                                                );

            CodeMatcher matcher2 = matcher.Clone();

            matcher2.MatchForward(true,
                                  new CodeMatch(OpCodes.Ldloc_S),
                                  new CodeMatch(OpCodes.Stloc_S));

            object arg = matcher2.Operand;

            matcher2.Advance(1);
            object label = matcher2.Operand;

            matcher.Advance(2)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, arg))
            .SetInstruction(Transpilers.EmitDelegate <Func <int, int, int> >((speed, other) =>
            {
                if (speed <= 1)
                {
                    return(other);
                }

                if (speed <= 2)
                {
                    other += 4;
                }
                else if (speed <= 5)
                {
                    other += 8;
                }
                else if (speed <= 12)
                {
                    other += 12;
                }

                return(other);
            }))
            .Advance(1)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Stloc_S, arg))
            .SetInstruction(new CodeInstruction(OpCodes.Br, label));


            return(matcher.InstructionEnumeration());
        }
            private static IEnumerable <CodeInstruction> FixUpdateCalcGarbage(IEnumerable <CodeInstruction> instructions, ILGenerator generator)
            {
                var c = new CodeMatcher(instructions, generator);

                // Remove unnecessary new object creation that gets immediately overwritten
                return(c.MatchForward(false,
                                      new CodeMatch(OpCodes.Newobj),
                                      new CodeMatch(OpCodes.Stloc_1),
                                      new CodeMatch(OpCodes.Ldarg_0),
                                      new CodeMatch(OpCodes.Ldfld))
                       .RemoveInstruction()
                       .RemoveInstruction()
                       .Instructions());
            }
Exemple #14
0
        public static IEnumerable <CodeInstruction> Replace10With1Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher matcher = new CodeMatcher(instructions)
                                  .MatchForward(false, new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(StationComponent), nameof(StationComponent.deliveryDrones))))
                                  .MatchBack(false, new CodeMatch(instr => instr.opcode == OpCodes.Ldc_R4 && Mathf.Approximately((float)instr.operand, 10f)))
                                  .SetOperandAndAdvance(1f);

            matcher
            .MatchForward(false, new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(StationComponent), nameof(StationComponent.deliveryShips))))
            .MatchBack(false, new CodeMatch(instr => instr.opcode == OpCodes.Ldc_R4 && Mathf.Approximately((float)instr.operand, 10f)))
            .SetOperandAndAdvance(1f);

            return(matcher.InstructionEnumeration());
        }
Exemple #15
0
        public static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator il)
        {
            var matcher = new CodeMatcher(instructions, il);

            // Search for the first time we check if the player has opened a chest, this happens when adding the items in the opened inventory to the dictionary
            matcher.MatchForward(false, new CodeMatch(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.player))), new CodeMatch(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.myPlayer))), new CodeMatch(OpCodes.Ldelem_Ref), new CodeMatch(OpCodes.Ldfld, typeof(Player).GetField(nameof(Player.chest))), new CodeMatch(OpCodes.Ldc_I4_M1), new CodeMatch(OpCodes.Beq));
            var findRecipesCode = new CodeInstruction[]
            {
                new CodeInstruction(OpCodes.Ldloca_S, 6),
                new CodeInstruction(OpCodes.Call, typeof(MagicStoragePlus).GetMethod(nameof(MagicStoragePlus.FindRecipesPatch), new Type[] { typeof(Dictionary <int, int>).MakeByRefType() }))
            };

            matcher.InsertAndAdvance(findRecipesCode);

            return(matcher.InstructionEnumeration());
        }
        private static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher codeMatcher = new CodeMatcher(instructions)
                                      .MatchForward(false, new CodeMatch(OpCodes.Br));

            object label = codeMatcher.Operand;

            return(codeMatcher
                   .MatchForward(false, new CodeMatch(OpCodes.Call, _currentGetter))
                   .Advance(2)
                   .Insert(
                       new CodeInstruction(OpCodes.Ldloc_1),
                       new CodeInstruction(OpCodes.Call, FakeNoteHelper._boundsNullCheck),
                       new CodeInstruction(OpCodes.Brtrue_S, label))
                   .InstructionEnumeration());
        }
        private static IEnumerable <CodeInstruction> InternalUpdate_Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            // Store projectile data after swarm.AddBullet(sailBullet, orbitId) if IsUpdateNeeded == true
            try
            {
                CodeMatcher matcher = new CodeMatcher(instructions)
                                      .MatchForward(false,
                                                    new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(SailBullet), nameof(SailBullet.lBegin))) //IL#638
                                                    );
                CodeInstruction loadInstruction = matcher.InstructionAt(-1);
                matcher.MatchForward(false,
                                     new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(DysonSwarm), nameof(DysonSwarm.AddBullet))) //IL#679
                                     )
                .Advance(2)
                .InsertAndAdvance(
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.planetId))),
                    loadInstruction,
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.orbitId))),
                    HarmonyLib.Transpilers.EmitDelegate <Action <int, Vector3, int> >((planetId, localPos, orbitId) =>
                {
                    // If the dyson sphere has no subscribers anymore, skip this data
                    if (!Multiplayer.IsActive || !Multiplayer.Session.Launch.Snapshots.ContainsKey(planetId / 100 - 1))
                    {
                        return;
                    }

                    // Assume orbitId < 65536
                    DysonLaunchData.Projectile data = new DysonLaunchData.Projectile
                    {
                        PlanetId = planetId,
                        TargetId = (ushort)orbitId,
                        LocalPos = localPos
                    };
                    Multiplayer.Session.Launch.ProjectileBag.Add(data);
                })
                    );
                return(matcher.InstructionEnumeration());
            }
            catch
            {
                NebulaModel.Logger.Log.Error("EjectorComponent.InternalUpdate_Transpiler failed. Mod version not compatible with game version.");
                return(instructions);
            }
        }
Exemple #18
0
        public static IEnumerable <CodeInstruction> RemoveDivisionBy10(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher matcher = new CodeMatcher(instructions)
                                  .MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), nameof(StationComponent.deliveryDrones))))
                                  .MatchForward(false, new CodeMatch(instr => instr.opcode == OpCodes.Ldc_R4 && Mathf.Approximately((float)instr.operand, 0.1f)))
                                  .SetAndAdvance(OpCodes.Nop, null)
                                  .Advance(2)
                                  .SetAndAdvance(OpCodes.Nop, null);

            matcher
            .MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), nameof(StationComponent.deliveryShips))))
            .MatchForward(false, new CodeMatch(instr => instr.opcode == OpCodes.Ldc_R4 && Mathf.Approximately((float)instr.operand, 0.1f)))
            .SetAndAdvance(OpCodes.Nop, null)
            .Advance(2)
            .SetAndAdvance(OpCodes.Nop, null);

            return(matcher.InstructionEnumeration());
        }
            private static IEnumerable <CodeInstruction> PhPluginSceneInfoSaveTranspiler(IEnumerable <CodeInstruction> instructions)
            {
                var matcher = new CodeMatcher(instructions);

                matcher.MatchForward(false, new CodeMatch(OpCodes.Ldstr, "【PHStudio】"));
                var getWriterInstr = matcher.InstructionAt(-1);

                matcher.Advance(2);
                matcher.InsertAndAdvance(
                    new CodeInstruction(OpCodes.Ldarg_0),
                    getWriterInstr,
                    CodeInstruction.Call(typeof(Hooks), nameof(SceneInfoSaveHook)));

                if (matcher.Instruction.opcode != OpCodes.Leave && matcher.Instruction.opcode != OpCodes.Leave_S)
                {
                    throw new Exception("Failed to patch SceneInfo.Save");
                }

                return(matcher.Instructions());
            }
Exemple #20
0
        static IEnumerable <CodeInstruction> FactorySystem_DrawModels_Patch(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher matcher = new CodeMatcher(instructions);

            matcher
            .MatchForward(false,
                          new CodeMatch(OpCodes.Ldarg_0),
                          new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(FactorySystem), nameof(FactorySystem.ejectorCursor)))
                          );

            // replace the code logic with the same logic wrapped in a meaningful lock for thread safety and return
            matcher
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .InsertAndAdvance(Transpilers.EmitDelegate <Action <FactorySystem> >(factorySystem =>
            {
                if (factorySystem.factory.dysonSphere != null)
                {
                    lock (factorySystem.factory.dysonSphere)
                    {
                        DysonSphereRelatedGameTick(factorySystem);
                    }
                }
                else
                {
                    DysonSphereRelatedGameTick(factorySystem);
                }
            }))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .InsertAndAdvance(Transpilers.EmitDelegate <Action <FactorySystem> >(factorySystem =>
            {
                lock (GameMain.history)
                {
                    TechRelatedGameTick(factorySystem);
                }
            }))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ret));

            return(matcher.InstructionEnumeration());
        }
Exemple #21
0
        public static IEnumerable <CodeInstruction> GizmoColor(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher matcher = new CodeMatcher(instructions)
                                  .MatchForward(false,
                                                new CodeMatch(OpCodes.Ldloca_S),
                                                new CodeMatch(OpCodes.Initobj, typeof(ConnGizmoObj)));

            object arg = matcher.Operand;

            matcher.MatchForward(false,
                                 new CodeMatch(OpCodes.Ldarg_0)
                                 ).Advance(1)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloca_S, arg))
            .InsertAndAdvance(Transpilers.EmitDelegate <RefAction <ConnGizmoObj> >((ref ConnGizmoObj obj) =>
            {
                if (obj.color == 12)
                {
                    obj.color = 6;
                }
            }));

            return(matcher.InstructionEnumeration());
        }
Exemple #22
0
        public static IEnumerable <CodeInstruction> PasteSnap(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher matcher = new CodeMatcher(instructions)
                                  .MatchForward(true,
                                                new CodeMatch(x => x.IsLdloc()),
                                                new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_Click.castGroundPos)))
                                                )
                                  .Advance(1)
                                  .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
                                  .InsertAndAdvance(Transpilers.EmitDelegate <Action <BuildTool_BlueprintPaste> >(tool =>
            {
                float x = tool.blueprint.dragBoxSize_x;
                float y = tool.blueprint.dragBoxSize_y;

                currentGridData.snapGrid = new Vector2(x, y);

                float longitude = 0;
                float latitude  = 0;
                BlueprintUtils.GetLongitudeLatitudeRad(tool.castGroundPos.normalized, ref longitude, ref latitude);

                if (isLockedLongitude)
                {
                    longitude = lockLongitude;
                }
                if (isLockedLatitude)
                {
                    latitude = lockLatitude;
                }

                tool.castGroundPos = BlueprintUtils.GetDir(longitude, latitude) * tool.castGroundPos.magnitude;
            }));

            matcher.MatchForward(false, new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(PlanetAuxData), nameof(PlanetAuxData.Snap))))
            .SetInstruction(new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(GridSnappingPatches), nameof(SnapModified))));

            return(matcher.InstructionEnumeration());
        }
Exemple #23
0
        internal static IEnumerable <CodeInstruction> FindSaveMethod(IEnumerable <CodeInstruction> instructions)
        {
            var btnSaveField = AccessTools.Field(typeof(CustomControl), "btnSave") ?? throw new MissingFieldException("could not find btnSave");
            var patchMethod  = AccessTools.Method(typeof(MakerCardSave), nameof(CardSavePatch)) ?? throw new MissingMethodException("could not find CardSavePatch");

#if KKS
            var cm = new CodeMatcher(instructions);
            var asyncGeneratedField = (FieldInfo)cm.MatchForward(false, new CodeMatch(OpCodes.Stfld)).Instruction.operand;
            var movenext            = asyncGeneratedField.DeclaringType.GetMethod("MoveNext", AccessTools.all);
            if (movenext == null)
            {
                throw new ArgumentNullException(nameof(movenext));
            }

            var ctx = new ILContext(new DynamicMethodDefinition(movenext).Definition);
            var il  = new ILCursor(ctx);
            il.GotoNext(instruction => instruction.MatchLdfld(btnSaveField));
            MethodReference targetMethodReference = null;
            il.GotoNext(instruction => instruction.MatchLdftn(out targetMethodReference));
            if (targetMethodReference == null)
            {
                throw new ArgumentNullException(nameof(targetMethodReference));
            }

            var targetMethod = targetMethodReference.ResolveReflection();
            KoikatuAPI.Logger.LogDebug("Save method found for patching MakerCardSave - " + targetMethod.Name);
            _harmony.Patch(targetMethod, new HarmonyMethod(patchMethod));

            return(instructions);
#else
            var  codes = instructions.ToList();
            bool buttonFound = false, success = false;

            for (int i = 0; i < codes.Count; i++)
            {
                var code = codes[i];

                if (!buttonFound && code.opcode == OpCodes.Ldfld && code.operand as FieldInfo == btnSaveField)
                {
                    buttonFound = true;
                }

                if (buttonFound && code.opcode == OpCodes.Ldftn)
                {
                    if (code.operand is MethodInfo methodInfo)
                    {
                        _harmony.Patch(methodInfo, new HarmonyMethod(patchMethod));
                        KoikatuAPI.Logger.LogDebug("Save method found for patching MakerCardSave - " + methodInfo.Name);
                        success = true;
                    }

                    break;
                }
            }

            if (!success)
            {
                KoikatuAPI.Logger.LogWarning("Could not find save method for patching MakerCardSave");
            }

            return(codes);
#endif
        }
Exemple #24
0
        static IEnumerable <CodeInstruction> AddMoreData(IEnumerable <CodeInstruction> instructions, ILGenerator generator)
        {
            CodeMatcher matcher = new CodeMatcher(instructions, generator)
                                  .MatchForward(true,
                                                new CodeMatch(OpCodes.Ldarg_S),
                                                new CodeMatch(OpCodes.Ldc_I4_0)
                                                ).Advance(3);

            Label contLabel = (Label)matcher.Operand;

            //allow generation when there is no buildings
            matcher.Advance(-3)
            .SetAndAdvance(OpCodes.Pop, null)
            .InsertAndAdvance(Transpilers.EmitDelegate <Func <bool> >(() => BlueprintCopyExtension.isEnabled && BlueprintCopyExtension.reformSelection.Count > 0))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Brtrue, contLabel))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_S, 4))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldc_I4_0));


            //Replace function call to include foundations
            matcher.MatchForward(false,
                                 new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(BlueprintUtils), nameof(BlueprintUtils.GetBoundingRange))))
            .SetInstruction(Transpilers.EmitDelegate <Func <PlanetData, PlanetAuxData, int[], int, float, BPGratBox> >((data, auxData, arg3, i, f) =>
            {
                BlueprintCopyExtension.CopyReforms();
                return(ReformBPUtils.GetBoundingRange(data, auxData, arg3, i, BlueprintCopyExtension.tmpReformList, f));
            }));

            //Add initialization
            matcher.MatchForward(false,
                                 new CodeMatch(OpCodes.Ldarg_0),
                                 new CodeMatch(OpCodes.Ldloc_S),
                                 new CodeMatch(OpCodes.Newarr),
                                 new CodeMatch(OpCodes.Stfld)
                                 ).Advance(1)
            .InsertAndAdvance(Transpilers.EmitDelegate <Action <BlueprintData> >(data =>
            {
                if (BlueprintCopyExtension.isEnabled && !UndoManager.IgnoreAllEvents.Value)
                {
                    data.reforms = new ReformData[BlueprintCopyExtension.reformSelection.Count];

                    int i = 0;
                    foreach (var kv in BlueprintCopyExtension.reformSelection)
                    {
                        data.reforms[i]           = kv.Value;
                        data.reforms[i].areaIndex = -1;
                        i++;
                    }
                }
                else
                {
                    data.reforms = new ReformData[0];
                }
            }))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0));

            //Just to anchor
            matcher.MatchForward(true,
                                 new CodeMatch(OpCodes.Call,
                                               AccessTools.Method(typeof(BlueprintUtils), nameof(BlueprintUtils.GetLongitudeSegmentCount), new[] { typeof(Vector3), typeof(int) })),
                                 new CodeMatch(OpCodes.Stloc_S),
                                 new CodeMatch(OpCodes.Ldloc_S),
                                 new CodeMatch(OpCodes.Ldloc_S),
                                 new CodeMatch(OpCodes.Call,
                                               AccessTools.Method(typeof(BlueprintUtils), nameof(BlueprintUtils.GetLongitudeRadPerGrid), new[] { typeof(int), typeof(int) })));

            // add my code
            matcher.MatchForward(false,
                                 new CodeMatch(OpCodes.Ldc_I4_0),
                                 new CodeMatch(OpCodes.Stloc_S))
            .Advance(1)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 18))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 17))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 7))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 20))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 11))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 19))
            .InsertAndAdvance(Transpilers.EmitDelegate <AddAction>(
                                  (blueprint, bpgratBox, i, array, longitudeRadPerGrid, latitudeRadPerGrid, longitudeSegmentCount) =>
            {
                if (!BlueprintCopyExtension.isEnabled)
                {
                    return;
                }
                if (UndoManager.IgnoreAllEvents.Value)
                {
                    return;
                }

                for (int j = 0; j < BlueprintCopyExtension.reformSelection.Count; j++)
                {
                    if (blueprint.reforms[j].areaIndex >= 0)
                    {
                        continue;
                    }

                    ReformData data = blueprint.reforms[j];

                    if (!(bpgratBox.y - 1E-05f <= data.latitude) || !(data.latitude <= bpgratBox.w + 1E-05f))
                    {
                        continue;
                    }

                    blueprint.reforms[j].areaIndex      = i;
                    blueprint.reforms[j].localLongitude = (data.longitude - array[i].x) / longitudeRadPerGrid;
                    blueprint.reforms[j].localLatitude  = (data.latitude - array[i].y) / latitudeRadPerGrid;

                    if (blueprint.reforms[j].localLongitude < -0.5001f)
                    {
                        blueprint.reforms[j].localLongitude += longitudeSegmentCount * 5;
                    }
                }
            }));

            //Add null check to buildings iteration
            matcher.MatchForward(true,
                                 new CodeMatch(OpCodes.Ldc_I4_0),
                                 new CodeMatch(OpCodes.Newarr),
                                 new CodeMatch(OpCodes.Stfld))
            .Advance(8)
            .CreateLabel(out Label exitLabel)
            .Advance(-2)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Pop))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_2))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Brfalse, exitLabel))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 21));


            return(matcher.InstructionEnumeration());
        }
Exemple #25
0
        private static IEnumerable <CodeInstruction> OnBeltBuilt_Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator iLGenerator)
        {
            /*
             * Calls
             * Multiplayer.Session.Factories.OnNewSetInserterPickTarget(objId, pickTarget, inserterId, offset, pointPos);
             * After
             * this.factorySystem.SetInserterPickTarget(inserterId, num6, num5 - num7);
             */
            CodeMatcher codeMatcher = new CodeMatcher(instructions, iLGenerator)
                                      .MatchForward(true,
                                                    new CodeMatch(i => i.opcode == OpCodes.Callvirt &&
                                                                  ((MethodInfo)i.operand).Name == nameof(FactorySystem.SetInserterPickTarget)
                                                                  )
                                                    );

            if (codeMatcher.IsInvalid)
            {
                NebulaModel.Logger.Log.Error("PlanetFactory_Transpiler.OnBeltBuilt 1 failed. Mod version not compatible with game version.");
                return(instructions);
            }

            List <CodeInstruction> setInserterTargetInsts = codeMatcher.InstructionsWithOffsets(-5, -1); // inserterId, pickTarget, offset
            CodeInstruction        objIdInst     = codeMatcher.InstructionAt(-13);                       // objId
            List <CodeInstruction> pointPosInsts = codeMatcher.InstructionsWithOffsets(8, 10);           // pointPos

            codeMatcher = codeMatcher
                          .Advance(1)
                          .InsertAndAdvance(setInserterTargetInsts.ToArray())
                          .InsertAndAdvance(objIdInst)
                          .InsertAndAdvance(pointPosInsts.ToArray())
                          .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Action <int, int, int, int, UnityEngine.Vector3> >((inserterId, pickTarget, offset, objId, pointPos) =>
            {
                if (Multiplayer.IsActive)
                {
                    Multiplayer.Session.Factories.OnNewSetInserterPickTarget(objId, pickTarget, inserterId, offset, pointPos);
                }
            }));

            /*
             * Calls
             * Multiplayer.Session.Factories.OnNewSetInserterInsertTarget(objId, pickTarget, inserterId, offset, pointPos);
             * After
             * this.factorySystem.SetInserterInsertTarget(inserterId, num9, num5 - num10);
             */
            codeMatcher = codeMatcher
                          .MatchForward(true,
                                        new CodeMatch(i => i.opcode == OpCodes.Callvirt &&
                                                      ((MethodInfo)i.operand).Name == nameof(FactorySystem.SetInserterInsertTarget)
                                                      )
                                        );

            if (codeMatcher.IsInvalid)
            {
                NebulaModel.Logger.Log.Error("PlanetFactory_Transpiler.OnBeltBuilt 2 failed. Mod version not compatible with game version.");
                return(codeMatcher.InstructionEnumeration());
            }

            setInserterTargetInsts = codeMatcher.InstructionsWithOffsets(-5, -1); // inserterId, pickTarget, offset
            objIdInst     = codeMatcher.InstructionAt(-13);                       // objId
            pointPosInsts = codeMatcher.InstructionsWithOffsets(9, 11);           // pointPos

            codeMatcher = codeMatcher
                          .Advance(1)
                          .InsertAndAdvance(setInserterTargetInsts.ToArray())
                          .InsertAndAdvance(objIdInst)
                          .InsertAndAdvance(pointPosInsts.ToArray())
                          .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Action <int, int, int, int, UnityEngine.Vector3> >((inserterId, pickTarget, offset, objId, pointPos) =>
            {
                if (Multiplayer.IsActive)
                {
                    Multiplayer.Session.Factories.OnNewSetInserterInsertTarget(objId, pickTarget, inserterId, offset, pointPos);
                }
            }));

            return(codeMatcher.InstructionEnumeration());
        }
Exemple #26
0
        public static void CheckBuildConditionsWorker(PlayerAction_Build __instance, BuildPreview bp)
        {
            IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
            {
                // modify the original CheckBuildConditions to only operate on a single item (so only the body of the for loop) injecting the BuildPreview passed as argument
                CodeMatcher matcher = new CodeMatcher(instructions)
                                      .MatchForward(false,
                                                    new CodeMatch(OpCodes.Ldc_I4_0),
                                                    new CodeMatch(OpCodes.Stloc_2)
                                                    )
                                      .SetOpcodeAndAdvance(OpCodes.Nop)
                                      .SetOpcodeAndAdvance(OpCodes.Nop)
                                      .SetOpcodeAndAdvance(OpCodes.Nop)
                                      .SetOpcodeAndAdvance(OpCodes.Nop)
                                      .SetOpcodeAndAdvance(OpCodes.Nop)
                                      .SetOpcodeAndAdvance(OpCodes.Nop)
                                      .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Ldarg_1))
                                      .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Stloc_3))
                                      .MatchForward(false,
                                                    new CodeMatch(OpCodes.Ldloc_3),
                                                    new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.desc))),
                                                    new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PrefabDesc), nameof(PrefabDesc.isBelt)))
                                                    )
                                      .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
                                      .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Ldloc_3))
                                      .SetInstructionAndAdvance(Transpilers.EmitDelegate <Func <PlayerAction_Build, BuildPreview, bool> >((actionBuild, buildPreview) =>
                {
                    // remove checks for belts by stating that the current buildPreview is not a belt.
                    if (BlueprintManager.pastedEntities.Count > 0 && buildPreview.desc.isBelt)
                    {
                        // but we have to take care of collision checks
                        Vector3 testPos = buildPreview.lpos + buildPreview.lpos.normalized * 0.3f;
                        if (!buildPreview.ignoreCollider)
                        {
                            actionBuild.GetOverlappedObjectsNonAlloc(testPos, 0.34f, 3f, false);
                            if (actionBuild._overlappedCount > 0)
                            {
                                buildPreview.condition = EBuildCondition.Collide;
                            }

                            actionBuild.GetOverlappedVeinsNonAlloc(testPos, 0.6f, 3f);
                            if (actionBuild._overlappedCount > 0)
                            {
                                buildPreview.condition = EBuildCondition.Collide;
                            }
                        }

                        return(false);
                    }
                    return(buildPreview.desc.isBelt);
                }))
                                      .MatchForward(false,
                                                    new CodeMatch(OpCodes.Ldloc_3),
                                                    new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.condition)))
                                                    )
                                      .Advance(1)
                                      .SetInstructionAndAdvance(Transpilers.EmitDelegate <Func <BuildPreview, bool> >(buildPreview =>
                {
                    // ignore checkbuildconditions for all inserters when copy pasting as we already took care of checking for collisions
                    if (BlueprintManager.pastedEntities.Count > 0 && buildPreview.desc.isInserter)
                    {
                        var actionBuild = GameMain.data.mainPlayer.controller.actionBuild;
                        // only check that we have enough items
                        if (buildPreview.coverObjId == 0 || buildPreview.willCover)
                        {
                            int id  = buildPreview.item.ID;
                            int num = 1;
                            if (actionBuild.tmpInhandId == id && actionBuild.tmpInhandCount > 0)
                            {
                                num = 1;
                                actionBuild.tmpInhandCount--;
                            }
                            else
                            {
                                actionBuild.tmpPackage.TakeTailItems(ref id, ref num, false);
                            }
                            if (num == 0)
                            {
                                buildPreview.condition = EBuildCondition.NotEnoughItem;
                            }
                        }

                        if (buildPreview.condition != EBuildCondition.Ok || !IsInserterConnected(buildPreview))
                        {
                            return(true);
                        }

                        // must calculate correct refCount/refArr to ensure inserter moves has the right speed

                        Vector3 posR = Vector3.zero;
                        bool inputIsBelt;
                        Vector3 inputPos;
                        bool outputIsBelt;
                        Vector3 outputPos;
                        if (buildPreview.input == null)
                        {
                            inputPos    = actionBuild.GetObjectPose(buildPreview.inputObjId).position;
                            inputIsBelt = actionBuild.ObjectIsBelt(buildPreview.inputObjId);
                        }
                        else
                        {
                            inputPos    = buildPreview.input.lpos;
                            inputIsBelt = buildPreview.input.desc.isBelt;
                        }
                        if (buildPreview.output == null)
                        {
                            outputPos    = actionBuild.GetObjectPose(buildPreview.outputObjId).position;
                            outputIsBelt = actionBuild.ObjectIsBelt(buildPreview.outputObjId);
                        }
                        else
                        {
                            outputPos    = buildPreview.output.lpos;
                            outputIsBelt = buildPreview.output.desc.isBelt;
                        }

                        if (inputIsBelt && !outputIsBelt)
                        {
                            posR = outputPos;
                        }
                        else if (!inputIsBelt && outputIsBelt)
                        {
                            posR = inputPos;
                        }
                        else
                        {
                            posR = (inputPos + outputPos) * 0.5f;
                        }
                        float segmentsCount = actionBuild.planetAux.mainGrid.CalcSegmentsAcross(posR, buildPreview.lpos, buildPreview.lpos2);

                        if (!inputIsBelt && !outputIsBelt)
                        {
                            segmentsCount -= 0.3f;
                        }
                        buildPreview.refCount = Mathf.RoundToInt(Mathf.Clamp(segmentsCount, 1f, 3f));
                        buildPreview.refArr   = new int[buildPreview.refCount];

                        return(true);
                    }

                    return(buildPreview.condition != EBuildCondition.Ok);
                }));

                // trim the code just before the for loop condition checks
                int endIdx = matcher
                             .MatchForward(false,
                                           new CodeMatch(OpCodes.Ldloc_3),
                                           new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.condition)))
                                           )
                             .SetOpcodeAndAdvance(OpCodes.Nop).Pos;


                List <CodeInstruction> instructionsList = matcher.InstructionEnumeration().ToList();
                List <CodeInstruction> code             = new List <CodeInstruction>();

                for (int i = 0; i < endIdx; i++)
                {
                    code.Add(instructionsList[i]);
                }
                return(code.AsEnumerable());
            }

            // make compiler happy
            _ = Transpiler(null);
            return;
        }
        static IEnumerable <CodeInstruction> RefreshPreviews(IEnumerable <CodeInstruction> instructions, ILGenerator generator)
        {
            float MirrorBuildingRotation(float yaw, BlueprintBuilding building)
            {
                if (buildingsAxis.ContainsKey(building.modelIndex) && buildingsAxis[building.modelIndex] == MajorAxis.XAXIS)
                {
                    return(MirrorRotation(yaw + 90f) - 90f);
                }

                return(MirrorRotation(yaw));
            }

            CodeMatcher matcher = new CodeMatcher(instructions, generator)
                                  .MatchForward(true,
                                                new CodeMatch(OpCodes.Ldelem, typeof(Vector4)),
                                                new CodeMatch(OpCodes.Stloc_S),
                                                new CodeMatch(OpCodes.Ldloca_S));

            matcher.Advance(1);

            object longAxisVar = matcher.Operand;

            while (matcher.Opcode != OpCodes.Stfld)
            {
                matcher.RemoveInstruction();
            }

            matcher.RemoveInstructions(2);

            object latAxisVar = matcher.Operand;

            while (matcher.Opcode != OpCodes.Stfld)
            {
                matcher.RemoveInstruction();
            }

            matcher.RemoveInstruction()
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, longAxisVar))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, latAxisVar))
            .InsertAndAdvance(Transpilers.EmitDelegate <RefAction>(MirrorArea));

            matcher.MatchForward(false,
                                 new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(BlueprintUtils), nameof(BlueprintUtils.TransitionWidthAndHeight))))
            .Advance(-3)
            .RemoveInstruction().RemoveInstruction().RemoveInstruction()
            .SetInstruction(Transpilers.EmitDelegate <Func <float, BlueprintBuilding, Vector2> >((yaw, building) =>
            {
                float x = building.localOffset_x;
                float y = building.localOffset_y;


                if ((mirrorLat && !mirrorLong || !mirrorLat && mirrorLong) && buildingsOffsets.ContainsKey(building.modelIndex))
                {
                    Vector2 offset   = buildingsOffsets[building.modelIndex];
                    float rotatedYaw = MirrorBuildingRotation(building.yaw, building);

                    offset = offset.Rotate(rotatedYaw + (mirrorLong ? 180 : 0));

                    x += offset.x;
                    y += offset.y;
                }

                return(BlueprintUtils.TransitionWidthAndHeight(yaw, x, y));
            }));

            matcher.MatchForward(false,
                                 new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BlueprintBuilding), nameof(BlueprintBuilding.yaw)))
                                 ).Advance(1)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 30))
            .InsertAndAdvance(Transpilers.EmitDelegate <Func <float, BlueprintBuilding, float> >(MirrorBuildingRotation)).Advance(2);

            matcher.MatchForward(false,
                                 new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BlueprintBuilding), nameof(BlueprintBuilding.yaw2)))
                                 ).Advance(1)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 30))
            .InsertAndAdvance(Transpilers.EmitDelegate <Func <float, BlueprintBuilding, float> >(MirrorBuildingRotation));

            matcher.MatchForward(true,
                                 new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.desc))),
                                 new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PrefabDesc), nameof(PrefabDesc.isInserter))))
            .Advance(2);

            object previewVar2 = matcher.Operand;

            matcher.Advance(1)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, previewVar2))
            .InsertAndAdvance(Transpilers.EmitDelegate <Action <BuildPreview> >(preview =>
            {
                if (preview.input != null && preview.input.desc.slotPoses.Length > preview.inputFromSlot &&
                    !preview.input.desc.isBelt)
                {
                    Quaternion invRot       = Quaternion.Inverse(preview.input.lrot);
                    Vector3 slotPosition    = preview.lpos - preview.input.lpos;
                    slotPosition            = invRot * slotPosition;
                    Quaternion slotRotation = invRot * preview.lrot;


                    for (int i = 0; i < preview.input.desc.slotPoses.Length; i++)
                    {
                        Pose slotPose = preview.input.desc.slotPoses[i];
                        if (!((slotPose.position - slotPosition).sqrMagnitude < 0.1f))
                        {
                            continue;
                        }
                        if (!slotPose.rotation.Approximately(slotRotation))
                        {
                            continue;
                        }
                        if (preview.inputFromSlot == i)
                        {
                            break;
                        }

                        preview.inputFromSlot = i;
                        break;
                    }
                }

                if (preview.output != null && preview.output.desc.slotPoses.Length > preview.outputToSlot &&
                    !preview.output.desc.isBelt)
                {
                    Quaternion invRot       = Quaternion.Inverse(preview.output.lrot);
                    Vector3 slotPosition    = preview.lpos2 - preview.output.lpos;
                    slotPosition            = invRot * slotPosition;
                    Quaternion slotRotation = invRot * (preview.lrot2 * Quaternion.Euler(0f, -180f, 0f));

                    for (int i = 0; i < preview.output.desc.slotPoses.Length; i++)
                    {
                        Pose slotPose = preview.output.desc.slotPoses[i];
                        if (!((slotPose.position - slotPosition).sqrMagnitude < 0.1f))
                        {
                            continue;
                        }
                        if (!slotPose.rotation.Approximately(slotRotation))
                        {
                            continue;
                        }
                        if (preview.outputToSlot == i)
                        {
                            break;
                        }

                        preview.outputToSlot = i;
                        break;
                    }
                }
            }));


            return(matcher.InstructionEnumeration());
        }
        public static IEnumerable <CodeInstruction> _OnLateUpdate_Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher matcher = new CodeMatcher(instructions)
                                  .MatchForward(true,
                                                new CodeMatch(OpCodes.Ldarg_0),
                                                new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIVirtualStarmap), "starPool")),
                                                new CodeMatch(OpCodes.Ldloc_S),
                                                new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_Item"),
                                                new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIVirtualStarmap.StarNode), "nameText")),
                                                new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_gameObject"),
                                                new CodeMatch(OpCodes.Ldloc_S),
                                                new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "SetActive"),
                                                new CodeMatch(OpCodes.Ldloc_S));

            if (matcher.IsInvalid)
            {
                Log.Warn("UIVirtualStarmap transpiler could not find injection point, not patching!");
                return(instructions);
            }

            matcher.Advance(1)
            .SetAndAdvance(OpCodes.Ldloc_2, null)     // change 'if (flag2 && flag)' to 'if (flag2 && pressing)'
            .Advance(2);

            // now remove original logic in this if(){}
            for (int i = 0; i < 39; i++)
            {
                matcher.SetAndAdvance(OpCodes.Nop, null);
            }

            // add own logic
            matcher.InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0),
                                     new CodeInstruction(OpCodes.Ldloc_S, 12),
                                     HarmonyLib.Transpilers.EmitDelegate <ShowSolarsystemDetails>((UIVirtualStarmap starmap, int starIndex) =>
            {
                if (pressSpamProtector)
                {
                    return;
                }
                pressSpamProtector = true;

                if (Multiplayer.Session != null && Multiplayer.Session.IsInLobby && starmap.clickText == "")
                {
                    ClearStarmap(starmap);
                    ShowSolarSystem(starmap, starIndex);
                }
                else if (Multiplayer.Session != null && Multiplayer.Session.IsInLobby && starmap.clickText != "")
                {
                    string[] split = starmap.clickText.Split(' ');
                    int starId     = 0;

                    starId = Convert.ToInt32(split[0]);

                    StarData starData = starmap._galaxyData.StarById(starId); // no increment as we stored the actual id in there
                    if (starData == null || starIndex == 0)                   // starIndex == 0 is the star in the middle, so we need to decrement by 1 below
                    {
                        return;
                    }

                    PlanetData pData = starData.planets[starIndex - 1];
                    if (pData == null)
                    {
                        return;
                    }

                    if (UIRoot.instance.uiGame.planetDetail.planet != null && UIRoot.instance.uiGame.planetDetail.planet.id == pData.id && pData.type != EPlanetType.Gas)
                    {
                        // clicked on planet and details already visible, so set as new birth planet
                        starmap._galaxyData.birthStarId   = starId;
                        starmap._galaxyData.birthPlanetId = pData.id;

                        GameMain.data.galaxy.birthStarId   = starId;
                        GameMain.data.galaxy.birthPlanetId = pData.id;

                        customBirthStar   = starData.id;
                        customBirthPlanet = pData.id;

                        Log.Info($"set birth planet{pData.id} {pData.displayName}");
                        Text text = GameObject.Find("UI Root/Overlay Canvas/Galaxy Select/start-button/start-text").GetComponent <Text>();
                        text.text = $"Start Game at {pData.displayName}";
                        text.horizontalOverflow = HorizontalWrapMode.Overflow;

                        if (pData.data == null)
                        {
                            Button button          = GameObject.Find("UI Root/Overlay Canvas/Galaxy Select/start-button").GetComponent <Button>();
                            button.interactable    = false;
                            EPlanetType planetType = pData.type;
                            pData.type             = EPlanetType.Gas;
                            PlanetModelingManager.genPlanetReqList.Enqueue(pData);
                            pData.onLoaded += (PlanetData planet) =>
                            {
                                pData.type          = planetType;
                                button.interactable = true;
                            };
                        }
                    }

                    starmap.clickText = split[0] + " " + starIndex.ToString();
                    UIRoot.instance.uiGame.SetPlanetDetail(pData);

                    GameObject.Find("UI Root/Overlay Canvas/Galaxy Select/right-group")?.SetActive(false);

                    UIRoot.instance.uiGame.planetDetail.gameObject.SetActive(true);
                    UIRoot.instance.uiGame.planetDetail.gameObject.GetComponent <RectTransform>().parent.gameObject.SetActive(true);
                    UIRoot.instance.uiGame.planetDetail.gameObject.GetComponent <RectTransform>().parent.gameObject.GetComponent <RectTransform>().parent.gameObject.SetActive(true);

                    UIRoot.instance.uiGame.planetDetail._OnUpdate();
                }
            }));

            // change for loop to start at 0 instead of 1
            matcher.Start();
            matcher.MatchForward(true,
                                 new CodeMatch(OpCodes.Stloc_2),
                                 new CodeMatch(OpCodes.Ldarg_0),
                                 new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIVirtualStarmap), "clickText")),
                                 new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "IsNullOrEmpty"),
                                 new CodeMatch(OpCodes.Ldc_I4_0),
                                 new CodeMatch(OpCodes.Ceq),
                                 new CodeMatch(OpCodes.Stloc_3)
                                 )
            .Advance(1)
            .SetInstruction(new CodeInstruction(OpCodes.Ldc_I4_0));

            // mark the correct star as birth point
            matcher.Start();
            matcher.MatchForward(true,
                                 new CodeMatch(OpCodes.Ldc_R4),
                                 new CodeMatch(OpCodes.Stloc_1),
                                 new CodeMatch(OpCodes.Br),
                                 new CodeMatch(OpCodes.Ldloc_S),
                                 new CodeMatch(OpCodes.Stloc_1),
                                 new CodeMatch(OpCodes.Ldloc_S),
                                 new CodeMatch(OpCodes.Stloc_0),
                                 new CodeMatch(OpCodes.Ldloc_S),
                                 new CodeMatch(OpCodes.Brtrue)
                                 )
            .Advance(-1)
            .SetAndAdvance(OpCodes.Nop, null)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 5))
            .Insert(HarmonyLib.Transpilers.EmitDelegate <IsBirthStar>((UIVirtualStarmap starmap, int starIndex) =>
            {
                return(starmap.starPool[starIndex].starData.id != starmap._galaxyData.birthStarId && starmap.starPool[starIndex].starData.id != starmap._galaxyData.birthPlanetId);
            }));

            // listen for general mouse clicks to deselect planet / solar system
            matcher.Start();
            matcher.MatchForward(true,
                                 new CodeMatch(OpCodes.Br),
                                 new CodeMatch(OpCodes.Ldarg_0),
                                 new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIVirtualStarmap), "starPool")),
                                 new CodeMatch(OpCodes.Ldloc_S),
                                 new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_Item"),
                                 new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIVirtualStarmap.StarNode), "active")),
                                 new CodeMatch(OpCodes.Brfalse),
                                 new CodeMatch(OpCodes.Ldloc_S),
                                 new CodeMatch(OpCodes.Ldloc_0),
                                 new CodeMatch(OpCodes.Ceq)
                                 )
            .Advance(3)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_0))
            .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <TrackPlayerClick>((UIVirtualStarmap starmap, int starIndex) =>
            {
                bool pressing = VFInput.rtsConfirm.pressing;
                if ((pressing && !pressSpamProtector) && starIndex == -1)
                {
                    if (starmap.clickText != "" && UIRoot.instance.uiGame.planetDetail.gameObject.activeSelf) // hide planet details
                    {
                        GameObject.Find("UI Root/Overlay Canvas/Galaxy Select/right-group").SetActive(true);
                        UIRoot.instance.uiGame.planetDetail.gameObject.SetActive(false);
                    }
                    else if (starmap.clickText != "" && !UIRoot.instance.uiGame.planetDetail.gameObject.activeSelf) // hide solar system details
                    {
                        starmap.clickText = "";
                        starmap.OnGalaxyDataReset();
                    }
                    pressSpamProtector = true;
                }
            }));

            return(matcher.InstructionEnumeration());
        }
        static IEnumerable <CodeInstruction> UpdateTargets_Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator iL)
        {
            /*
             * Update search for new targets. Do not include targets that are already pending response from server.
             * Change:
             *   if (!this.serving.Contains(num4) && (prebuildPool[i].itemRequired == 0 || prebuildPool[i].itemRequired <= this.player.package.GetItemCount((int)prebuildPool[i].protoId)))
             *
             * To:
             *   if (!this.serving.Contains(num4) && !DroneManager.IsPendingBuildRequest(num4) && (prebuildPool[i].itemRequired == 0 || prebuildPool[i].itemRequired <= this.player.package.GetItemCount((int)prebuildPool[i].protoId)))
             */
            var codeMatcher = new CodeMatcher(instructions, iL)
                              .MatchForward(true,
                                            new CodeMatch(i => i.IsLdarg()),
                                            new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "serving"),
                                            new CodeMatch(i => i.IsLdloc()),
                                            new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "Contains"),
                                            new CodeMatch(OpCodes.Brtrue)
                                            );

            if (codeMatcher.IsInvalid)
            {
                NebulaModel.Logger.Log.Error("MechaDroneLogic_Transpiler.UpdateTargets_Transpiler 1 failed. Mod version not compatible with game version.");
                return(instructions);
            }

            var num4Instruction = codeMatcher.InstructionAt(-2);
            var jumpInstruction = codeMatcher.Instruction;

            codeMatcher = codeMatcher
                          .Advance(1)
                          .InsertAndAdvance(num4Instruction)
                          .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Func <int, bool> >((num4) =>
            {
                return(DroneManager.IsPendingBuildRequest(num4));
            }))
                          .InsertAndAdvance(jumpInstruction);

            /*
             * Make sure targets are only chosen if player is closest to the build preview
             * Change:
             *  if (a.sqrMagnitude > this.sqrMinBuildAlt && sqrMagnitude <= num2)
             * To:
             *  if (DroneManager.AmIClosestPlayer(ref a) && a.sqrMagnitude > this.sqrMinBuildAlt && sqrMagnitude <= num2)
             */
            codeMatcher = codeMatcher
                          .MatchForward(false,
                                        new CodeMatch(i => i.IsLdloc()),
                                        new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "get_sqrMagnitude"),
                                        new CodeMatch(OpCodes.Ldarg_0),
                                        new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "sqrMinBuildAlt"),
                                        new CodeMatch(OpCodes.Ble_Un)
                                        );

            if (codeMatcher.IsInvalid)
            {
                NebulaModel.Logger.Log.Error("MechaDroneLogic_Transpiler.UpdateTargets_Transpiler 2 failed. Mod version not compatible with game version.");
                return(codeMatcher.InstructionEnumeration());
            }

            var aOperand    = codeMatcher.Instruction.operand;
            var jumpOperand = codeMatcher.InstructionAt(4).operand;

            codeMatcher = codeMatcher
                          .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, aOperand))
                          .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Func <Vector3, bool> >((aVar) =>
            {
                return(DroneManager.AmIClosestPlayer(ref aVar));
            }))
                          .InsertAndAdvance(new CodeInstruction(OpCodes.Brfalse, jumpOperand));

            /*
             * Insert
             *  DroneManager.BroadcastDroneOrder(droneId, entityId, stage);
             * After
             *  this.serving.Add(num3);
             */
            codeMatcher = codeMatcher
                          .MatchForward(true,
                                        new CodeMatch(i => i.IsLdarg()),
                                        new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "serving"),
                                        new CodeMatch(i => i.IsLdloc()),
                                        new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "Add"),
                                        new CodeMatch(OpCodes.Pop)
                                        );

            if (codeMatcher.IsInvalid)
            {
                NebulaModel.Logger.Log.Error("MechaDroneLogic_Transpiler.UpdateTargets_Transpiler 3 failed. Mod version not compatible with game version.");
                return(codeMatcher.InstructionEnumeration());
            }

            // The index from drones[j]
            var droneIdInstruction = codeMatcher.InstructionAt(-8);

            // num3 from this.serving.Add(num3);
            var entityIdInstruction = codeMatcher.InstructionAt(-2);

            // drones[j].stage = 1;
            var stageInstruction = new CodeInstruction(OpCodes.Ldc_I4_1);

            return(codeMatcher
                   .Advance(1)
                   .InsertAndAdvance(droneIdInstruction)
                   .InsertAndAdvance(entityIdInstruction)
                   .InsertAndAdvance(stageInstruction)
                   .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Action <int, int, int> >((droneId, entityId, stage) =>
            {
                DroneManager.BroadcastDroneOrder(droneId, entityId, stage);
            }))
                   .InstructionEnumeration());
        }
Exemple #30
0
        public static IEnumerable <CodeInstruction> ShowLockedIconsUpdate(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher matcher = new CodeMatcher(instructions)
                                  .MatchForward(false,
                                                new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(Proto), nameof(Proto.ID))),
                                                new CodeMatch(OpCodes.Stloc_S),
                                                new CodeMatch(OpCodes.Ldloc_0))
                                  .MatchForward(false, new CodeMatch(OpCodes.Ldloc_3))
                                  .Advance(1)
                                  .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_2))
                                  .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
                                  .InsertAndAdvance(Transpilers.EmitDelegate <Func <bool, int, UIBuildMenu, bool> >((result, index, that) =>
            {
                if (result)
                {
                    return(true);
                }
                if (!GameMain.history.TechUnlocked(1001) || index == 9)
                {
                    return(false);
                }

                that.isAnyCategoryUnlocked = true;
                return(true);
            }));


            matcher.MatchForward(false,
                                 new CodeMatch(OpCodes.Ldarg_0),
                                 new CodeMatch(OpCodes.Ldfld),
                                 new CodeMatch(OpCodes.Ldc_I4_1)
                                 )
            .MatchForward(false,
                          new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(GameHistoryData), nameof(GameHistoryData.ItemUnlocked))));

            CodeMatcher matcher2 = matcher.Clone();

            matcher2.MatchForward(false,
                                  new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(UIButton), nameof(UIButton.highlighted))))
            .Advance(1);

            Label label = (Label)matcher2.Operand;

            matcher.Advance(1)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Pop))
            .SetInstruction(new CodeInstruction(OpCodes.Br, label));


            matcher.MatchForward(false,
                                 new CodeMatch(OpCodes.Ldfld),
                                 new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(Component), "get_gameObject")),
                                 new CodeMatch(OpCodes.Ldc_I4_0),
                                 new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(GameObject), nameof(GameObject.SetActive)))
                                 )
            .Advance(5)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 9))
            .InsertAndAdvance(Transpilers.EmitDelegate <Action <UIBuildMenu, int> >(ButtonLogic));

            return(matcher.InstructionEnumeration());
        }