Example #1
0
        void GameLocation_performAction()
        {
            var code = FindCode(
                Instructions.Call_get(typeof(Game1), nameof(Game1.player)),
                Instructions.Ldfld(typeof(Farmer), nameof(Farmer.maxItems)),
                OpCodes.Call,
                Instructions.Ldc_I4_S(36),
                OpCodes.Bge
                );

            code.Extend(
                Instructions.Ldstr("Backpack"),
                OpCodes.Call,
                OpCodes.Br
                );
            var len = code.length;

            code.Append(
                Instructions.Call_get(typeof(Game1), nameof(Game1.player)),
                Instructions.Ldfld(typeof(Farmer), nameof(Farmer.maxItems)),
                code[2],
                Instructions.Ldc_I4_S(48),
                code[4],
                Instructions.Call(GetType(), "clickBackpack"),
                Instructions.Br((Label)code[len - 1].operand)
                );
            code[4] = Instructions.Bge(AttachLabel(code[len]));
        }
Example #2
0
        // Support harvesting of spring onions with scythe
        private void Crop_harvest_support_spring_onion(LocalBuilder var_vector)
        {
            if (config.HarvestMode.SpringOnion == HarvestModeEnum.HAND)
            {
                return;
            }

            // Note: the branch
            //   if (this.forageCrop)
            // refers mainly to the crop spring union.

            // Find the lines:
            var AddItem = FindCode(
                // if (Game1.player.addItemToInventoryBool (@object, false)) {
                Instructions.Call_get(typeof(Game1), nameof(Game1.player)),
                InstructionMatcher.AnyOf( // @object
                    OpCodes.Ldloc_0,
                    OpCodes.Ldloc_1
                    ),
                OpCodes.Ldc_I4_0,
                Instructions.Callvirt(typeof(Farmer), nameof(Farmer.addItemToInventoryBool), typeof(Item), typeof(bool)),
                OpCodes.Brfalse
                );

            var ldarg_0      = Instructions.Ldarg_0();
            var Ldloc_object = AddItem[1];
            var tail         = AttachLabel(AddItem.FindNext(
                                               OpCodes.Ldarg_0,
                                               Instructions.Ldfld(typeof(Crop), nameof(Crop.regrowAfterHarvest))
                                               )[0]);

            // Insert check for harvesting with scythe and act accordingly.
            AddItem.ReplaceJump(0, ldarg_0);
            AddItem.Prepend(
                // if (this.harvestMethod != 0) {
                ldarg_0,
                Instructions.Ldfld(typeof(Crop), nameof(Crop.harvestMethod)),
                Instructions.Call_get(typeof(NetInt), nameof(NetInt.Value)),
                Instructions.Brfalse(AttachLabel(AddItem[0])),
                // Game1.createItemDebris (@object, vector, -1, null, -1)
                Ldloc_object,                     // @object
                Instructions.Ldloc_S(var_vector), // vector
                Instructions.Ldc_I4_M1(),         // -1
                Instructions.Ldnull(),            // null
                Instructions.Ldc_I4_M1(),         // -1
                Instructions.Call(typeof(Game1), nameof(Game1.createItemDebris), typeof(Item), typeof(Vector2), typeof(int), typeof(GameLocation), typeof(int)),
                Instructions.Pop(),               // For SDV 1.4
                // Jump to tail
                Instructions.Br(tail)
                );
        }
