예제 #1
0
        static IEnumerable <CodeInstruction> Transpiler_ShowCreateCharacter(IEnumerable <CodeInstruction> instructions)
        {
            var Sequence = new PatchTargetInstructionSet(new List <PatchTargetInstruction>
            {
                new PatchTargetInstruction(OpCodes.Call, CreateCharacter_BuildLibraryManagement)
            });

            bool patched = false;

            foreach (var instruction in instructions)
            {
                if (!patched && Sequence.IsMatchComplete(instruction))
                {
                    instruction.operand = QudUX_BuildLibraryScreen_Show;
                    patched             = true;
                }
                yield return(instruction);
            }

            if (patched)
            {
                PatchHelpers.LogPatchResult("CreateCharacter",
                                            "Patched successfully." /* Revamps the Build Library text UI. */);
            }
            else
            {
                PatchHelpers.LogPatchResult("CreateCharacter",
                                            "Failed. This patch may not be compatible with the current game version. "
                                            + "The revamped Build Library text UI will not be used.");
            }
        }
        static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            var Sequence = new PatchTargetInstructionSet(new List <PatchTargetInstruction>
            {
                new PatchTargetInstruction(OpCodes.Ldstr, "Starting game!")
            });

            bool patched = false;

            foreach (var instruction in instructions)
            {
                if (!patched && Sequence.IsMatchComplete(instruction))
                {
                    yield return(new CodeInstruction(OpCodes.Call, Events_EmbarkEvent));

                    yield return(new CodeInstruction(OpCodes.Call, Events_OnLoadAlwaysEvent));

                    patched = true;
                }
                yield return(instruction);
            }
            if (patched)
            {
                PatchHelpers.LogPatchResult("XRLCore.NewGame",
                                            "Patched successfully." /* Enables an event framework that other QudUX features rely on. */);
            }
            else
            {
                PatchHelpers.LogPatchResult("XRLCore.NewGame",
                                            "Failed. This patch may not be compatible with the current game version. "
                                            + "Custom tiles chosen during character creation won't be properly applied at game start, "
                                            + "and certain other event-based QudUX features might not work as expected.");
            }
        }
예제 #3
0
 static bool Prepare()
 {
     if (!Options.UI.UseQudUXCookMenus)
     {
         PatchHelpers.LogPatchResult("Campfire",
                                     "Skipped. The \"Use revamped cooking menus\" option is disabled.");
         return(false);
     }
     return(true);
 }
        static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            //QudHistoryFactory.NameRuinsSite takes Stat.Random(0, 80) and then if the value is not less than
            //60, it returns "some forgotten ruins" as the name of a ruins site. To patch this function, we
            //will find the "Stat.Random(0, 80)" code, and replace it with "Stat.Random(0, 59)" which will
            //ensure that "some forgotten ruins" is never returned from the function.

            //Specifically, we need to find and overwrite the following sequence of IL instructions
            //	  IL_0321: ldc.i4.0
            //    IL_0322: ldc.i4.s  80
            //    IL_0324: call      int32 XRL.Rules.Stat::Random(int32, int32)

            var ins = new List <CodeInstruction>(instructions);

            //don't apply patch if option is toggled off
            if (!Options.Exploration.RenameRuins)
            {
                PatchHelpers.LogPatchResult("QudHistoryFactory",
                                            "Skipped. The \"Force all ruins to have unique names\" option is disabled.");
                return(ins.AsEnumerable());
            }

            for (var i = 0; i < ins.Count; i++)
            {
                if (ins[i].opcode == OpCodes.Ldc_I4_0 && (i + 2) < ins.Count)
                {
                    if (ins[i + 1].opcode == OpCodes.Ldc_I4_S && Convert.ToInt32(ins[i + 1].operand) == 80)
                    {
                        if (ins[i + 2].opcode == OpCodes.Call)
                        {
                            MethodInfo callMethodInfo = (MethodInfo)(ins[i + 2].operand);
                            if (callMethodInfo.MetadataToken == Stat_Random.MetadataToken)
                            {
                                TranspilePatchApplied = true;
                                PatchHelpers.LogPatchResult("QudHistoryFactory",
                                                            "Patched successfully." /* Removes \"some forgotten ruins\" as a naming option and ensures all ruins have unique names. */);
                                //We have found our target triplet of IL instructions
                                ins[i + 1].operand = 59;    //make the modification
                                return(ins.AsEnumerable()); //return the modified instructions
                            }
                        }
                    }
                }
            }
            PatchHelpers.LogPatchResult("QudHistoryFactory",
                                        "Failed. This patch may not be compatible with the current game version. Ruins may not all have unique names, and \"some forgotten ruins\" may still be used.");
            return(ins.AsEnumerable());
        }
