diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index f2db5c8..25f5091 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -80,7 +80,7 @@ namespace stardew_access harmony.Patch( original: AccessTools.Method(typeof(CraftingPage), nameof(CraftingPage.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.CraftingPagePatch)) + postfix: new HarmonyMethod(typeof(CraftingPagePatch), nameof(CraftingPagePatch.DrawPatch)) ); harmony.Patch( diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index 2afb84b..027802d 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -9,13 +9,10 @@ namespace stardew_access.Patches { internal static string hoveredItemQueryKey = ""; internal static string gameMenuQueryKey = ""; - internal static string craftingPageQueryKey = ""; internal static string inventoryPageQueryKey = ""; internal static string exitPageQueryKey = ""; internal static string optionsPageQueryKey = ""; internal static string profilePageQuery = ""; - internal static int currentSelectedCraftingRecipe = -1; - internal static bool isSelectingRecipe = false; internal static void GameMenuPatch(GameMenu __instance) { @@ -48,237 +45,6 @@ namespace stardew_access.Patches } } - internal static void CraftingPagePatch(CraftingPage __instance, CraftingRecipe ___hoverRecipe, int ___currentCraftingPage) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - - if (MainClass.Config.SnapToFirstInventorySlotKey.JustPressed() && __instance.inventory.inventory.Count > 0) - { - // snap to first inventory slot - __instance.setCurrentlySnappedComponentTo(__instance.inventory.inventory[0].myID); - __instance.inventory.inventory[0].snapMouseCursorToCenter(); - currentSelectedCraftingRecipe = -1; - } - else if (MainClass.Config.SnapToFirstSecondaryInventorySlotKey.JustPressed() && __instance.pagesOfCraftingRecipes[___currentCraftingPage].Count > 0) - { - // snap to first crafting recipe - __instance.setCurrentlySnappedComponentTo(__instance.pagesOfCraftingRecipes[___currentCraftingPage].ElementAt(0).Key.myID); - __instance.pagesOfCraftingRecipes[___currentCraftingPage].ElementAt(0).Key.snapMouseCursorToCenter(); - currentSelectedCraftingRecipe = 0; - } - else if (MainClass.Config.CraftingMenuCycleThroughRecipiesKey.JustPressed() && !isSelectingRecipe) - { - isSelectingRecipe = true; - CycleThroughRecipies(__instance.pagesOfCraftingRecipes, ___currentCraftingPage, __instance); - Task.Delay(200).ContinueWith(_ => { isSelectingRecipe = false; }); - } - - #region Narrate buttons in the menu - if (__instance.upButton != null && __instance.upButton.containsPoint(x, y)) - { - string toSpeak = "Previous Recipe List"; - if (craftingPageQueryKey != toSpeak) - { - craftingPageQueryKey = toSpeak; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.downButton != null && __instance.downButton.containsPoint(x, y)) - { - string toSpeak = "Next Recipe List"; - if (craftingPageQueryKey != toSpeak) - { - craftingPageQueryKey = toSpeak; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.trashCan.containsPoint(x, y)) - { - string toSpeak = "Trash Can"; - if (craftingPageQueryKey != toSpeak) - { - craftingPageQueryKey = toSpeak; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.dropItemInvisibleButton.containsPoint(x, y)) - { - string toSpeak = "Drop Item"; - if (craftingPageQueryKey != toSpeak) - { - craftingPageQueryKey = toSpeak; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - Game1.playSound("drop_item"); - } - return; - } - #endregion - - #region Narrate hovered recipe - if (___hoverRecipe != null) - { - string name = ___hoverRecipe.DisplayName; - int numberOfProduce = ___hoverRecipe.numberProducedPerCraft; - string description = ""; - string ingredients = ""; - string buffs = ""; - string craftable = ""; - - description = $"Description:\n{___hoverRecipe.description}"; - craftable = ___hoverRecipe.doesFarmerHaveIngredientsInInventory(getContainerContents(__instance._materialContainers)) ? "Craftable" : "Not Craftable"; - - #region Crafting ingredients - ingredients = "Ingredients:\n"; - for (int i = 0; i < ___hoverRecipe.recipeList.Count; i++) - { - int recipeCount = ___hoverRecipe.recipeList.ElementAt(i).Value; - int recipeItem = ___hoverRecipe.recipeList.ElementAt(i).Key; - string recipeName = ___hoverRecipe.getNameFromIndex(recipeItem); - - ingredients += $" ,{recipeCount} {recipeName}"; - } - #endregion - - #region Health & stamina and buff items (effects like +1 walking speed) - Item producesItem = ___hoverRecipe.createItem(); - if (producesItem is StardewValley.Object producesItemObject) - { - if (producesItemObject.Edibility != -300) - { - int stamina_recovery = producesItemObject.staminaRecoveredOnConsumption(); - buffs += $"{stamina_recovery} Energy"; - if (stamina_recovery >= 0) - { - int health_recovery = producesItemObject.healthRecoveredOnConsumption(); - buffs += $"\n{health_recovery} Health"; - } - } - // These variables are taken from the game's code itself (IClickableMenu.cs -> 1016 line) - bool edibleItem = producesItem != null && (int)producesItemObject.Edibility != -300; - string[]? buffIconsToDisplay = (producesItem != null && edibleItem && Game1.objectInformation[producesItemObject.ParentSheetIndex].Split('/').Length > 7) - ? producesItem.ModifyItemBuffs(Game1.objectInformation[producesItemObject.ParentSheetIndex].Split('/')[7].Split(' ')) - : null; - - if (buffIconsToDisplay != null) - { - for (int j = 0; j < buffIconsToDisplay.Length; j++) - { - string buffName = ((Convert.ToInt32(buffIconsToDisplay[j]) > 0) ? "+" : "") + buffIconsToDisplay[j] + " "; - if (j <= 11) - { - buffName = Game1.content.LoadString("Strings\\UI:ItemHover_Buff" + j, buffName); - } - try - { - int count = int.Parse(buffName.Substring(0, buffName.IndexOf(' '))); - if (count != 0) - buffs += $"{buffName}\n"; - } - catch (Exception) { } - } - - buffs = $"Buffs and boosts:\n {buffs}"; - } - } - #endregion - - - string toSpeak = $"{numberOfProduce} {name}, {craftable}, \n\t{ingredients}, \n\t{description} \n\t{buffs}"; - - if (craftingPageQueryKey != toSpeak) - { - craftingPageQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - else - { - var isRecipeInFocus = false; - foreach (var item in __instance.pagesOfCraftingRecipes[___currentCraftingPage]) - { - if (item.Key.containsPoint(x, y)) - { - isRecipeInFocus = true; - break; - } - } - - if (isRecipeInFocus) - { - string query = $"unknown recipe:{__instance.getCurrentlySnappedComponent().myID}"; - - if (craftingPageQueryKey != query) - { - craftingPageQueryKey = query; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say("unknown recipe", true); - } - return; - } - } - #endregion - - #region Narrate hovered item - if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) - { - gameMenuQueryKey = ""; - craftingPageQueryKey = ""; - return; - } - #endregion - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - private static void CycleThroughRecipies(List> pagesOfCraftingRecipes, int ___currentCraftingPage, CraftingPage __instance) - { - currentSelectedCraftingRecipe++; - if (currentSelectedCraftingRecipe < 0 || currentSelectedCraftingRecipe >= pagesOfCraftingRecipes[0].Count) - currentSelectedCraftingRecipe = 0; - - __instance.setCurrentlySnappedComponentTo(pagesOfCraftingRecipes[___currentCraftingPage].ElementAt(currentSelectedCraftingRecipe).Key.myID); - pagesOfCraftingRecipes[___currentCraftingPage].ElementAt(currentSelectedCraftingRecipe).Key.snapMouseCursorToCenter(); - - // Skip if recipe is not unlocked/unknown - if (pagesOfCraftingRecipes[___currentCraftingPage].ElementAt(currentSelectedCraftingRecipe).Key.hoverText.Equals("ghosted")) - CycleThroughRecipies(pagesOfCraftingRecipes, ___currentCraftingPage, __instance); - } - - // This method is used to get the inventory items to check if the player has enough ingredients for a recipe - // Taken from CraftingPage.cs -> 169 line - internal static IList? getContainerContents(List materialContainers) - { - if (materialContainers == null) - { - return null; - } - List items = new List(); - for (int i = 0; i < materialContainers.Count; i++) - { - items.AddRange(materialContainers[i].items); - } - return items; - } - internal static void InventoryPagePatch(InventoryPage __instance) { try diff --git a/stardew-access/Patches/GameMenuPatches/CraftingPagePatch.cs b/stardew-access/Patches/GameMenuPatches/CraftingPagePatch.cs new file mode 100644 index 0000000..51db2e5 --- /dev/null +++ b/stardew-access/Patches/GameMenuPatches/CraftingPagePatch.cs @@ -0,0 +1,250 @@ +using StardewValley; +using StardewValley.Menus; +using StardewValley.Objects; + +namespace stardew_access.Patches +{ + internal class CraftingPagePatch + { + internal static string hoveredItemQueryKey = ""; + internal static string craftingPageQueryKey = ""; + internal static int currentSelectedCraftingRecipe = -1; + internal static bool isSelectingRecipe = false; + + internal static void DrawPatch(CraftingPage __instance, CraftingRecipe ___hoverRecipe, int ___currentCraftingPage) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + if (MainClass.Config.SnapToFirstInventorySlotKey.JustPressed() && __instance.inventory.inventory.Count > 0) + { + // snap to first inventory slot + __instance.setCurrentlySnappedComponentTo(__instance.inventory.inventory[0].myID); + __instance.inventory.inventory[0].snapMouseCursorToCenter(); + currentSelectedCraftingRecipe = -1; + } + else if (MainClass.Config.SnapToFirstSecondaryInventorySlotKey.JustPressed() && __instance.pagesOfCraftingRecipes[___currentCraftingPage].Count > 0) + { + // snap to first crafting recipe + __instance.setCurrentlySnappedComponentTo(__instance.pagesOfCraftingRecipes[___currentCraftingPage].ElementAt(0).Key.myID); + __instance.pagesOfCraftingRecipes[___currentCraftingPage].ElementAt(0).Key.snapMouseCursorToCenter(); + currentSelectedCraftingRecipe = 0; + } + else if (MainClass.Config.CraftingMenuCycleThroughRecipiesKey.JustPressed() && !isSelectingRecipe) + { + isSelectingRecipe = true; + CycleThroughRecipies(__instance.pagesOfCraftingRecipes, ___currentCraftingPage, __instance); + Task.Delay(200).ContinueWith(_ => { isSelectingRecipe = false; }); + } + + #region Narrate buttons in the menu + if (__instance.upButton != null && __instance.upButton.containsPoint(x, y)) + { + string toSpeak = "Previous Recipe List"; + if (craftingPageQueryKey != toSpeak) + { + craftingPageQueryKey = toSpeak; + hoveredItemQueryKey = ""; + MainClass.ScreenReader.Say(toSpeak, true); + } + return; + } + + if (__instance.downButton != null && __instance.downButton.containsPoint(x, y)) + { + string toSpeak = "Next Recipe List"; + if (craftingPageQueryKey != toSpeak) + { + craftingPageQueryKey = toSpeak; + hoveredItemQueryKey = ""; + MainClass.ScreenReader.Say(toSpeak, true); + } + return; + } + + if (__instance.trashCan.containsPoint(x, y)) + { + string toSpeak = "Trash Can"; + if (craftingPageQueryKey != toSpeak) + { + craftingPageQueryKey = toSpeak; + hoveredItemQueryKey = ""; + MainClass.ScreenReader.Say(toSpeak, true); + } + return; + } + + if (__instance.dropItemInvisibleButton.containsPoint(x, y)) + { + string toSpeak = "Drop Item"; + if (craftingPageQueryKey != toSpeak) + { + craftingPageQueryKey = toSpeak; + hoveredItemQueryKey = ""; + MainClass.ScreenReader.Say(toSpeak, true); + Game1.playSound("drop_item"); + } + return; + } + #endregion + + #region Narrate hovered recipe + if (___hoverRecipe != null) + { + string name = ___hoverRecipe.DisplayName; + int numberOfProduce = ___hoverRecipe.numberProducedPerCraft; + string description = ""; + string ingredients = ""; + string buffs = ""; + string craftable = ""; + + description = $"Description:\n{___hoverRecipe.description}"; + craftable = ___hoverRecipe.doesFarmerHaveIngredientsInInventory(getContainerContents(__instance._materialContainers)) ? "Craftable" : "Not Craftable"; + + #region Crafting ingredients + ingredients = "Ingredients:\n"; + for (int i = 0; i < ___hoverRecipe.recipeList.Count; i++) + { + int recipeCount = ___hoverRecipe.recipeList.ElementAt(i).Value; + int recipeItem = ___hoverRecipe.recipeList.ElementAt(i).Key; + string recipeName = ___hoverRecipe.getNameFromIndex(recipeItem); + + ingredients += $" ,{recipeCount} {recipeName}"; + } + #endregion + + #region Health & stamina and buff items (effects like +1 walking speed) + Item producesItem = ___hoverRecipe.createItem(); + if (producesItem is StardewValley.Object producesItemObject) + { + if (producesItemObject.Edibility != -300) + { + int stamina_recovery = producesItemObject.staminaRecoveredOnConsumption(); + buffs += $"{stamina_recovery} Energy"; + if (stamina_recovery >= 0) + { + int health_recovery = producesItemObject.healthRecoveredOnConsumption(); + buffs += $"\n{health_recovery} Health"; + } + } + // These variables are taken from the game's code itself (IClickableMenu.cs -> 1016 line) + bool edibleItem = producesItem != null && (int)producesItemObject.Edibility != -300; + string[]? buffIconsToDisplay = (producesItem != null && edibleItem && Game1.objectInformation[producesItemObject.ParentSheetIndex].Split('/').Length > 7) + ? producesItem.ModifyItemBuffs(Game1.objectInformation[producesItemObject.ParentSheetIndex].Split('/')[7].Split(' ')) + : null; + + if (buffIconsToDisplay != null) + { + for (int j = 0; j < buffIconsToDisplay.Length; j++) + { + string buffName = ((Convert.ToInt32(buffIconsToDisplay[j]) > 0) ? "+" : "") + buffIconsToDisplay[j] + " "; + if (j <= 11) + { + buffName = Game1.content.LoadString("Strings\\UI:ItemHover_Buff" + j, buffName); + } + try + { + int count = int.Parse(buffName.Substring(0, buffName.IndexOf(' '))); + if (count != 0) + buffs += $"{buffName}\n"; + } + catch (Exception) { } + } + + buffs = $"Buffs and boosts:\n {buffs}"; + } + } + #endregion + + + string toSpeak = $"{numberOfProduce} {name}, {craftable}, \n\t{ingredients}, \n\t{description} \n\t{buffs}"; + + if (craftingPageQueryKey != toSpeak) + { + craftingPageQueryKey = toSpeak; + hoveredItemQueryKey = ""; + MainClass.ScreenReader.Say(toSpeak, true); + } + return; + } + else + { + var isRecipeInFocus = false; + foreach (var item in __instance.pagesOfCraftingRecipes[___currentCraftingPage]) + { + if (item.Key.containsPoint(x, y)) + { + isRecipeInFocus = true; + break; + } + } + + if (isRecipeInFocus) + { + string query = $"unknown recipe:{__instance.getCurrentlySnappedComponent().myID}"; + + if (craftingPageQueryKey != query) + { + craftingPageQueryKey = query; + hoveredItemQueryKey = ""; + MainClass.ScreenReader.Say("unknown recipe", true); + } + return; + } + } + #endregion + + #region Narrate hovered item + if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) + { + craftingPageQueryKey = ""; + return; + } + #endregion + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static void CycleThroughRecipies(List> pagesOfCraftingRecipes, int ___currentCraftingPage, CraftingPage __instance) + { + currentSelectedCraftingRecipe++; + if (currentSelectedCraftingRecipe < 0 || currentSelectedCraftingRecipe >= pagesOfCraftingRecipes[0].Count) + currentSelectedCraftingRecipe = 0; + + __instance.setCurrentlySnappedComponentTo(pagesOfCraftingRecipes[___currentCraftingPage].ElementAt(currentSelectedCraftingRecipe).Key.myID); + pagesOfCraftingRecipes[___currentCraftingPage].ElementAt(currentSelectedCraftingRecipe).Key.snapMouseCursorToCenter(); + + // Skip if recipe is not unlocked/unknown + if (pagesOfCraftingRecipes[___currentCraftingPage].ElementAt(currentSelectedCraftingRecipe).Key.hoverText.Equals("ghosted")) + CycleThroughRecipies(pagesOfCraftingRecipes, ___currentCraftingPage, __instance); + } + + // This method is used to get the inventory items to check if the player has enough ingredients for a recipe + // Taken from CraftingPage.cs -> 169 line + internal static IList? getContainerContents(List materialContainers) + { + if (materialContainers == null) + { + return null; + } + List items = new List(); + for (int i = 0; i < materialContainers.Count; i++) + { + items.AddRange(materialContainers[i].items); + } + return items; + } + + internal static void Cleanup() + { + hoveredItemQueryKey = ""; + craftingPageQueryKey = ""; + currentSelectedCraftingRecipe = -1; + isSelectingRecipe = false; + } + } +} diff --git a/stardew-access/Patches/IClickableMenuPatch.cs b/stardew-access/Patches/IClickableMenuPatch.cs index e925565..7a5efea 100644 --- a/stardew-access/Patches/IClickableMenuPatch.cs +++ b/stardew-access/Patches/IClickableMenuPatch.cs @@ -34,13 +34,11 @@ namespace stardew_access.Patches else if (menu is GameMenu) { GameMenuPatches.gameMenuQueryKey = ""; - GameMenuPatches.craftingPageQueryKey = ""; GameMenuPatches.inventoryPageQueryKey = ""; GameMenuPatches.exitPageQueryKey = ""; GameMenuPatches.optionsPageQueryKey = ""; SocialPagePatch.Cleanup(); - GameMenuPatches.currentSelectedCraftingRecipe = -1; - GameMenuPatches.isSelectingRecipe = false; + CraftingPagePatch.Cleanup(); } else if (menu is JunimoNoteMenu) {