Example #3
0
        void Crop_harvest()
        {
            // >>> Fix harvesting of spring onions.

            // Find the line:
            //   if (Game1.player.addItemToInventoryBool (@object, false)) {
            var AddItem = FindCode(
                Instructions.Call_get(typeof(StardewValley.Game1), "player"),
                OpCodes.Ldloc_0,
                OpCodes.Ldc_I4_0,
                Instructions.Callvirt(typeof(StardewValley.Farmer), "addItemToInventoryBool", typeof(StardewValley.Item), typeof(bool)),
                OpCodes.Brfalse
                );

            // Make jumps to the start of AddItem jump to the start of our new code instead.
            var ldarg0 = Instructions.Ldarg_0();

            AddItem.ReplaceJump(0, ldarg0);

            // Insert check for harvesting with scythe and act accordingly.
            AddItem.Prepend(
                // if (this.harvestMethod != 0) {
                ldarg0,
                Instructions.Ldfld(typeof(StardewValley.Crop), "harvestMethod"),
                Instructions.Call_get(typeof(Netcode.NetInt), "Value"), // this.indexOfHarvest
                Instructions.Brfalse(AttachLabel(AddItem[0])),
                // Game1.createObjectDebris (@object.ParentSheetIndex, xTile, yTile, -1, @object.Quality, 1.0, null);
                Instructions.Ldloc_0(),
                Instructions.Callvirt_get(typeof(StardewValley.Item), "ParentSheetIndex"), // @object.ParentSheetIndex
                Instructions.Ldarg_1(),                                                    // xTile
                Instructions.Ldarg_2(),                                                    // yTile
                Instructions.Ldc_I4_M1(),                                                  // -1
                Instructions.Ldloc_0(),
                Instructions.Callvirt_get(typeof(StardewValley.Object), "Quality"),        // @object.Quality
                Instructions.Ldc_R4(1),                                                    // 1.0
                Instructions.Ldnull(),                                                     // null
                Instructions.Call(typeof(StardewValley.Game1), "createObjectDebris", typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(float), typeof(StardewValley.GameLocation)),
                // Game1.player.gainExperience (2, howMuch);
                Instructions.Call_get(typeof(StardewValley.Game1), "player"),
                Instructions.Ldc_I4_2(),
                Instructions.Ldloc_1(),
                Instructions.Callvirt(typeof(StardewValley.Farmer), "gainExperience", typeof(int), typeof(int)),
                // return true
                Instructions.Ldc_I4_1(),
                Instructions.Ret()
                // }
                );

            // >>> Patch code to drop sunflower seeds when harvesting with scythe.
            // Patch is configurable, so it can be disabled in case it breaks in the future.
            if (config.HarvestSeeds)
            {
                // Find tail of harvestMethod==1 branch
                var ScytheBranchTail = FindCode(
                    OpCodes.Ldarg_0,
                    Instructions.Ldfld(typeof(StardewValley.Crop), "harvestMethod"),
                    OpCodes.Call, // Netcode
                    OpCodes.Ldc_I4_1,
                    OpCodes.Bne_Un
                    ).Follow(4);
                // Select starting from the exp code.
                ScytheBranchTail.ExtendBackwards(
                    Instructions.Ldsfld(typeof(StardewValley.Game1), "objectInformation"),
                    OpCodes.Ldarg_0,
                    Instructions.Ldfld(typeof(StardewValley.Crop), "indexOfHarvest"),
                    OpCodes.Call, // Netcode
                    OpCodes.Callvirt,
                    OpCodes.Ldc_I4_1
                    );

                // Monitor.Log(ScytheBranchTail.ToString());
                if (ScytheBranchTail.length > 60)
                {
                    throw new Exception("Too many operations in tail of harvestMethod branch");
                }

                // Find the start of the 'drop sunflower seeds' part.
                var DropSunflowerSeeds = FindCode(
                    OpCodes.Ldarg_0,
                    Instructions.Ldfld(typeof(StardewValley.Crop), "indexOfHarvest"),
                    OpCodes.Call,             // Netcode
                    Instructions.Ldc_I4(421), // 421 = Item ID of Sunflower.
                    OpCodes.Bne_Un
                    );

                // Find the local variable that stores the amount being dropped.
                var DropAmount = DropSunflowerSeeds.FindNext(
                    Instructions.Callvirt(typeof(System.Random), "Next", typeof(int), typeof(int)),
                    OpCodes.Stloc_S
                    );
                // Rewrite the tail of the Scythe harvest branch.
                ScytheBranchTail.Replace(
                    // Set num2 = 0.
                    Instructions.Ldc_I4_0(),
                    Instructions.Stloc_S((LocalBuilder)DropAmount[1].operand),
                    // Jump to the 'drop subflower seeds' part.
                    Instructions.Br(AttachLabel(DropSunflowerSeeds[0]))
                    );
            }
        }