예제 #5
0
        static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            bool patchComplete = false;
            var  ilcodes       = new List <CodeInstruction>(instructions);

            for (int i = 0; i < ilcodes.Count; i++)
            {
                if (PatchHelpers.InstructionsAreEqual(ilcodes[i], TargetInstruction))
                {
                    if (ilcodes[i - 2].opcode == OpCodes.Br)
                    {
                        object jumpTarget = ilcodes[i - 2].operand;
                        List <CodeInstruction> optionSwitch = PatchHelpers.ILBlocks.IfOptionYesJumpTo(Options.Exploration.OptionStrings.DisableMagnets, jumpTarget);

                        //clone and then null out this instruction, preserving the jump label here
                        CodeInstruction shiftedInstruction = ilcodes[i - 1].Clone();
                        ilcodes[i - 1].opcode  = OpCodes.Nop;
                        ilcodes[i - 1].operand = null;

                        //insert our option checking code next after the Nop/label
                        ilcodes.InsertRange(i, optionSwitch);

                        //reinsert the cloned instruction we copied earlier (now without a label)
                        ilcodes.Insert(i + optionSwitch.Count, shiftedInstruction);

                        patchComplete = true;
                        break;
                    }
                }
            }
            if (patchComplete)
            {
                PatchHelpers.LogPatchResult("MagneticPulse",
                                            "Patched successfully." /* Enables option to prevent pulsed field magnets from ripping items from your inventory. */);
            }
            else
            {
                PatchHelpers.LogPatchResult("MagneticPulse",
                                            "Failed. This patch may not be compatible with the current game version. "
                                            + "The option to prevent pulsed field magnets from ripping items from your inventory won't work.");
            }
            return(ilcodes.AsEnumerable());
        }
예제 #6
0
 static bool Prepare(MethodBase original)
 {
     if (original == null)
     {
         return(true);
     }
     if (original.Name == "ShowCreateCharacter" && !Options.UI.UseQudUXBuildLibrary)
     {
         PatchHelpers.LogPatchResult("CreateCharacter",
                                     "Skipped. The \"Use revamped build library\" option is disabled.");
         return(false);
     }
     if (original.Name == "GenerateCharacter" && !Options.UI.UseSpriteMenu)
     {
         PatchHelpers.LogPatchResult("GenerateCharacter",
                                     "Skipped. Adds a menu for choosing your character sprite during character creation.");
         return(false);
     }
     return(true);
 }
 private static void ReportPatchStatus(bool success)
 {
     PatchStatuses.Add(success);
     if (PatchStatuses.Count >= 2)
     {
         int failCount = PatchStatuses.Where(s => s == false).Count();
         if (failCount > 0)
         {
             PatchHelpers.LogPatchResult("GameObject.Move",
                                         $"Failed ({failCount}/2). This patch may not be compatible with the current game version. "
                                         + "Some particle text effects may not be shown when movement is prevented.");
         }
         else
         {
             PatchHelpers.LogPatchResult("GameObject.Move",
                                         "Patched successfully." /* Adds option to show particle text messages when movement is prevented for various reasons. */);
         }
         PatchStatuses.Clear();
     }
 }
        static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            var Sequence1 = new PatchTargetInstructionSet(new List <PatchTargetInstruction>
            {
                new PatchTargetInstruction(OpCodes.Ldstr, " ]}}"),
                new PatchTargetInstruction(OpCodes.Callvirt, ScreenBuffer_Write, 2),
                new PatchTargetInstruction(OpCodes.Pop, 0)
            });

            bool patched = false;

            foreach (var instruction in instructions)
            {
                yield return(instruction);

                if (!patched && Sequence1.IsMatchComplete(instruction))
                {
                    //draw conversation speaker's tile and temporarily suppress any Unity prefab animations beneath it
                    yield return(new CodeInstruction(OpCodes.Ldsfld, ConversationUI__ScreenBuffer)); //_ScreenBuffer

                    yield return(new CodeInstruction(OpCodes.Ldarg_1));                              //Speaker

                    yield return(new CodeInstruction(OpCodes.Call, ConversationUIExtender_DrawConversationSpeakerTile));

                    patched = true;
                }
            }
            if (patched)
            {
                PatchHelpers.LogPatchResult("ConversationUI",
                                            "Patched successfully." /* Adds the speaker's sprite to the title bar of conversation windows. */);
            }
            else
            {
                PatchHelpers.LogPatchResult("ConversationUI",
                                            "Failed. This patch may not be compatible with the current game version. "
                                            + "Sprites won't be added to the title bar of conversation windows.");
            }
        }
