/********* ** 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); }
/// <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); }