Example #4
0
        /**
         * Patch code to drop sunflower seeds when harvesting with scythe.
         * Patch code to let harvesting with scythe drop only 1 item.
         * The other item drops are handled by the plucking code.
         */
        void Crop_harvest_sunflower_drops(LocalBuilder var_quality)
        {
            // Remove start of loop
            var start_loop = FindCode(
                // for (int i = 0
                OpCodes.Ldc_I4_0,
                OpCodes.Stloc_S,
                OpCodes.Br,
                // junimoHarvester != null
                Instructions.Ldarg_S(4),
                OpCodes.Brfalse
                );

            // Get a reference to the 'i' variable.
            var var_i = start_loop[1].operand as LocalBuilder;

            // Remove the head of the loop.
            start_loop.length = 3;
            start_loop.Remove();

            // Find the start of the 'drop sunflower seeds' part.
            var DropSunflowerSeeds = FindCode(
                OpCodes.Ldarg_0,
                Instructions.Ldfld(typeof(Crop), nameof(Crop.indexOfHarvest)),
                OpCodes.Call,             // Netcode
                Instructions.Ldc_I4(421), // 421 = Item ID of Sunflower.
                OpCodes.Bne_Un
                );

            // Set quality for seeds to 0.
            DropSunflowerSeeds.Append(
                Instructions.Ldc_I4_0(),
                Instructions.Stloc_S(var_quality)
                );

            // Remove end of loop and everything after that until the end of the harvest==1 branch.
            var ScytheBranchTail = FindCode(
                OpCodes.Ldarg_0,
                Instructions.Ldfld(typeof(Crop), nameof(Crop.harvestMethod)),
                OpCodes.Call, // Netcode
                OpCodes.Ldc_I4_1,
                OpCodes.Bne_Un
                ).Follow(4);

            ScytheBranchTail.ExtendBackwards(
                Instructions.Ldloc_S(var_i),
                OpCodes.Ldc_I4_1,
                OpCodes.Add,
                Instructions.Stloc_S(var_i),
                Instructions.Ldloc_S(var_i),
                OpCodes.Ldloc_S, // num2
                OpCodes.Blt
                );

            // Change jump to end of loop into jump to drop sunflower seeds.
            ScytheBranchTail.ReplaceJump(0, DropSunflowerSeeds[0]);

            // Rewrite the tail of the Scythe harvest branch.
            ScytheBranchTail.Replace(
                // Jump to the 'drop subflower seeds' part.
                Instructions.Br(AttachLabel(DropSunflowerSeeds[0]))
                );
        }