예제 #9
0
        static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            var Sequence = new PatchTargetInstructionSet(new List <PatchTargetInstruction>
            {
                new PatchTargetInstruction(OpCodes.Ldstr, "The way is blocked by "),
                new PatchTargetInstruction(OpCodes.Call, IComponent_GameObject_AddPlayerMessage, 11)
            });

            bool patched = false;

            foreach (var instruction in instructions)
            {
                yield return(instruction);

                if (!patched && Sequence.IsMatchComplete(instruction))
                {
                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                    yield return(new CodeInstruction(OpCodes.Callvirt, IPart_get_ParentObject));

                    yield return(new CodeInstruction(OpCodes.Call, ParticleTextMaker_EmitFromPlayerIfBarrierInDifferentZone));

                    patched = true;
                }
            }
            if (patched)
            {
                PatchHelpers.LogPatchResult("Physics.HandleEvent",
                                            "Patched successfully." /* Adds option to show particle text messages when movement to connected zone is prevented. */);
            }
            else
            {
                PatchHelpers.LogPatchResult("Physics.HandleEvent",
                                            "Failed. This patch may not be compatible with the current game version. "
                                            + "Some particle text effects may not be shown when movement is prevented.");
            }
        }
        static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator generator)
        {
            var Sequence1 = new PatchTargetInstructionSet(new List <PatchTargetInstruction>
            {
                new PatchTargetInstruction(OpCodes.Ldc_I4_S, (object)62),
                new PatchTargetInstruction(OpCodes.Ldc_I4_S, (object)24, 0),
                new PatchTargetInstruction(OpCodes.Callvirt, 0), //ScreenBuffer.Goto
                new PatchTargetInstruction(OpCodes.Pop, 0),
                new PatchTargetInstruction(OpCodes.Ldsfld, 0),
                new PatchTargetInstruction(LoadHighScoreDeleteCommandString, 0),
                new PatchTargetInstruction(OpCodes.Ldc_I4_1, 0),
                new PatchTargetInstruction(OpCodes.Callvirt, ScreenBuffer_Write, 0),
                new PatchTargetInstruction(OpCodes.Pop, 0),
            });
            var Sequence2 = new PatchTargetInstructionSet(new List <PatchTargetInstruction>
            {
                new PatchTargetInstruction(OpCodes.Ldsfld), //continues immediately after the previous sequence
                new PatchTargetInstruction(OpCodes.Ldsfld, 0),
                new PatchTargetInstruction(OpCodes.Ldnull, 0),
                new PatchTargetInstruction(OpCodes.Ldc_I4_1, 0),
                new PatchTargetInstruction(OpCodes.Callvirt, TextConsole_DrawBuffer, 0)
            });
            var Sequence3 = new PatchTargetInstructionSet(new List <PatchTargetInstruction>
            {
                new PatchTargetInstruction(IsStoreLocalInstruction),
                new PatchTargetInstruction(IsLoadLocalInstruction, 0),
                new PatchTargetInstruction(OpCodes.Ldc_I4_S, (object)98, 0)
            });

            int  seq     = 1;
            bool patched = false;

            foreach (var instruction in instructions)
            {
                if (seq == 1)
                {
                    if (Sequence1.IsMatchComplete(instruction))
                    {
                        yield return(new CodeInstruction(OpCodes.Ldc_I4_S, 58));

                        yield return(new CodeInstruction(OpCodes.Ldc_I4_S, 23));

                        yield return(Sequence1.MatchedInstructions[2].Clone());

                        yield return(new CodeInstruction(OpCodes.Ldstr, "&Y[&WTab&y - Detailed Stats&Y]"));

                        yield return(Sequence1.MatchedInstructions[6].Clone());

                        yield return(Sequence1.MatchedInstructions[7].Clone());

                        seq++;
                    }
                }
                else if (seq == 2)
                {
                    if (Sequence2.IsMatchComplete(instruction))
                    {
                        seq++;
                    }
                }
                else if (!patched && seq == 3)
                {
                    if (Sequence3.IsMatchComplete(instruction))
                    {
                        //here we are essentially adding:
                        //   if (keys == Keys.Tab)
                        //   {
                        //       EnhancedScoreboardExtender.ShowGameStatsScreen();
                        //       Console.DrawBuffer(Buffer, null, bSkipIfOverlay: true);
                        //   }

                        yield return(new CodeInstruction(OpCodes.Ldc_I4_S, 9)); //Keys.Tab

                        Label newLabel = generator.DefineLabel();
                        yield return(new CodeInstruction(OpCodes.Bne_Un_S, newLabel));

                        yield return(new CodeInstruction(OpCodes.Call, EnhancedScoreboardExtender_ShowGameStatsScreen));

                        //redraw the buffer over the screen we made
                        yield return(Sequence2.MatchedInstructions[0].Clone());

                        yield return(Sequence2.MatchedInstructions[1].Clone());

                        yield return(Sequence2.MatchedInstructions[2].Clone());

                        yield return(Sequence2.MatchedInstructions[3].Clone());

                        yield return(Sequence2.MatchedInstructions[4].Clone());

                        CodeInstruction markedLoadLocal = Sequence3.MatchedInstructions[1].Clone();
                        markedLoadLocal.labels.Add(newLabel);
                        yield return(markedLoadLocal);

                        patched = true;
                    }
                }
                yield return(instruction);
            }

            if (patched)
            {
                PatchHelpers.LogPatchResult("Scores",
                                            "Patched successfully." /* Adds an option to open a detailed game stats menu from the High Scores console UI. */);
            }
            else
            {
                PatchHelpers.LogPatchResult("Scores",
                                            "Failed. This patch may not be compatible with the current game version. "
                                            + "The High Scores text UI won't have an option to open detailed stats.");
            }
        }
