Beispiel #1
0
        /*********
        ** Internal Methods
        *********/
        /// <summary>The prefix for the <see cref="StardewValley.Buildings.Mill.doAction(Microsoft.Xna.Framework.Vector2, StardewValley.Farmer)"/> method.</summary>
        /// <param name="tileLocation">The tile on the mill the player clicked on.</param>
        /// <param name="who">The player who clicked on the mill.</param>
        /// <param name="__instance">The current <see cref="StardewValley.Buildings.Mill"/> instance being patched.</param>
        /// <param name="__result">The return value of the method being patched.</param>
        /// <returns><see langword="false"/>, meaning the original method will not get ran.</returns>
        /// <remarks>This reimplements the original method to add the custom millables.</remarks>
        internal static bool DoActionPrefix(Vector2 tileLocation, Farmer who, Mill __instance, ref bool __result)
        {
            if (__instance.daysOfConstructionLeft <= 0)
            {
                // check if the player is clicking on the part of the mill to input new items
                if (tileLocation.X == __instance.tileX + 1 && tileLocation.Y == __instance.tileY + 1)
                {
                    // ensure player is holding an item
                    if (who != null && who.ActiveObject != null)
                    {
                        var item = who.ActiveObject;

                        // ensure held item is millable
                        var isItemMillable = false;
                        if (ModEntry.Instance.Recipes.Any(recipe => recipe.InputId == item.ParentSheetIndex))
                        {
                            isItemMillable = true;
                        }
                        if (!isItemMillable)
                        {
                            Game1.showRedMessage(Game1.content.LoadString("Strings\\Buildings:CantMill"));
                            __result = false;
                            return(false);
                        }

                        // place held object in mill
                        who.ActiveObject = null;
                        item             = (StardewValley.Object)MillPatch.AddItemToThisInventoryList(item, __instance.input.Value.items, 36);
                        if (item != null)
                        {
                            who.ActiveObject = item;
                            Game1.showRedMessage(Game1.content.LoadString("Strings\\Buildings:MillFull"));
                        }

                        typeof(Mill).GetField("hasLoadedToday", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, true);
                        Game1.playSound("Ship");
                    }
                }

                // check if the player is clicking on the part of the mill to pick up processed items
                else if (tileLocation.X == __instance.tileX + 3 && tileLocation.Y == __instance.tileY + 1)
                {
                    Utility.CollectSingleItemOrShowChestMenu(__instance.output.Value, __instance);
                    __result = true;
                    return(false);
                }
            }

            // call the base doAction method
            // this approach isn't ideal but when using regular reflection and invoking the MethodInfo directly, it would call this patch (instead of the base method) resulting in a stack overflow
            // https://stackoverflow.com/questions/4357729/use-reflection-to-invoke-an-overridden-base-method/14415506#14415506
            var baseMethod      = typeof(Building).GetMethod("doAction", BindingFlags.Public | BindingFlags.Instance);
            var functionPointer = baseMethod.MethodHandle.GetFunctionPointer();
            var function        = (Func <Vector2, Farmer, bool>)Activator.CreateInstance(typeof(Func <Vector2, Farmer, bool>), __instance, functionPointer);

            __result = function(tileLocation, who);
            return(false);
        }
Beispiel #2
0
        /// <summary>The prefix for the <see cref="StardewValley.Buildings.Mill.dayUpdate(int)"/> method.</summary>
        /// <param name="dayOfMonth">The current day of month.</param>
        /// <param name="__instance">The current <see cref="StardewValley.Buildings.Mill"/> instance being patched.</param>
        /// <returns><see langword="false"/>, meaning the original method will not get ran.</returns>
        /// <remarks>This reimplements the original method to add the custom millables.</remarks>
        internal static bool DayUpdatePrefix(int dayOfMonth, Mill __instance)
        {
            // reset hasLoadedToday
            typeof(Mill).GetField("hasLoadedToday", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, false);

            // convert all inputs
            for (int i = __instance.input.Value.items.Count - 1; i >= 0; i--)
            {
                // ensure there's an item in the input slot
                var item = __instance.input.Value.items[i];
                if (item == null)
                {
                    continue;
                }

                // get the recipe output
                var recipe = ModEntry.Instance.Recipes.FirstOrDefault(r => r.InputId == item.ParentSheetIndex);
                if (recipe == null)
                {
                    ModEntry.Instance.Monitor.Log($"Failed to retreive recipe that has an input id of {item.ParentSheetIndex}, mill input will be deleted", LogLevel.Error);
                    __instance.input.Value.items[i] = null;
                }
                var outputObject = new StardewValley.Object(recipe.Output.Id, item.Stack * recipe.Output.Amount);

                // try to add the item to the output
                if (Utility.canItemBeAddedToThisInventoryList(outputObject, __instance.output.Value.items, 36))
                {
                    __instance.input.Value.items[i] = MillPatch.AddItemToThisInventoryList(outputObject, __instance.output.Value.items, 36);
                }
            }

            // call the base dayUpdate method
            // this approach isn't ideal but when using regular reflection and invoking the MethodInfo directly, it would call this patch (instead of the base method) resulting in a stack overflow
            // https://stackoverflow.com/questions/4357729/use-reflection-to-invoke-an-overridden-base-method/14415506#14415506
            var baseMethod      = typeof(Building).GetMethod("dayUpdate", BindingFlags.Public | BindingFlags.Instance);
            var functionPointer = baseMethod.MethodHandle.GetFunctionPointer();
            var function        = (Func <int, bool>)Activator.CreateInstance(typeof(Func <int, bool>), __instance, functionPointer);

            function(dayOfMonth);
            return(false);
        }