Example #5
0
        void Crop_harvest()
        {
            #region Fix vector
            Harmony.CodeInstruction ins = null;
            // Remove line (2x)
            // Vector2 vector = new Vector2 ((float)xTile, (float)yTile);
            for (int i = 0; i < 2; i++)
            {
                var vec = FindCode(
                    OpCodes.Ldloca_S,
                    OpCodes.Ldarg_1,
                    OpCodes.Conv_R4,
                    OpCodes.Ldarg_2,
                    OpCodes.Conv_R4,
                    OpCodes.Call
                    );
                ins = vec[5];
                vec.Remove();
            }

            // Add to begin of function
            // Vector2 vector = new Vector2 ((float)xTile*64., (float)yTile*64.);
            BeginCode().Append(
                Instructions.Ldloca_S(3),
                Instructions.Ldarg_1(),
                Instructions.Conv_R4(),
                Instructions.Ldc_R4(64),
                Instructions.Mul(),
                Instructions.Ldarg_2(),
                Instructions.Conv_R4(),
                Instructions.Ldc_R4(64),
                Instructions.Mul(),
                ins
                );

            // Replace (4x):
            //   from: new Vector2 (vector.X * 64f, vector.Y * 64f)
            //   to:   vector
            for (int i = 0; i < 4; i++)
            {
                FindCode(
                    null,
                    OpCodes.Ldfld,
                    Instructions.Ldc_R4(64),
                    OpCodes.Mul,
                    null,
                    OpCodes.Ldfld,
                    Instructions.Ldc_R4(64),
                    OpCodes.Mul,
                    OpCodes.Newobj
                    ).Replace(
                    Instructions.Ldloc_3() // vector
                    );
            }
            #endregion

            #region Support harvesting of spring onions with scythe
            // Note: the branch
            //   if (this.forageCrop)
            // refers mainly to the crop spring union.

            // Find the lines:
            var AddItem = FindCode(
                // if (Game1.player.addItemToInventoryBool (@object, false)) {
                Instructions.Call_get(typeof(Game1), nameof(Game1.player)),
                OpCodes.Ldloc_0,
                OpCodes.Ldc_I4_0,
                Instructions.Callvirt(typeof(Farmer), nameof(Farmer.addItemToInventoryBool), typeof(Item), typeof(bool)),
                OpCodes.Brfalse
                );

            // Swap the lines (add '*64' to vector) &
            // Insert check for harvesting with scythe and act accordingly.
            AddItem.Prepend(
                // if (this.harvestMethod != 0) {
                Instructions.Ldarg_0(),
                Instructions.Ldfld(typeof(Crop), nameof(Crop.harvestMethod)),
                Instructions.Call_get(typeof(NetInt), nameof(NetInt.Value)),
                Instructions.Brfalse(AttachLabel(AddItem[0])),
                // Game1.createItemDebris (@object, vector, -1, null, -1)
                Instructions.Ldloc_0(),   // @object
                Instructions.Ldloc_3(),   // vector
                Instructions.Ldc_I4_M1(), // -1
                Instructions.Ldnull(),    // null
                Instructions.Ldc_I4_M1(), // -1
                Instructions.Call(typeof(Game1), nameof(Game1.createItemDebris), typeof(Item), typeof(Vector2), typeof(int), typeof(GameLocation), typeof(int)),
                // Game1.player.gainExperience (2, howMuch);
                Instructions.Call_get(typeof(Game1), nameof(Game1.player)),
                Instructions.Ldc_I4_2(),
                Instructions.Ldloc_1(),
                Instructions.Callvirt(typeof(Farmer), nameof(Farmer.gainExperience), typeof(int), typeof(int)),
                // return true
                Instructions.Ldc_I4_1(),
                Instructions.Ret()
                // }
                );
            #endregion

            #region Sunflower drops
            // >>> Patch code to drop sunflower seeds when harvesting with scythe.
            // >>> Patch code to let harvesting with scythe drop only 1 item.
            // >>> The other item drops are handled by the plucking code.

            // Remove start of loop
            var start_loop = FindCode(
                // for (int i = 0
                OpCodes.Ldc_I4_0,
                OpCodes.Stloc_S,
                OpCodes.Br,
                // junimoHarvester != null
                Instructions.Ldarg_S(4),
                OpCodes.Brfalse
                );
            // Get a reference to the 'i' variable.
            var var_i = (LocalBuilder)start_loop[1].operand;
            // Remove the head of the loop.
            start_loop.length = 3;
            start_loop.Remove();

            // Find the start of the 'drop sunflower seeds' part.
            var DropSunflowerSeeds = FindCode(
                OpCodes.Ldarg_0,
                Instructions.Ldfld(typeof(Crop), nameof(Crop.indexOfHarvest)),
                OpCodes.Call,             // Netcode
                Instructions.Ldc_I4(421), // 421 = Item ID of Sunflower.
                OpCodes.Bne_Un
                );
            // Set quality for seeds to 0.
            DropSunflowerSeeds.Append(
                Instructions.Ldc_I4_0(),
                Instructions.Stloc_S(5)
                );

            // Remove end of loop and everything after that until the end of the harvest==1 branch.
            var ScytheBranchTail = FindCode(
                OpCodes.Ldarg_0,
                Instructions.Ldfld(typeof(Crop), nameof(Crop.harvestMethod)),
                OpCodes.Call, // Netcode
                OpCodes.Ldc_I4_1,
                OpCodes.Bne_Un
                ).Follow(4);
            ScytheBranchTail.ExtendBackwards(
                Instructions.Ldloc_S(var_i),
                OpCodes.Ldc_I4_1,
                OpCodes.Add,
                Instructions.Stloc_S(var_i),
                Instructions.Ldloc_S(var_i),
                Instructions.Ldloc_S(4),
                OpCodes.Blt
                );

            // Change jump to end of loop into jump to drop sunflower seeds.
            ScytheBranchTail.ReplaceJump(0, DropSunflowerSeeds[0]);

            // Rewrite the tail of the Scythe harvest branch.
            ScytheBranchTail.Replace(
                // Jump to the 'drop subflower seeds' part.
                Instructions.Br(AttachLabel(DropSunflowerSeeds[0]))
                );
            #endregion

            #region Colored flowers
            // For colored flowers we need to call createItemDebris instead of createObjectDebris
            FindCode(
                // Game1.createObjectDebris (indexOfHarvest, xTile, yTile, -1, num3, 1f, null);
                OpCodes.Ldarg_0,
                OpCodes.Ldfld,
                OpCodes.Call,
                OpCodes.Ldarg_1,
                OpCodes.Ldarg_2,
                OpCodes.Ldc_I4_M1,
                OpCodes.Ldloc_S,
                OpCodes.Ldc_R4,
                OpCodes.Ldnull,
                OpCodes.Call
                ).Replace(
                // var tmp = CreateObject(this, num3);
                Instructions.Ldarg_0(),  // this
                Instructions.Ldloc_S(5), // num3
                Instructions.Call(typeof(ModEntry), nameof(CreateObject), typeof(Crop), typeof(int)),
                // Game1.createItemDebris(tmp, vector, -1, null, -1);
                Instructions.Ldloc_3(),   // vector
                Instructions.Ldc_I4_M1(), // -1
                Instructions.Ldnull(),    // null
                Instructions.Ldc_I4_M1(), // -1
                Instructions.Call(typeof(Game1), nameof(Game1.createItemDebris), typeof(Item), typeof(Vector2), typeof(int), typeof(GameLocation), typeof(int))
                );
            #endregion

            if (config.AllHaveQuality)
            {
                // Patch function calls for additional harvest to pass on the harvest quality.
                FindCode(
                    OpCodes.Ldc_I4_M1,
                    OpCodes.Ldc_I4_0,
                    Instructions.Ldc_R4(1.0f),
                    OpCodes.Ldnull,
                    Instructions.Call(typeof(Game1), nameof(Game1.createObjectDebris), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(float), typeof(GameLocation))
                    )[1] = Instructions.Ldloc_S(5);

                FindCode(
                    OpCodes.Ldc_I4_1,
                    OpCodes.Ldc_I4_0,
                    OpCodes.Ldc_I4_M1,
                    OpCodes.Ldc_I4_0,
                    OpCodes.Newobj,
                    Instructions.Callvirt(typeof(JunimoHarvester), nameof(JunimoHarvester.tryToAddItemToHut), typeof(Item))
                    )[3] = Instructions.Ldloc_S(5);
            }

            if (!config.ScytheHarvestFlowers)
            {
                var lbl = AttachLabel(instructions[0]);
                BeginCode().Append(
                    // if (harvestMethod==1 && programColored) {
                    Instructions.Ldarg_0(),
                    Instructions.Ldfld(typeof(Crop), nameof(Crop.harvestMethod)),
                    Instructions.Call_get(typeof(NetInt), nameof(NetInt.Value)),
                    Instructions.Brfalse(lbl),
                    Instructions.Ldarg_0(),
                    Instructions.Ldfld(typeof(Crop), nameof(Crop.programColored)),
                    Instructions.Call_get(typeof(NetBool), nameof(NetBool.Value)),
                    Instructions.Brfalse(lbl),
                    // return false
                    Instructions.Ldc_I4_0(),
                    Instructions.Ret()
                    // }
                    );
            }
        }