예제 #11
0
        static IEnumerable <CodeInstruction> Transpiler_GenerateCharacter(IEnumerable <CodeInstruction> instructions, ILGenerator generator)
        {
            var Sequence1 = new PatchTargetInstructionSet(new List <PatchTargetInstruction>
            {
                new PatchTargetInstruction(OpCodes.Ldstr, "StartingPet"),
                new PatchTargetInstruction(OpCodes.Callvirt, List_GameObjectBlueprint_get_Count, 1),
                new PatchTargetInstruction(OpCodes.Ldsfld, CreateCharacter__Console, 35),
                new PatchTargetInstruction(OpCodes.Ldarg_0, 0),
                new PatchTargetInstruction(OpCodes.Ldnull, 0),
                new PatchTargetInstruction(OpCodes.Ldc_I4_0, 0),
                new PatchTargetInstruction(OpCodes.Callvirt, TextConsole_DrawBuffer, 0)
            });
            var Sequence2 = new PatchTargetInstructionSet(new List <PatchTargetInstruction>
            {
                new PatchTargetInstruction(OpCodes.Ldc_I4_S, (object)84),
                new PatchTargetInstruction(OpCodes.Bne_Un_S, 0),
                new PatchTargetInstruction(OpCodes.Ldstr, "HEY! Try my Caves of Qud character build.\n", 0),
                new PatchTargetInstruction(IsLoadLocalInstruction, 5),
                new PatchTargetInstruction(OpCodes.Ldc_I4_S, (object)83)
            });

            int  seq     = 1;
            bool patched = false;

            foreach (var instruction in instructions)
            {
                if (seq == 1)
                {
                    if (Sequence1.IsMatchComplete(instruction))
                    {
                        yield return(Sequence1.MatchedInstructions[3].Clone());

                        yield return(new CodeInstruction(OpCodes.Call, CreateCharacterExtender_WriteCharCreateSpriteOptionText));

                        seq++;
                    }
                }
                else if (!patched && seq == 2)
                {
                    if (Sequence2.IsMatchComplete(instruction))
                    {
                        //here we are essentially adding:
                        //   if (keys == Keys.M)
                        //   {
                        //       CreateCharacterExtender.PickCharacterTile();
                        //       _Console.DrawBuffer(SB);
                        //   }

                        yield return(new CodeInstruction(OpCodes.Ldc_I4_S, 77)); //Keys.M

                        Label newLabel = generator.DefineLabel();
                        yield return(new CodeInstruction(OpCodes.Bne_Un_S, newLabel));

                        yield return(new CodeInstruction(OpCodes.Ldsfld, CreateCharacter_Template));

                        yield return(new CodeInstruction(OpCodes.Call, CreateCharacterExtender_PickCharacterTile));

                        //redraw the buffer over the screen we made
                        yield return(Sequence1.MatchedInstructions[2].Clone());

                        yield return(Sequence1.MatchedInstructions[3].Clone());

                        yield return(Sequence1.MatchedInstructions[4].Clone());

                        yield return(Sequence1.MatchedInstructions[5].Clone());

                        yield return(Sequence1.MatchedInstructions[6].Clone());

                        CodeInstruction markedLoadLocal = Sequence2.MatchedInstructions[3].Clone();
                        markedLoadLocal.labels.Add(newLabel);
                        yield return(markedLoadLocal);

                        patched = true;
                    }
                }
                yield return(instruction);
            }

            if (patched)
            {
                PatchHelpers.LogPatchResult("GenerateCharacter",
                                            "Patched successfully." /* Adds a menu for choosing your character sprite during character creation. */);
            }
            else
            {
                PatchHelpers.LogPatchResult("GenerateCharacter",
                                            "Failed. This patch may not be compatible with the current game version. "
                                            + "The character sprite customization menu won't be available during character creation.");
            }
        }
예제 #12
0
        static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            int             idx          = 0;
            int             gap          = 0;
            int             patchSegment = 1;
            bool            found        = false;
            CodeInstruction instruction_LoadLocal_AbilityNodeArray = null;
            CodeInstruction instruction_LoadLocal_AbilityNodeIndex = null;
            CodeInstruction instruction_LoadLocal_TextBlock        = null;
            bool            patchComplete = false;

            foreach (var instruction in instructions)
            {
                if (found)
                {
                    if (patchSegment == 1)
                    {
                        if (idx == 0 && instruction.opcode == OpCodes.Brfalse_S)
                        {
                            idx++;
                            //do nothing, we're just confirming the expected instruction is here
                        }
                        else if (idx == 1 && PatchHelpers.IsLoadLocalInstruction(instruction))
                        {
                            instruction_LoadLocal_AbilityNodeArray = instruction.Clone();
                            idx++;
                        }
                        else if (idx == 2 && PatchHelpers.IsLoadLocalInstruction(instruction))
                        {
                            instruction_LoadLocal_AbilityNodeIndex = instruction.Clone();
                            found = false;
                            idx   = 0;
                            patchSegment++;
                        }
                    }
                    else if (!patchComplete)
                    {
                        if (idx == 0 && instruction.opcode == OpCodes.Stloc_S)
                        {
                            idx++;
                            instruction_LoadLocal_TextBlock = new CodeInstruction(OpCodes.Ldloc_S, instruction.operand);
                        }
                        else if (idx == 1)
                        {
                            //inject our patch code
                            //get the current AbilityNode item from the array and load it onto the stack
                            yield return(instruction_LoadLocal_AbilityNodeArray);

                            yield return(instruction_LoadLocal_AbilityNodeIndex);

                            yield return(new CodeInstruction(OpCodes.Callvirt, List_AbilityNode_get_Item));

                            //load the TextBlock onto the stack before the game uses it to write to the screen
                            yield return(instruction_LoadLocal_TextBlock);

                            //call our custom function to update the text block
                            yield return(new CodeInstruction(OpCodes.Call, AbilityManagerExtender_UpdateAbilityText));

                            PatchHelpers.LogPatchResult("AbilityManager",
                                                        "Patched successfully." /* Improves activated ability descriptions and cooldown information on the Manage Abilities screen. */);
                            patchComplete = true;
                        }
                    }
                }
                else if (patchSegment == 1)
                {
                    if (PatchHelpers.InstructionsAreEqual(instruction, FirstSegmentTargetInstructions[idx]))
                    {
                        if (++idx == FirstSegmentTargetInstructions.Count())
                        {
                            found = true;
                            idx   = 0;
                        }
                        gap = 0;
                    }
                    else
                    {
                        if (++gap > AllowedInstructionDistance)
                        {
                            idx = 0;
                        }
                    }
                }
                else if (patchSegment == 2)
                {
                    if (PatchHelpers.InstructionsAreEqual(instruction, SecondSegmentTargetInstruction))
                    {
                        found = true;
                    }
                }
                yield return(instruction);
            }
            if (patchComplete == false)
            {
                PatchHelpers.LogPatchResult("AbilityManager",
                                            "Failed. This patch may not be compatible with the current game version. Improved activated ability descriptions and cooldown information won't be added to the Manage Abilities screen.");
            }
        }