Example #6
0
        // Support harvesting of spring onions with scythe
        private void Crop_harvest_support_spring_onion(LocalBuilder var_vector)
        {
            if (config.HarvestMode.SpringOnion == HarvestModeEnum.HAND)
            {
                return;
            }

            // Note: the branch
            //   if (this.forageCrop)
            // refers mainly to the crop spring union.

            InstructionMatcher addItemToInventoryBool = Instructions.Callvirt(typeof(Farmer), nameof(Farmer.addItemToInventoryBool), typeof(Item), typeof(bool));

            if (helper.ModRegistry.IsLoaded("spacechase0.MoreRings"))
            {
                try {
                    addItemToInventoryBool = InstructionMatcher.AnyOf(
                        addItemToInventoryBool,
                        Instructions.Callvirt(AccessTools.TypeByName("MoreRings.Patches.CropPatcher"), "Farmer_AddItemToInventoryBool", typeof(Farmer), typeof(Item), typeof(bool))
                        );
                } catch (Exception e) {
                    Monitor.Log("The More Rings mod was loaded, but MoreRings.Patches.CropPatcher.Farmer_AddItemToInventoryBool(...) was not found.");
                    LogException(e, LogLevel.Warn);
                }
            }

            // Find the lines:
            InstructionRange AddItem;

            AddItem = FindCode(
                // if (Game1.player.addItemToInventoryBool (@object, false)) {
                Instructions.Call_get(typeof(Game1), nameof(Game1.player)),
                InstructionMatcher.AnyOf( // @object
                    OpCodes.Ldloc_0,
                    OpCodes.Ldloc_1
                    ),
                OpCodes.Ldc_I4_0,
                addItemToInventoryBool,
                OpCodes.Brfalse
                );

            var ldarg_0      = Instructions.Ldarg_0();
            var Ldloc_object = AddItem[1];
            var tail         = AttachLabel(AddItem.FindNext(
                                               OpCodes.Ldarg_0,
                                               Instructions.Ldfld(typeof(Crop), nameof(Crop.regrowAfterHarvest))
                                               )[0]);

            // Insert check for harvesting with scythe and act accordingly.
            AddItem.ReplaceJump(0, ldarg_0);
            AddItem.Prepend(
                // if (this.harvestMethod != 0) {
                ldarg_0,
                Instructions.Ldfld(typeof(Crop), nameof(Crop.harvestMethod)),
                Instructions.Call_get(typeof(NetInt), nameof(NetInt.Value)),
                Instructions.Brfalse(AttachLabel(AddItem[0])),
                // Game1.createItemDebris (@object, vector, -1, null, -1)
                Ldloc_object,                     // @object
                Instructions.Ldloc_S(var_vector), // vector
                Instructions.Ldc_I4_M1(),         // -1
                Instructions.Ldnull(),            // null
                Instructions.Ldc_I4_M1(),         // -1
                Instructions.Call(typeof(Game1), nameof(Game1.createItemDebris), typeof(Item), typeof(Vector2), typeof(int), typeof(GameLocation), typeof(int)),
                Instructions.Pop(),               // For SDV 1.4
                // Jump to tail
                Instructions.Br(tail)
                );
        }