예제 #13
0
        static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            int    patchSegment                  = 1;
            int    patchSegment1_stloc_ct        = 0;
            object ingredientList_LocalVarIndex  = null;
            object ingredientBools_LocalVarIndex = null;
            object recipeList_LocalVarIndex      = null;
            int    idx           = 0;
            int    gap           = 0;
            bool   found         = false;
            bool   patchComplete = false;

            foreach (var instruction in instructions)
            {
                if (found)
                {
                    if (patchSegment == 1)
                    {
                        if (instruction.opcode == OpCodes.Stloc_S)
                        {
                            patchSegment1_stloc_ct++;
                        }
                        if (patchSegment1_stloc_ct == 2)
                        {
                            //save the local variable index of the ingredient list
                            ingredientList_LocalVarIndex = instruction.operand;
                            patchSegment++;
                            found = false;
                        }
                    }
                    else if (patchSegment == 2)
                    {
                        if (instruction.opcode == OpCodes.Stloc_S)
                        {
                            ingredientBools_LocalVarIndex = instruction.operand;
                            patchSegment++;
                            found = false;
                        }
                    }
                    else if (patchSegment == 3)
                    {
                        //ignore all the lines that push stuff onto the stack for Popup.ShowOptionList
                        if (!PatchHelpers.InstructionsAreEqual(instruction, ThirdSegmentFinalInstruction))
                        {
                            continue;
                        }
                        //replace the call to Popup.ShowOptionList with our custom ingredient selection menu
                        yield return(new CodeInstruction(OpCodes.Ldloc_S, ingredientList_LocalVarIndex));

                        yield return(new CodeInstruction(OpCodes.Ldloc_S, ingredientBools_LocalVarIndex));

                        yield return(new CodeInstruction(OpCodes.Call, QudUX_IngredientSelectionScreen_Static_Show));

                        patchSegment++;
                        found = false;
                        continue;
                    }
                    else if (patchSegment == 4)
                    {
                        //unused
                    }
                    else if (patchSegment == 5)
                    {
                        if (recipeList_LocalVarIndex == null && instruction.opcode == OpCodes.Ldloc_S)
                        {
                            //grab the recipe list variable, we'll need it below
                            recipeList_LocalVarIndex = instruction.operand;
                        }
                        else if (PatchHelpers.InstructionsAreEqual(instruction, FifthSegmentFinalInstruction))
                        {
                            //replace the call to Popup.ShowOptionList with our custom recipe selection menu
                            yield return(new CodeInstruction(OpCodes.Ldloc_S, recipeList_LocalVarIndex));

                            yield return(new CodeInstruction(OpCodes.Call, QudUX_RecipeSelectionScreen_Static_Show));

                            patchSegment++;
                            found = false;
                        }
                        continue;
                    }
                    else if (!patchComplete)
                    {
                        if (PatchHelpers.InstructionsAreEqual(instruction, SixthSegmentFinalInstruction))
                        {
                            patchComplete = true;
                            PatchHelpers.LogPatchResult("Campfire",
                                                        "Patched successfully." /* Adds completely new UI screens for ingredient- and recipe-based cooking. */);
                            //allow this instruction to fall through, we want it and everything after it.
                        }
                        else
                        {
                            //null out various instructions (several of them are used as labels, so can't just ignore them)
                            instruction.opcode  = OpCodes.Nop;
                            instruction.operand = null;
                        }
                    }
                }
                else if (patchSegment == 1)
                {
                    if (PatchHelpers.InstructionsAreEqual(instruction, FirstSegmentTargetInstruction))
                    {
                        found = true;
                        idx   = 0;
                    }
                }
                else if (patchSegment == 2)
                {
                    if (PatchHelpers.InstructionsAreEqual(instruction, SecondSegmentTargetInstructions[idx]))
                    {
                        if (++idx == SecondSegmentTargetInstructions.Count())
                        {
                            found = true;
                            idx   = 0;
                        }
                        gap = 0;
                    }
                    else
                    {
                        if (++gap > AllowedInstructionDistance)
                        {
                            idx = 0;
                        }
                    }
                }
                else if (patchSegment == 3)
                {
                    if (PatchHelpers.InstructionsAreEqual(instruction, ThirdSegmentTargetInstructions[idx]))
                    {
                        if (++idx == ThirdSegmentTargetInstructions.Count())
                        {
                            found = true;
                            instruction.opcode  = OpCodes.Nop; //null out this instruction (can't remove it because it's used as a label)
                            instruction.operand = null;
                            idx = 0;
                        }
                        gap = 0;
                    }
                    else
                    {
                        if (++gap > AllowedInstructionDistance)
                        {
                            idx = 0;
                        }
                    }
                }
                else if (patchSegment == 4)
                {
                    if (idx == 0)
                    {
                        if (PatchHelpers.InstructionsAreEqual(instruction, FourthSegmentTarget1_Instruction))
                        {
                            idx++;
                        }
                    }
                    else if (idx == 1)
                    {
                        if (instruction.opcode == FourthSegmentTarget2_OpCodeOnly)
                        {
                            idx++;
                        }
                        else
                        {
                            idx = 0;
                        }
                    }
                    else if (idx == 2)
                    {
                        if (!PatchHelpers.InstructionsAreEqual(instruction, FourthSegmentTarget3_Instruction))
                        {
                            if (++gap > FourthSegmentAllowedInstructionDistance)
                            {
                                idx = 0;
                                gap = 0;
                            }
                        }
                        else
                        {
                            instruction.opcode = OpCodes.Ldc_I4_1; //modify to set flag to true instead of false, so that recipes without ingredients on hand aren't hidden from the array
                            patchSegment++;
                            idx = 0;
                            gap = 0;
                        }
                    }
                }
                else if (patchSegment == 5)
                {
                    if (PatchHelpers.InstructionsAreEqual(instruction, FifthSegmentTargetInstruction))
                    {
                        found = true;
                        instruction.opcode  = OpCodes.Nop; //null out this instruction (can't remove it because it's used as a label)
                        instruction.operand = null;
                    }
                }
                else if (patchSegment == 6)
                {
                    if (PatchHelpers.InstructionsAreEqual(instruction, SixthSegmentTargetInstruction))
                    {
                        found = true;
                        instruction.opcode  = OpCodes.Nop;
                        instruction.operand = null;
                    }
                }
                yield return(instruction);
            }
            if (patchComplete == false)
            {
                PatchHelpers.LogPatchResult("Campfire",
                                            "Failed. This patch may not be compatible with the current game version. "
                                            + "The game's default cooking UI pop-ups will be used instead of QudUX's revamped screens.");
            }
        }
        static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            var Sequence1 = new PatchTargetInstructionSet(new List <PatchTargetInstruction>
            {
                new PatchTargetInstruction(OpCodes.Ldstr, "CmdWalk"),
                new PatchTargetInstruction(OpCodes.Ldstr, " | {{hotkey|", 8),
                new PatchTargetInstruction(OpCodes.Ldsfld, Look_Buffer, 30),
                new PatchTargetInstruction(OpCodes.Ldloc_S, 0),
                new PatchTargetInstruction(OpCodes.Ldloc_S, 1),
                new PatchTargetInstruction(OpCodes.Callvirt, ScreenBuffer_WriteAt, 1),
                new PatchTargetInstruction(OpCodes.Pop, 0),
                new PatchTargetInstruction(OpCodes.Ldloc_S, 0),
                new PatchTargetInstruction(OpCodes.Brfalse, 0),
                new PatchTargetInstruction(OpCodes.Ldloc_S, 0),
                new PatchTargetInstruction(OpCodes.Brfalse, 0)
            });
            var Sequence2 = new PatchTargetInstructionSet(new List <PatchTargetInstruction>
            {
                new PatchTargetInstruction(OpCodes.Ldloc_S),
                new PatchTargetInstruction(OpCodes.Ldc_I4_S, (object)101, 0),
                new PatchTargetInstruction(OpCodes.Beq_S, 0),
                new PatchTargetInstruction(OpCodes.Ldloc_S, 0),
                new PatchTargetInstruction(OpCodes.Ldc_I4_S, (object)27, 0),
                new PatchTargetInstruction(OpCodes.Bne_Un_S, 0),
                new PatchTargetInstruction(OpCodes.Ldc_I4_1, 0),
                new PatchTargetInstruction(OpCodes.Stloc_1, 0),
                new PatchTargetInstruction(OpCodes.Ldloc_S, 0)
            });

            int  seq     = 1;
            bool patched = false;

            foreach (var instruction in instructions)
            {
                if (seq == 1)
                {
                    if (Sequence1.IsMatchComplete(instruction))
                    {
                        yield return(instruction);

                        yield return(Sequence1.MatchedInstructions[2].Clone()); //load ScreenBuffer

                        yield return(Sequence1.MatchedInstructions[7].Clone()); //load target GameObject

                        yield return(Sequence1.MatchedInstructions[4].Clone()); //load Look UI hotkey string

                        yield return(new CodeInstruction(OpCodes.Call, LookExtender_AddMarkLegendaryOptionToLooker));

                        seq++;
                        continue;
                    }
                }
                else if (!patched)
                {
                    if (Sequence2.IsMatchComplete(instruction))
                    {
                        CodeInstruction replacementInstruction = instruction.Clone();
                        instruction.opcode  = Sequence2.MatchedInstructions[0].opcode;
                        instruction.operand = Sequence2.MatchedInstructions[0].operand;
                        yield return(instruction);                                                   //load pressed key onto stack (with existing label)

                        yield return(Sequence1.MatchedInstructions[7].Clone());                      //load target GameObject

                        yield return(new CodeInstruction(OpCodes.Ldloc_1));                          //load flag on the stack

                        yield return(new CodeInstruction(OpCodes.Call, LookExtender_CheckKeyPress)); //call our method, which puts a bool on stack

                        yield return(new CodeInstruction(OpCodes.Stloc_1));                          //store bool into flag

                        yield return(replacementInstruction);                                        //return original instruction (without label)

                        patched = true;
                        continue;
                    }
                }
                yield return(instruction);
            }
            if (patched)
            {
                PatchHelpers.LogPatchResult("Look",
                                            "Patched successfully." /* Adds option to mark legendary creature locations in the journal from the Look UI. */);
            }
            else
            {
                PatchHelpers.LogPatchResult("Look",
                                            "Failed. This patch may not be compatible with the current game version. "
                                            + "The option to mark legendary creature locations in journal won't be available from the Look UI.");
            }
        }