diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index 557d7bf..8d10fc2 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -1,4 +1,5 @@ using Microsoft.Xna.Framework; +using stardew_access.Features; using stardew_access.Patches; using StardewModdingAPI; using StardewValley; @@ -327,18 +328,18 @@ namespace stardew_access return; } - BuildingNAnimalMenuPatches.marked[index] = new Vector2((int)Game1.player.getTileX(), (int)Game1.player.getTileY()); + BuildingOperations.marked[index] = new Vector2((int)Game1.player.getTileX(), (int)Game1.player.getTileY()); MainClass.InfoLog($"Location {(int)Game1.player.getTileX()}x {(int)Game1.player.getTileY()}y added at {index} index."); }); helper.ConsoleCommands.Add("marklist", "List all marked positions.", (string commmand, string[] args) => { string toPrint = ""; - for (int i = 0; i < BuildingNAnimalMenuPatches.marked.Length; i++) + for (int i = 0; i < BuildingOperations.marked.Length; i++) { - if (BuildingNAnimalMenuPatches.marked[i] != Vector2.Zero) + if (BuildingOperations.marked[i] != Vector2.Zero) { - toPrint = $"{toPrint}\n Index {i}: {BuildingNAnimalMenuPatches.marked[i].X}x {BuildingNAnimalMenuPatches.marked[i].Y}y"; + toPrint = $"{toPrint}\n Index {i}: {BuildingOperations.marked[i].X}x {BuildingOperations.marked[i].Y}y"; } } @@ -355,7 +356,7 @@ namespace stardew_access helper.ConsoleCommands.Add("buildsel", "Select the building index which you want to upgrade/demolish/paint", (string commmand, string[] args) => { - if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu && Game1.activeClickableMenu is not AnimalQueryMenu) || !BuildingNAnimalMenuPatches.isOnFarm) + if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu && Game1.activeClickableMenu is not AnimalQueryMenu) || !CarpenterMenuPatch.isOnFarm) { MainClass.InfoLog($"Cannot select building."); return; @@ -380,12 +381,12 @@ namespace stardew_access string? positionIndexInString = args.ElementAtOrDefault(1); int positionIndex = 0; - if (BuildingNAnimalMenuPatches.isMoving) + if (CarpenterMenuPatch.isMoving) { - if (BuildingNAnimalMenuPatches.isConstructing || BuildingNAnimalMenuPatches.isMoving) + if (CarpenterMenuPatch.isConstructing || CarpenterMenuPatch.isMoving) { - if (BuildingNAnimalMenuPatches.availableBuildings[index] == null) + if (BuildingOperations.availableBuildings[index] == null) { MainClass.InfoLog($"No building found with index {index}. Use buildlist."); return; @@ -406,9 +407,9 @@ namespace stardew_access } } } - else if (BuildingNAnimalMenuPatches.isConstructing && !BuildingNAnimalMenuPatches.isUpgrading) + else if (CarpenterMenuPatch.isConstructing && !CarpenterMenuPatch.isUpgrading) { - if (BuildingNAnimalMenuPatches.marked[index] == Vector2.Zero) + if (BuildingOperations.marked[index] == Vector2.Zero) { MainClass.InfoLog($"No marked position found at {index} index."); return; @@ -416,7 +417,7 @@ namespace stardew_access } else { - if (BuildingNAnimalMenuPatches.availableBuildings[index] == null) + if (BuildingOperations.availableBuildings[index] == null) { MainClass.InfoLog($"No building found with index {index}. Use buildlist."); return; @@ -427,19 +428,19 @@ namespace stardew_access if (Game1.activeClickableMenu is PurchaseAnimalsMenu) { - BuildingNAnimalMenuPatches.PurchaseAnimal(BuildingNAnimalMenuPatches.availableBuildings[index]); + BuildingOperations.PurchaseAnimal(BuildingOperations.availableBuildings[index]); } else if (Game1.activeClickableMenu is AnimalQueryMenu) { - BuildingNAnimalMenuPatches.MoveAnimal(BuildingNAnimalMenuPatches.availableBuildings[index]); + BuildingOperations.MoveAnimal(BuildingOperations.availableBuildings[index]); } else { - if (BuildingNAnimalMenuPatches.isConstructing && !BuildingNAnimalMenuPatches.isUpgrading) { response = BuildingNAnimalMenuPatches.Contstruct(BuildingNAnimalMenuPatches.marked[index]); } - else if (BuildingNAnimalMenuPatches.isMoving) { response = BuildingNAnimalMenuPatches.Move(BuildingNAnimalMenuPatches.availableBuildings[index], BuildingNAnimalMenuPatches.marked[positionIndex]); } - else if (BuildingNAnimalMenuPatches.isDemolishing) { response = BuildingNAnimalMenuPatches.Demolish(BuildingNAnimalMenuPatches.availableBuildings[index]); } - else if (BuildingNAnimalMenuPatches.isUpgrading) { response = BuildingNAnimalMenuPatches.Upgrade(BuildingNAnimalMenuPatches.availableBuildings[index]); } - else if (BuildingNAnimalMenuPatches.isPainting) { response = BuildingNAnimalMenuPatches.Paint(BuildingNAnimalMenuPatches.availableBuildings[index]); } + if (CarpenterMenuPatch.isConstructing && !CarpenterMenuPatch.isUpgrading) { response = BuildingOperations.Contstruct(BuildingOperations.marked[index]); } + else if (CarpenterMenuPatch.isMoving) { response = BuildingOperations.Move(BuildingOperations.availableBuildings[index], BuildingOperations.marked[positionIndex]); } + else if (CarpenterMenuPatch.isDemolishing) { response = BuildingOperations.Demolish(BuildingOperations.availableBuildings[index]); } + else if (CarpenterMenuPatch.isUpgrading) { response = BuildingOperations.Upgrade(BuildingOperations.availableBuildings[index]); } + else if (CarpenterMenuPatch.isPainting) { response = BuildingOperations.Paint(BuildingOperations.availableBuildings[index]); } } if (response != null) @@ -517,7 +518,7 @@ namespace stardew_access string? name = buildings[i].nameOfIndoorsWithoutUnique; name = (name == "null") ? buildings[i].buildingType.Value : name; - BuildingNAnimalMenuPatches.availableBuildings[buildingIndex] = buildings[i]; + BuildingOperations.availableBuildings[buildingIndex] = buildings[i]; toPrint = $"{toPrint}\nIndex {buildingIndex}: {name}: At {buildings[i].tileX}x and {buildings[i].tileY}y"; ++buildingIndex; } diff --git a/stardew-access/Features/BuildingOperations.cs b/stardew-access/Features/BuildingOperations.cs new file mode 100644 index 0000000..f970ba3 --- /dev/null +++ b/stardew-access/Features/BuildingOperations.cs @@ -0,0 +1,366 @@ +using Microsoft.Xna.Framework; +using stardew_access.Patches; +using StardewValley; +using StardewValley.Buildings; +using StardewValley.Locations; +using StardewValley.Objects; + +namespace stardew_access.Features +{ + internal class BuildingOperations + { + internal static Building?[] availableBuildings = new Building[100]; + internal static Vector2[] marked = new Vector2[10]; + + public static string? Demolish(Building? toDemolish) + { + if (toDemolish == null) + return null; + + string? response = null; + // This code is taken from the game's code (CarpenterMenu.cs::654) + Farm farm = (Farm)Game1.getLocationFromName("Farm"); + Action buildingLockFailed = delegate + { + if (CarpenterMenuPatch.isDemolishing) + { + response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_LockFailed"); + } + }; + Action continueDemolish = delegate + { + if (CarpenterMenuPatch.isDemolishing && toDemolish != null && farm.buildings.Contains(toDemolish)) + { + if ((int)toDemolish.daysOfConstructionLeft.Value > 0 || (int)toDemolish.daysUntilUpgrade.Value > 0) + { + response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_DuringConstruction"); + } + else if (toDemolish.indoors.Value != null && toDemolish.indoors.Value is AnimalHouse && ((AnimalHouse)toDemolish.indoors.Value).animalsThatLiveHere.Count > 0) + { + response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_AnimalsHere"); + } + else if (toDemolish.indoors.Value != null && toDemolish.indoors.Value.farmers.Any()) + { + response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_PlayerHere"); + } + else + { + if (toDemolish.indoors.Value != null && toDemolish.indoors.Value is Cabin) + { + foreach (Farmer current in Game1.getAllFarmers()) + { + if (current.currentLocation != null && current.currentLocation.Name == ((Cabin)toDemolish.indoors.Value).GetCellarName()) + { + response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_PlayerHere"); + return; + } + } + } + if (toDemolish.indoors.Value is Cabin && ((Cabin)toDemolish.indoors.Value).farmhand.Value.isActive()) + { + response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_FarmhandOnline"); + } + else + { + toDemolish.BeforeDemolish(); + Chest? chest = null; + if (toDemolish.indoors.Value is Cabin) + { + List list = ((Cabin)toDemolish.indoors.Value).demolish(); + if (list.Count > 0) + { + chest = new Chest(playerChest: true); + chest.fixLidFrame(); + chest.items.Set(list); + } + } + if (farm.destroyStructure(toDemolish)) + { + _ = (int)toDemolish.tileY.Value; + _ = (int)toDemolish.tilesHigh.Value; + Game1.flashAlpha = 1f; + toDemolish.showDestroyedAnimation(Game1.getFarm()); + Game1.playSound("explosion"); + Utility.spreadAnimalsAround(toDemolish, farm); + if (CarpenterMenuPatch.carpenterMenu != null) + DelayedAction.functionAfterDelay(CarpenterMenuPatch.carpenterMenu.returnToCarpentryMenu, 1500); + // freeze = true; + if (chest != null) + { + farm.objects[new Vector2((int)toDemolish.tileX.Value + (int)toDemolish.tilesWide.Value / 2, (int)toDemolish.tileY.Value + (int)toDemolish.tilesHigh.Value / 2)] = chest; + } + } + } + } + } + }; + if (toDemolish != null) + { + if (toDemolish.indoors.Value != null && toDemolish.indoors.Value is Cabin && !Game1.IsMasterGame) + { + response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_LockFailed"); + toDemolish = null; + return response; + } + if (CarpenterMenuPatch.carpenterMenu != null && !CarpenterMenuPatch.carpenterMenu.CanDemolishThis(toDemolish)) + { + toDemolish = null; + return response; + } + if (CarpenterMenuPatch.carpenterMenu != null && !Game1.IsMasterGame && !CarpenterMenuPatch.carpenterMenu.hasPermissionsToDemolish(toDemolish)) + { + toDemolish = null; + return response; + } + } + if (toDemolish != null && toDemolish.indoors.Value is Cabin) + { + Cabin cabin = (Cabin)toDemolish.indoors.Value; + if (cabin.farmhand.Value != null && (bool)cabin.farmhand.Value.isCustomized.Value) + { + Game1.currentLocation.createQuestionDialogue(Game1.content.LoadString("Strings\\UI:Carpenter_DemolishCabinConfirm", cabin.farmhand.Value.Name), Game1.currentLocation.createYesNoResponses(), delegate (Farmer f, string answer) + { + if (answer == "Yes") + { + Game1.activeClickableMenu = CarpenterMenuPatch.carpenterMenu; + Game1.player.team.demolishLock.RequestLock(continueDemolish, buildingLockFailed); + } + else + { + if (CarpenterMenuPatch.carpenterMenu != null) + DelayedAction.functionAfterDelay(CarpenterMenuPatch.carpenterMenu.returnToCarpentryMenu, 1000); + } + }); + return response; + } + } + if (toDemolish != null) + { + Game1.player.team.demolishLock.RequestLock(continueDemolish, buildingLockFailed); + } + + return response; + } + + public static string? Contstruct(Vector2 position) + { + string? response = null; + // This code is taken from the game's code (CarpenterMenu.cs::874) + Game1.player.team.buildLock.RequestLock(delegate + { + if (CarpenterMenuPatch.isOnFarm && Game1.locationRequest == null) + { + if (tryToBuild(position)) + { + if (CarpenterMenuPatch.carpenterMenu != null) + { + CarpenterMenuPatch.carpenterMenu.CurrentBlueprint.consumeResources(); + DelayedAction.functionAfterDelay(CarpenterMenuPatch.carpenterMenu.returnToCarpentryMenuAfterSuccessfulBuild, 2000); + } + // freeze = true; + } + else + { + response = Game1.content.LoadString("Strings\\UI:Carpenter_CantBuild"); + } + } + Game1.player.team.buildLock.ReleaseLock(); + }); + + return response; + } + + public static bool tryToBuild(Vector2 position) + { + if (CarpenterMenuPatch.carpenterMenu == null) + return false; + return ((Farm)Game1.getLocationFromName("Farm")).buildStructure(CarpenterMenuPatch.carpenterMenu.CurrentBlueprint, position, Game1.player, CarpenterMenuPatch.isMagicalConstruction); + } + + public static string? Upgrade(Building? toUpgrade) + { + string? response = null; + // This code is taken from the game's code (CarpenterMenu.cs::775) + if (CarpenterMenuPatch.carpenterMenu != null && toUpgrade != null && CarpenterMenuPatch.carpenterMenu.CurrentBlueprint.name != null && toUpgrade.buildingType.Equals(CarpenterMenuPatch.carpenterMenu.CurrentBlueprint.nameOfBuildingToUpgrade)) + { + CarpenterMenuPatch.carpenterMenu.CurrentBlueprint.consumeResources(); + toUpgrade.daysUntilUpgrade.Value = 2; + toUpgrade.showUpgradeAnimation(Game1.getFarm()); + Game1.playSound("axe"); + DelayedAction.functionAfterDelay(CarpenterMenuPatch.carpenterMenu.returnToCarpentryMenuAfterSuccessfulBuild, 1500); + // freeze = true; + // Game1.multiplayer.globalChatInfoMessage("BuildingBuild", Game1.player.Name, Utility.AOrAn(carpenterMenu.CurrentBlueprint.displayName), carpenterMenu.CurrentBlueprint.displayName, Game1.player.farmName.Value); + } + else if (toUpgrade != null) + { + response = Game1.content.LoadString("Strings\\UI:Carpenter_CantUpgrade_BuildingType"); + } + return response; + } + + public static string? Paint(Building? toPaint) + { + string? response = null; + // This code is taken from the game's code (CarpenterMenu.cs::793) + Farm farm_location = Game1.getFarm(); + if (toPaint != null) + { + if (!toPaint.CanBePainted()) + { + response = Game1.content.LoadString("Strings\\UI:Carpenter_CannotPaint"); + return response; + } + if (CarpenterMenuPatch.carpenterMenu != null && !CarpenterMenuPatch.carpenterMenu.HasPermissionsToPaint(toPaint)) + { + response = Game1.content.LoadString("Strings\\UI:Carpenter_CannotPaint_Permission"); + return response; + } + toPaint.color.Value = Color.White; + + if (CarpenterMenuPatch.carpenterMenu != null) + CarpenterMenuPatch.carpenterMenu.SetChildMenu(new StardewValley.Menus.BuildingPaintMenu(toPaint)); + } + /* TODO Add painting of farm house + else if (farm_location.GetHouseRect().Contains(Utility.Vector2ToPoint(new Vector2(toPaint.tileX, toPaint.tileY)))) + { + if (!carpenterMenu.CanPaintHouse()) + { + response = Game1.content.LoadString("Strings\\UI:Carpenter_CannotPaint"); + } + else if (!carpenterMenu.HasPermissionsToPaint(null)) + { + response = Game1.content.LoadString("Strings\\UI:Carpenter_CannotPaint_Permission"); + } + else + { + carpenterMenu.SetChildMenu(new BuildingPaintMenu("House", () => (farm_location.paintedHouseTexture != null) ? farm_location.paintedHouseTexture : Farm.houseTextures, farm_location.houseSource.Value, farm_location.housePaintColor.Value)); + } + }*/ + return response; + } + + public static string? Move(Building? buildingToMove, Vector2 position) + { + string? response = null; + // This code is taken from the game's code (CarpenterMenu.cs::829) + if (buildingToMove != null) + { + string? name = buildingToMove.nameOfIndoorsWithoutUnique; + name = (name == "null") ? buildingToMove.buildingType.Value : name; + + if ((int)buildingToMove.daysOfConstructionLeft.Value > 0) + { + buildingToMove = null; + return "Building under construction, cannot move"; + } + if (CarpenterMenuPatch.carpenterMenu != null && !CarpenterMenuPatch.carpenterMenu.hasPermissionsToMove(buildingToMove)) + { + buildingToMove = null; + return "You don't have permission to move this building"; + } + Game1.playSound("axchop"); + + if (((Farm)Game1.getLocationFromName("Farm")).buildStructure(buildingToMove, position, Game1.player)) + { + if (buildingToMove is ShippingBin) + { + ((ShippingBin)buildingToMove).initLid(); + } + if (buildingToMove is GreenhouseBuilding) + { + Game1.getFarm().greenhouseMoved.Value = true; + } + buildingToMove.performActionOnBuildingPlacement(); + buildingToMove = null; + Game1.playSound("axchop"); + DelayedAction.playSoundAfterDelay("dirtyHit", 50); + DelayedAction.playSoundAfterDelay("dirtyHit", 150); + + response = $"{buildingToMove} moved to {position.X}x {position.Y}y"; + } + else + { + Game1.playSound("cancel"); + response = $"Cannot move building to {position.X}x {position.Y}y"; + } + + } + + return response; + } + + public static void PurchaseAnimal(Building? selection) + { + if (selection == null) + return; + + if (PurchaseAnimalsMenuPatch.purchaseAnimalsMenu == null) + return; + + int x = (selection.tileX.Value * Game1.tileSize) - Game1.viewport.X; + int y = (selection.tileY.Value * Game1.tileSize) - Game1.viewport.Y; + + if (PurchaseAnimalsMenuPatch.animalBeingPurchased != null && !selection.buildingType.Value.Contains(PurchaseAnimalsMenuPatch.animalBeingPurchased.buildingTypeILiveIn.Value)) + { + string warn = Game1.content.LoadString("Strings\\StringsFromCSFiles:PurchaseAnimalsMenu.cs.11326", PurchaseAnimalsMenuPatch.animalBeingPurchased.displayType); + MainClass.ScreenReader.Say(warn, true); + return; + } + + if (((AnimalHouse)selection.indoors.Value).isFull()) + { + string warn = Game1.content.LoadString("Strings\\StringsFromCSFiles:PurchaseAnimalsMenu.cs.11321"); + MainClass.ScreenReader.Say(warn, true); + return; + } + + PurchaseAnimalsMenuPatch.purchaseAnimalsMenu.receiveLeftClick(x, y); + } + + public static void MoveAnimal(Building? selection) + { + if (selection == null) + return; + + if (AnimalQueryMenuPatch.animalQueryMenu == null) + return; + + if (AnimalQueryMenuPatch.animalBeingMoved == null) + return; + + // The following code is taken from the game's source code [AnimalQueryMenu.cs::receiveLeftClick] + if (selection.buildingType.Value.Contains(AnimalQueryMenuPatch.animalBeingMoved.buildingTypeILiveIn.Value)) + { + if (((AnimalHouse)selection.indoors.Value).isFull()) + { + string warn = Game1.content.LoadString("Strings\\UI:AnimalQuery_Moving_BuildingFull"); + MainClass.ScreenReader.Say(warn, true); + return; + } + if (selection.Equals(AnimalQueryMenuPatch.animalBeingMoved.home)) + { + string warn = Game1.content.LoadString("Strings\\UI:AnimalQuery_Moving_AlreadyHome"); + MainClass.ScreenReader.Say(warn, true); + return; + } + ((AnimalHouse)AnimalQueryMenuPatch.animalBeingMoved.home.indoors.Value).animalsThatLiveHere.Remove(AnimalQueryMenuPatch.animalBeingMoved.myID.Value); + if (((AnimalHouse)AnimalQueryMenuPatch.animalBeingMoved.home.indoors.Value).animals.ContainsKey(AnimalQueryMenuPatch.animalBeingMoved.myID.Value)) + { + ((AnimalHouse)selection.indoors.Value).animals.Add(AnimalQueryMenuPatch.animalBeingMoved.myID.Value, AnimalQueryMenuPatch.animalBeingMoved); + ((AnimalHouse)AnimalQueryMenuPatch.animalBeingMoved.home.indoors.Value).animals.Remove(AnimalQueryMenuPatch.animalBeingMoved.myID.Value); + } + AnimalQueryMenuPatch.animalBeingMoved.home = selection; + AnimalQueryMenuPatch.animalBeingMoved.homeLocation.Value = new Vector2((int)selection.tileX.Value, (int)selection.tileY.Value); + ((AnimalHouse)selection.indoors.Value).animalsThatLiveHere.Add(AnimalQueryMenuPatch.animalBeingMoved.myID.Value); + AnimalQueryMenuPatch.animalBeingMoved.makeSound(); + Game1.globalFadeToBlack(AnimalQueryMenuPatch.animalQueryMenu.finishedPlacingAnimal); + } + else + { + string warn = Game1.content.LoadString("Strings\\UI:AnimalQuery_Moving_CantLiveThere"); + MainClass.ScreenReader.Say(warn, true); + } + return; + } + } +} diff --git a/stardew-access/Patches/InventoryUtils.cs b/stardew-access/Features/InventoryUtils.cs similarity index 98% rename from stardew-access/Patches/InventoryUtils.cs rename to stardew-access/Features/InventoryUtils.cs index 5903f92..8b8e913 100644 --- a/stardew-access/Patches/InventoryUtils.cs +++ b/stardew-access/Features/InventoryUtils.cs @@ -2,7 +2,7 @@ using StardewValley; using StardewValley.Menus; -namespace stardew_access.Patches +namespace stardew_access.Features { internal class InventoryUtils { @@ -202,5 +202,11 @@ namespace stardew_access.Patches if (MainClass.Config.DisableInventoryVerbosity) return ""; return " not usable here"; } + + internal static void Cleanup() + { + hoveredItemQueryKey = ""; + prevSlotIndex = -999; + } } } diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index bf570f2..659cb28 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -16,34 +16,34 @@ namespace stardew_access #region Dialogue Patches harmony.Patch( original: AccessTools.Method(typeof(DialogueBox), nameof(DialogueBox.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.DialoguePatch)) + postfix: new HarmonyMethod(typeof(DialogueBoxPatch), nameof(DialogueBoxPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(DialogueBox), nameof(DialogueBox.receiveLeftClick)), - postfix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.ClearDialogueString)) + postfix: new HarmonyMethod(typeof(DialogueBoxPatch), nameof(DialogueBoxPatch.RecieveLeftClickPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(IClickableMenu), nameof(IClickableMenu.drawHoverText), new Type[] { typeof(SpriteBatch), typeof(string), typeof(SpriteFont), typeof(int), typeof(int), typeof(int), typeof(string), typeof(int), typeof(string[]), typeof(Item), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(float), typeof(CraftingRecipe), typeof(IList) }), - postfix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.HoverTextPatch)) + postfix: new HarmonyMethod(typeof(IClickableMenuPatch), nameof(IClickableMenuPatch.DrawHoverTextPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(NPC), nameof(NPC.drawAboveAlwaysFrontLayer)), - postfix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.drawAboveAlwaysFrontLayerPatch)) + postfix: new HarmonyMethod(typeof(NPCPatch), nameof(NPCPatch.DrawAboveAlwaysFrontLayerPatch)) ); #endregion #region Title Menu Patches harmony.Patch( original: AccessTools.Method(typeof(TitleMenu), nameof(TitleMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(TitleMenuPatches), nameof(TitleMenuPatches.TitleMenuPatch)) + postfix: new HarmonyMethod(typeof(TitleMenuPatch), nameof(TitleMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(LoadGameMenu.SaveFileSlot), nameof(LoadGameMenu.SaveFileSlot.Draw), new Type[] { typeof(SpriteBatch), typeof(int) }), - postfix: new HarmonyMethod(typeof(TitleMenuPatches), nameof(TitleMenuPatches.LoadGameMenuPatch)) + postfix: new HarmonyMethod(typeof(LoadGameMenuPatch), nameof(LoadGameMenuPatch.DrawPatch)) ); harmony.Patch( @@ -53,172 +53,167 @@ namespace stardew_access harmony.Patch( original: AccessTools.Method(typeof(CoopMenu), nameof(CoopMenu.update), new Type[] { typeof(GameTime) }), - postfix: new HarmonyMethod(typeof(TitleMenuPatches), nameof(TitleMenuPatches.CoopMenuPatch)) + postfix: new HarmonyMethod(typeof(CoopMenuPatch), nameof(CoopMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(AdvancedGameOptions), nameof(AdvancedGameOptions.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(TitleMenuPatches), nameof(TitleMenuPatches.AdvancedGameOptionsPatch)) + postfix: new HarmonyMethod(typeof(AdvancedGameOptionsPatch), nameof(AdvancedGameOptionsPatch.DrawPatch)) ); #endregion #region Game Menu Patches harmony.Patch( original: AccessTools.Method(typeof(GameMenu), nameof(GameMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.GameMenuPatch)) + postfix: new HarmonyMethod(typeof(GameMenuPatch), nameof(GameMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(OptionsPage), nameof(OptionsPage.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.OptionsPagePatch)) + postfix: new HarmonyMethod(typeof(OptionsPagePatch), nameof(OptionsPagePatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(ExitPage), nameof(ExitPage.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.ExitPagePatch)) + postfix: new HarmonyMethod(typeof(ExitPagePatch), nameof(ExitPagePatch.DrawPatch)) ); 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( original: AccessTools.Method(typeof(InventoryPage), nameof(InventoryPage.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.InventoryPagePatch)) + postfix: new HarmonyMethod(typeof(InventoryPagePatch), nameof(InventoryPagePatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(ItemGrabMenu), nameof(ItemGrabMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.ItemGrabMenuPatch)) + postfix: new HarmonyMethod(typeof(ItemGrabMenuPatch), nameof(ItemGrabMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(GeodeMenu), nameof(GeodeMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.GeodeMenuPatch)) + postfix: new HarmonyMethod(typeof(GeodeMenuPatch), nameof(GeodeMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(ShopMenu), nameof(ShopMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.ShopMenuPatch)) + postfix: new HarmonyMethod(typeof(ShopMenuPatch), nameof(ShopMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(SocialPage), nameof(SocialPage.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.SocialPagePatch)) + postfix: new HarmonyMethod(typeof(SocialPagePatch), nameof(SocialPagePatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(CollectionsPage), nameof(CollectionsPage.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.CollectionsPagePatch)) + postfix: new HarmonyMethod(typeof(CollectionsPagePatch), nameof(CollectionsPagePatch.DrawPatch)) ); #endregion #region Bundle Menu Patches harmony.Patch( original: AccessTools.Method(typeof(JunimoNoteMenu), nameof(JunimoNoteMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(BundleMenuPatches), nameof(BundleMenuPatches.JunimoNoteMenuPatch)) + postfix: new HarmonyMethod(typeof(JunimoNoteMenuPatch), nameof(JunimoNoteMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(JojaCDMenu), nameof(JojaCDMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(BundleMenuPatches), nameof(BundleMenuPatches.JojaCDMenuPatch)) + postfix: new HarmonyMethod(typeof(JojaCDMenuPatch), nameof(JojaCDMenuPatch.DrawPatch)) ); #endregion #region Menu Patches harmony.Patch( original: AccessTools.Method(typeof(LetterViewerMenu), nameof(LetterViewerMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.LetterViewerMenuPatch)) + postfix: new HarmonyMethod(typeof(LetterViwerMenuPatch), nameof(LetterViwerMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(ShippingMenu), nameof(ShippingMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ShippingMenuPatch)) + postfix: new HarmonyMethod(typeof(ShippingMenuPatch), nameof(ShippingMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(LevelUpMenu), nameof(LevelUpMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.LevelUpMenuPatch)) + postfix: new HarmonyMethod(typeof(LevelUpMenuPatch), nameof(LevelUpMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(ConfirmationDialog), nameof(ConfirmationDialog.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ConfirmationDialogPatch)) + postfix: new HarmonyMethod(typeof(ConfirmationDialogMenuPatch), nameof(ConfirmationDialogMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(TitleTextInputMenu), nameof(TitleTextInputMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.TitleTextInputMenuPatch)) + postfix: new HarmonyMethod(typeof(TitleTextInputMenuPatch), nameof(TitleTextInputMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(NamingMenu), nameof(NamingMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.NamingMenuPatch)) + postfix: new HarmonyMethod(typeof(NamingMenuPatch), nameof(NamingMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(MineElevatorMenu), nameof(MineElevatorMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.MineElevatorMenuPatch)) + postfix: new HarmonyMethod(typeof(MineElevatorMenuPatch), nameof(MineElevatorMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(LanguageSelectionMenu), nameof(LanguageSelectionMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.LanguageSelectionMenuPatch)) - ); - - harmony.Patch( - original: AccessTools.Method(typeof(MuseumMenu), nameof(MuseumMenu.receiveKeyPress), new Type[] { typeof(Keys) }), - prefix: new HarmonyMethod(typeof(DonationMenuPatches), nameof(DonationMenuPatches.MuseumMenuKeyPressPatch)) + postfix: new HarmonyMethod(typeof(LanguageSelectionMenuPatch), nameof(LanguageSelectionMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(ChooseFromListMenu), nameof(ChooseFromListMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ChooseFromListMenuPatch)) + postfix: new HarmonyMethod(typeof(ChooseFromListMenuPatch), nameof(ChooseFromListMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(TailoringMenu), nameof(TailoringMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.TailoringMenuPatch)) + postfix: new HarmonyMethod(typeof(TailoringMenuPatch), nameof(TailoringMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(PondQueryMenu), nameof(PondQueryMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.PondQueryMenuPatch)) + postfix: new HarmonyMethod(typeof(PondQueryMenuPatch), nameof(PondQueryMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(ForgeMenu), nameof(ForgeMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ForgeMenuPatch)) + postfix: new HarmonyMethod(typeof(ForgeMenuPatch), nameof(ForgeMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(ItemListMenu), nameof(ItemListMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ItemListMenuPatch)) + postfix: new HarmonyMethod(typeof(ItemListMenuPatch), nameof(ItemListMenuPatch.DrawPatch)) ); #endregion #region Quest Patches harmony.Patch( original: AccessTools.Method(typeof(SpecialOrdersBoard), nameof(SpecialOrdersBoard.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(QuestPatches), nameof(QuestPatches.SpecialOrdersBoardPatch)) + postfix: new HarmonyMethod(typeof(SpecialOrdersBoardPatch), nameof(SpecialOrdersBoardPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(QuestLog), nameof(QuestLog.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(QuestPatches), nameof(QuestPatches.QuestLogPatch)) + postfix: new HarmonyMethod(typeof(QuestLogPatch), nameof(QuestLogPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(Billboard), nameof(Billboard.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(QuestPatches), nameof(QuestPatches.BillboardPatch)) + postfix: new HarmonyMethod(typeof(BillboardPatch), nameof(BillboardPatch.DrawPatch)) ); #endregion #region Chat Menu Patches harmony.Patch( original: AccessTools.Method(typeof(ChatBox), nameof(ChatBox.update), new Type[] { typeof(GameTime) }), - postfix: new HarmonyMethod(typeof(ChatMenuPatches), nameof(ChatMenuPatches.ChatBoxPatch)) + postfix: new HarmonyMethod(typeof(ChatBoxPatch), nameof(ChatBoxPatch.UpdatePatch)) ); #endregion @@ -227,9 +222,10 @@ namespace stardew_access original: AccessTools.Method(typeof(IClickableMenu), nameof(IClickableMenu.exitThisMenu)), postfix: new HarmonyMethod(typeof(IClickableMenuPatch), nameof(IClickableMenuPatch.ExitThisMenuPatch)) ); + harmony.Patch( original: AccessTools.Method(typeof(Game1), nameof(Game1.exitActiveMenu)), - prefix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.Game1ExitActiveMenuPatch)) + prefix: new HarmonyMethod(typeof(Game1Patch), nameof(Game1Patch.ExitActiveMenuPatch)) ); #endregion @@ -237,17 +233,17 @@ namespace stardew_access harmony.Patch( original: AccessTools.Method(typeof(CarpenterMenu), nameof(CarpenterMenu.draw), new Type[] { typeof(SpriteBatch) }), - prefix: new HarmonyMethod(typeof(BuildingNAnimalMenuPatches), nameof(BuildingNAnimalMenuPatches.CarpenterMenuPatch)) + prefix: new HarmonyMethod(typeof(CarpenterMenuPatch), nameof(CarpenterMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(PurchaseAnimalsMenu), nameof(PurchaseAnimalsMenu.draw), new Type[] { typeof(SpriteBatch) }), - prefix: new HarmonyMethod(typeof(BuildingNAnimalMenuPatches), nameof(BuildingNAnimalMenuPatches.PurchaseAnimalsMenuPatch)) + prefix: new HarmonyMethod(typeof(PurchaseAnimalsMenuPatch), nameof(PurchaseAnimalsMenuPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(AnimalQueryMenu), nameof(AnimalQueryMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(BuildingNAnimalMenuPatches), nameof(BuildingNAnimalMenuPatches.AnimalQueryMenuPatch)) + postfix: new HarmonyMethod(typeof(AnimalQueryMenuPatch), nameof(AnimalQueryMenuPatch.DrawPatch)) ); #endregion @@ -255,35 +251,40 @@ namespace stardew_access #region Donation Menus harmony.Patch( original: AccessTools.Method(typeof(MuseumMenu), nameof(MuseumMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(DonationMenuPatches), nameof(DonationMenuPatches.MuseumMenuPatch)) + postfix: new HarmonyMethod(typeof(MuseumMenuPatch), nameof(MuseumMenuPatch.DrawPatch)) + ); + + harmony.Patch( + original: AccessTools.Method(typeof(MuseumMenu), nameof(MuseumMenu.receiveKeyPress), new Type[] { typeof(Keys) }), + prefix: new HarmonyMethod(typeof(MuseumMenuPatch), nameof(MuseumMenuPatch.RecieveKeyPressPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(FieldOfficeMenu), nameof(FieldOfficeMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(DonationMenuPatches), nameof(DonationMenuPatches.FieldOfficeMenuPatch)) + postfix: new HarmonyMethod(typeof(FieldOfficeMenuPatch), nameof(FieldOfficeMenuPatch.DrawPatch)) ); #endregion #region Mini Games harmony.Patch( original: AccessTools.Method(typeof(Intro), nameof(Intro.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MiniGamesPatches), nameof(MiniGamesPatches.IntroPatch)) + postfix: new HarmonyMethod(typeof(IntroPatch), nameof(IntroPatch.DrawPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(GrandpaStory), nameof(GrandpaStory.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MiniGamesPatches), nameof(MiniGamesPatches.GrandpaStoryPatch)) + postfix: new HarmonyMethod(typeof(GrandpaStoryPatch), nameof(GrandpaStoryPatch.DrawPatch)) ); #endregion harmony.Patch( original: AccessTools.Method(typeof(Game1), nameof(Game1.playSound)), - prefix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.PlaySoundPatch)) + prefix: new HarmonyMethod(typeof(Game1Patch), nameof(Game1Patch.PlaySoundPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(InstanceGame), nameof(InstanceGame.Exit)), - prefix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ExitEventPatch)) + prefix: new HarmonyMethod(typeof(InstanceGamePatch), nameof(InstanceGamePatch.ExitPatch)) ); harmony.Patch( diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index def28d6..1830c50 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -128,14 +128,14 @@ namespace stardew_access HarmonyPatches.Initialize(harmony); //Initialize marked locations - for (int i = 0; i < BuildingNAnimalMenuPatches.marked.Length; i++) + for (int i = 0; i < BuildingOperations.marked.Length; i++) { - BuildingNAnimalMenuPatches.marked[i] = Vector2.Zero; + BuildingOperations.marked[i] = Vector2.Zero; } - for (int i = 0; i < BuildingNAnimalMenuPatches.availableBuildings.Length; i++) + for (int i = 0; i < BuildingOperations.availableBuildings.Length; i++) { - BuildingNAnimalMenuPatches.availableBuildings[i] = null; + BuildingOperations.availableBuildings[i] = null; } #endregion diff --git a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs deleted file mode 100644 index 352011f..0000000 --- a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs +++ /dev/null @@ -1,705 +0,0 @@ -using Microsoft.Xna.Framework; -using StardewValley; -using StardewValley.Buildings; -using StardewValley.Locations; -using StardewValley.Menus; -using StardewValley.Objects; - -namespace stardew_access.Patches -{ - internal class BuildingNAnimalMenuPatches - { - internal static Vector2[] marked = new Vector2[10]; - internal static Building?[] availableBuildings = new Building[100]; - internal static CarpenterMenu? carpenterMenu = null; - internal static bool isNarratingAnimalInfo = false; - internal static string animalQueryMenuQuery = " "; - internal static string carpenterMenuQuery = "", purchaseAnimalMenuQuery = ""; - internal static bool isSayingBlueprintInfo = false; - internal static string prevBlueprintInfo = ""; - internal static bool isOnFarm = false, isUpgrading = false, isDemolishing = false, isPainting = false, isConstructing = false, isMoving = false, isMagicalConstruction = false; - internal static bool firstTimeInNamingMenu = true; - internal static PurchaseAnimalsMenu? purchaseAnimalsMenu; - internal static AnimalQueryMenu? animalQueryMenu; - private static FarmAnimal? animalBeingPurchasedOrMoved = null; - - internal static void AnimalQueryMenuPatch(AnimalQueryMenu __instance, bool ___confirmingSell, FarmAnimal ___animal, TextBox ___textBox, string ___parentName, bool ___movingAnimal) - { - try - { - if (TextBoxPatch.isAnyTextBoxActive) return; - - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); // For narrating animal details - string toSpeak = " ", details = " "; - - isOnFarm = ___movingAnimal; - animalQueryMenu = __instance; - animalBeingPurchasedOrMoved = ___animal; - - if (isPrimaryInfoKeyPressed & !isNarratingAnimalInfo) - { - string name = ___animal.displayName; - string type = ___animal.displayType; - int age = (___animal.GetDaysOwned() + 1) / 28 + 1; - string ageText = (age <= 1) ? Game1.content.LoadString("Strings\\UI:AnimalQuery_Age1") : Game1.content.LoadString("Strings\\UI:AnimalQuery_AgeN", age); - string parent = ""; - if ((int)___animal.age.Value < (byte)___animal.ageWhenMature.Value) - { - ageText += Game1.content.LoadString("Strings\\UI:AnimalQuery_AgeBaby"); - } - if (___parentName != null) - { - parent = Game1.content.LoadString("Strings\\UI:AnimalQuery_Parent", ___parentName); - } - - details = $"Name: {name} Type: {type} \n\t Age: {ageText} {parent}"; - animalQueryMenuQuery = " "; - - isNarratingAnimalInfo = true; - Task.Delay(200).ContinueWith(_ => { isNarratingAnimalInfo = false; }); - } - - if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - toSpeak = "OK button"; - else if (__instance.sellButton != null && __instance.sellButton.containsPoint(x, y)) - toSpeak = $"Sell for {___animal.getSellPrice()}g button"; - else if (___confirmingSell && __instance.yesButton != null && __instance.yesButton.containsPoint(x, y)) - toSpeak = "Confirm selling animal"; - else if (___confirmingSell && __instance.noButton != null && __instance.noButton.containsPoint(x, y)) - toSpeak = "Cancel selling animal"; - else if (__instance.moveHomeButton != null && __instance.moveHomeButton.containsPoint(x, y)) - toSpeak = "Change home building button"; - else if (__instance.allowReproductionButton != null && __instance.allowReproductionButton.containsPoint(x, y)) - toSpeak = ((___animal.allowReproduction.Value) ? "Enabled" : "Disabled") + " allow reproduction button"; - else if (__instance.textBoxCC != null && __instance.textBoxCC.containsPoint(x, y)) - toSpeak = "Animal name text box"; - - if (animalQueryMenuQuery != toSpeak) - { - animalQueryMenuQuery = toSpeak; - MainClass.ScreenReader.Say($"{details} {toSpeak}", true); - } - } - catch (System.Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void PurchaseAnimalsMenuPatch(PurchaseAnimalsMenu __instance, bool ___onFarm, bool ___namingAnimal, TextBox ___textBox, FarmAnimal ___animalBeingPurchased) - { - try - { - if (TextBoxPatch.isAnyTextBoxActive) return; - - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - purchaseAnimalsMenu = __instance; - isOnFarm = ___onFarm; - animalBeingPurchasedOrMoved = ___animalBeingPurchased; - - if (___onFarm && ___namingAnimal) - { - string toSpeak = ""; - if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - { - toSpeak = "Cancel Button"; - } - else if (__instance.doneNamingButton != null && __instance.doneNamingButton.containsPoint(x, y)) - { - toSpeak = "OK Button"; - } - else if (__instance.randomButton != null && __instance.randomButton.containsPoint(x, y)) - { - toSpeak = "Random Name Button"; - } - else if (__instance.textBoxCC != null && __instance.textBoxCC.containsPoint(x, y)) - { - toSpeak = "Name Text Box"; - // string? value = ___textBox.Text; - // if (value != "" && value != null && value != "null") - // toSpeak = $"{toSpeak}, Value: {value}"; - } - - if (purchaseAnimalMenuQuery != toSpeak) - { - purchaseAnimalMenuQuery = toSpeak; - - if (firstTimeInNamingMenu) - { - toSpeak = $"Enter the name of animal in the name text box. {toSpeak}"; - firstTimeInNamingMenu = false; - } - - MainClass.ScreenReader.Say(toSpeak, true); - } - } - else if (___onFarm && !___namingAnimal) - { - firstTimeInNamingMenu = true; - } - else if (!___onFarm && !___namingAnimal) - { - firstTimeInNamingMenu = true; - if (__instance.hovered != null) - { - string toSpeak = ""; - if (((StardewValley.Object)__instance.hovered.item).Type != null) - { - toSpeak = ((StardewValley.Object)__instance.hovered.item).Type; - } - else - { - string displayName = PurchaseAnimalsMenu.getAnimalTitle(__instance.hovered.hoverText); - int price = __instance.hovered.item.salePrice(); - string description = PurchaseAnimalsMenu.getAnimalDescription(__instance.hovered.hoverText); - - toSpeak = $"{displayName}, Price: {price}g, Description: {description}"; - } - - if (purchaseAnimalMenuQuery != toSpeak) - { - purchaseAnimalMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void CarpenterMenuPatch( - CarpenterMenu __instance, bool ___onFarm, List ___ingredients, int ___price, - List ___blueprints, int ___currentBlueprintIndex, bool ___upgrading, bool ___demolishing, bool ___moving, - bool ___painting, bool ___magicalConstruction) - { - try - { - isOnFarm = ___onFarm; - carpenterMenu = __instance; - isMagicalConstruction = ___magicalConstruction; - if (!___onFarm) - { - isUpgrading = false; - isDemolishing = false; - isPainting = false; - isMoving = false; - isConstructing = false; - - #region The blueprint menu - BluePrint currentBluprint = __instance.CurrentBlueprint; - if (currentBluprint == null) - return; - - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); - string ingredients = ""; - string name = currentBluprint.displayName; - string upgradeName = currentBluprint.nameOfBuildingToUpgrade; - string description = currentBluprint.description; - string price = $"{___price}g"; - string blueprintInfo; - int width = currentBluprint.tilesWidth; - int height = currentBluprint.tilesHeight; - - #region Get ingredients - for (int i = 0; i < ___ingredients.Count; i++) - { - string itemName = ___ingredients[i].DisplayName; - int itemStack = ___ingredients[i].Stack; - string itemQuality = ""; - - int qualityValue = ((StardewValley.Object)___ingredients[i]).Quality; - if (qualityValue == 1) - { - itemQuality = "Silver quality"; - } - else if (qualityValue == 2 || qualityValue == 3) - { - itemQuality = "Gold quality"; - } - else if (qualityValue >= 4) - { - itemQuality = "Iridium quality"; - } - - ingredients = $"{ingredients}, {itemStack} {itemName} {itemQuality}"; - } - #endregion - - blueprintInfo = $"{name}, Price: {price}, Ingredients: {ingredients}, Dimensions: {width} width and {height} height, Description: {description}"; - - if (isPrimaryInfoKeyPressed && !isSayingBlueprintInfo) - { - SayBlueprintInfo(blueprintInfo); - } - else if (prevBlueprintInfo != blueprintInfo) - { - prevBlueprintInfo = blueprintInfo; - SayBlueprintInfo(blueprintInfo); - } - else - { - if (__instance.backButton != null && __instance.backButton.containsPoint(x, y)) - { - string toSpeak = "Previous Blueprint"; - if (carpenterMenuQuery != toSpeak) - { - carpenterMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.forwardButton != null && __instance.forwardButton.containsPoint(x, y)) - { - string toSpeak = "Next Blueprint"; - if (carpenterMenuQuery != toSpeak) - { - carpenterMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.demolishButton != null && __instance.demolishButton.containsPoint(x, y)) - { - string toSpeak = $"Demolish Building" + (__instance.CanDemolishThis(___blueprints[___currentBlueprintIndex]) ? "" : ", cannot demolish building"); - if (carpenterMenuQuery != toSpeak) - { - carpenterMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - { - string toSpeak = "Construct Building" + (___blueprints[___currentBlueprintIndex].doesFarmerHaveEnoughResourcesToBuild() ? "" : ", cannot cunstrut building, not enough resources to build."); - if (carpenterMenuQuery != toSpeak) - { - carpenterMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.moveButton != null && __instance.moveButton.containsPoint(x, y)) - { - string toSpeak = "Move Building"; - if (carpenterMenuQuery != toSpeak) - { - carpenterMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.paintButton != null && __instance.paintButton.containsPoint(x, y)) - { - string toSpeak = "Paint Building"; - if (carpenterMenuQuery != toSpeak) - { - carpenterMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.cancelButton != null && __instance.cancelButton.containsPoint(x, y)) - { - string toSpeak = "Cancel Button"; - if (carpenterMenuQuery != toSpeak) - { - carpenterMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - } - #endregion - } - else - { - if (___demolishing) - isDemolishing = true; - else if (___upgrading) - isUpgrading = true; - else if (___painting) - isPainting = true; - else if (___moving) - isMoving = true; - else - isConstructing = true; - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - private static async void SayBlueprintInfo(string info) - { - isSayingBlueprintInfo = true; - MainClass.ScreenReader.Say(info, true); - await Task.Delay(300); - isSayingBlueprintInfo = false; - } - - public static string? Demolish(Building? toDemolish) - { - if (toDemolish == null) - return null; - - string? response = null; - // This code is taken from the game's code (CarpenterMenu.cs::654) - Farm farm = (Farm)Game1.getLocationFromName("Farm"); - Action buildingLockFailed = delegate - { - if (isDemolishing) - { - response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_LockFailed"); - } - }; - Action continueDemolish = delegate - { - if (isDemolishing && toDemolish != null && farm.buildings.Contains(toDemolish)) - { - if ((int)toDemolish.daysOfConstructionLeft.Value > 0 || (int)toDemolish.daysUntilUpgrade.Value > 0) - { - response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_DuringConstruction"); - } - else if (toDemolish.indoors.Value != null && toDemolish.indoors.Value is AnimalHouse && ((AnimalHouse)toDemolish.indoors.Value).animalsThatLiveHere.Count > 0) - { - response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_AnimalsHere"); - } - else if (toDemolish.indoors.Value != null && toDemolish.indoors.Value.farmers.Any()) - { - response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_PlayerHere"); - } - else - { - if (toDemolish.indoors.Value != null && toDemolish.indoors.Value is Cabin) - { - foreach (Farmer current in Game1.getAllFarmers()) - { - if (current.currentLocation != null && current.currentLocation.Name == ((Cabin)toDemolish.indoors.Value).GetCellarName()) - { - response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_PlayerHere"); - return; - } - } - } - if (toDemolish.indoors.Value is Cabin && ((Cabin)toDemolish.indoors.Value).farmhand.Value.isActive()) - { - response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_FarmhandOnline"); - } - else - { - toDemolish.BeforeDemolish(); - Chest? chest = null; - if (toDemolish.indoors.Value is Cabin) - { - List list = ((Cabin)toDemolish.indoors.Value).demolish(); - if (list.Count > 0) - { - chest = new Chest(playerChest: true); - chest.fixLidFrame(); - chest.items.Set(list); - } - } - if (farm.destroyStructure(toDemolish)) - { - _ = (int)toDemolish.tileY.Value; - _ = (int)toDemolish.tilesHigh.Value; - Game1.flashAlpha = 1f; - toDemolish.showDestroyedAnimation(Game1.getFarm()); - Game1.playSound("explosion"); - Utility.spreadAnimalsAround(toDemolish, farm); - if (carpenterMenu != null) - DelayedAction.functionAfterDelay(carpenterMenu.returnToCarpentryMenu, 1500); - // freeze = true; - if (chest != null) - { - farm.objects[new Vector2((int)toDemolish.tileX.Value + (int)toDemolish.tilesWide.Value / 2, (int)toDemolish.tileY.Value + (int)toDemolish.tilesHigh.Value / 2)] = chest; - } - } - } - } - } - }; - if (toDemolish != null) - { - if (toDemolish.indoors.Value != null && toDemolish.indoors.Value is Cabin && !Game1.IsMasterGame) - { - response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_LockFailed"); - toDemolish = null; - return response; - } - if (carpenterMenu != null && !carpenterMenu.CanDemolishThis(toDemolish)) - { - toDemolish = null; - return response; - } - if (carpenterMenu != null && !Game1.IsMasterGame && !carpenterMenu.hasPermissionsToDemolish(toDemolish)) - { - toDemolish = null; - return response; - } - } - if (toDemolish != null && toDemolish.indoors.Value is Cabin) - { - Cabin cabin = (Cabin)toDemolish.indoors.Value; - if (cabin.farmhand.Value != null && (bool)cabin.farmhand.Value.isCustomized.Value) - { - Game1.currentLocation.createQuestionDialogue(Game1.content.LoadString("Strings\\UI:Carpenter_DemolishCabinConfirm", cabin.farmhand.Value.Name), Game1.currentLocation.createYesNoResponses(), delegate (Farmer f, string answer) - { - if (answer == "Yes") - { - Game1.activeClickableMenu = carpenterMenu; - Game1.player.team.demolishLock.RequestLock(continueDemolish, buildingLockFailed); - } - else - { - if (carpenterMenu != null) - DelayedAction.functionAfterDelay(carpenterMenu.returnToCarpentryMenu, 1000); - } - }); - return response; - } - } - if (toDemolish != null) - { - Game1.player.team.demolishLock.RequestLock(continueDemolish, buildingLockFailed); - } - - return response; - } - - public static string? Contstruct(Vector2 position) - { - string? response = null; - // This code is taken from the game's code (CarpenterMenu.cs::874) - Game1.player.team.buildLock.RequestLock(delegate - { - if (isOnFarm && Game1.locationRequest == null) - { - if (tryToBuild(position)) - { - if (carpenterMenu != null) - { - carpenterMenu.CurrentBlueprint.consumeResources(); - DelayedAction.functionAfterDelay(carpenterMenu.returnToCarpentryMenuAfterSuccessfulBuild, 2000); - } - // freeze = true; - } - else - { - response = Game1.content.LoadString("Strings\\UI:Carpenter_CantBuild"); - } - } - Game1.player.team.buildLock.ReleaseLock(); - }); - - return response; - } - - public static bool tryToBuild(Vector2 position) - { - if (carpenterMenu == null) - return false; - return ((Farm)Game1.getLocationFromName("Farm")).buildStructure(carpenterMenu.CurrentBlueprint, position, Game1.player, isMagicalConstruction); - } - - public static string? Upgrade(Building? toUpgrade) - { - string? response = null; - // This code is taken from the game's code (CarpenterMenu.cs::775) - if (carpenterMenu != null && toUpgrade != null && carpenterMenu.CurrentBlueprint.name != null && toUpgrade.buildingType.Equals(carpenterMenu.CurrentBlueprint.nameOfBuildingToUpgrade)) - { - carpenterMenu.CurrentBlueprint.consumeResources(); - toUpgrade.daysUntilUpgrade.Value = 2; - toUpgrade.showUpgradeAnimation(Game1.getFarm()); - Game1.playSound("axe"); - DelayedAction.functionAfterDelay(carpenterMenu.returnToCarpentryMenuAfterSuccessfulBuild, 1500); - // freeze = true; - // Game1.multiplayer.globalChatInfoMessage("BuildingBuild", Game1.player.Name, Utility.AOrAn(carpenterMenu.CurrentBlueprint.displayName), carpenterMenu.CurrentBlueprint.displayName, Game1.player.farmName.Value); - } - else if (toUpgrade != null) - { - response = Game1.content.LoadString("Strings\\UI:Carpenter_CantUpgrade_BuildingType"); - } - return response; - } - - public static string? Paint(Building? toPaint) - { - string? response = null; - // This code is taken from the game's code (CarpenterMenu.cs::793) - Farm farm_location = Game1.getFarm(); - if (toPaint != null) - { - if (!toPaint.CanBePainted()) - { - response = Game1.content.LoadString("Strings\\UI:Carpenter_CannotPaint"); - return response; - } - if (carpenterMenu != null && !carpenterMenu.HasPermissionsToPaint(toPaint)) - { - response = Game1.content.LoadString("Strings\\UI:Carpenter_CannotPaint_Permission"); - return response; - } - toPaint.color.Value = Color.White; - - if (carpenterMenu != null) - carpenterMenu.SetChildMenu(new BuildingPaintMenu(toPaint)); - } - /* TODO Add painting of farm house - else if (farm_location.GetHouseRect().Contains(Utility.Vector2ToPoint(new Vector2(toPaint.tileX, toPaint.tileY)))) - { - if (!carpenterMenu.CanPaintHouse()) - { - response = Game1.content.LoadString("Strings\\UI:Carpenter_CannotPaint"); - } - else if (!carpenterMenu.HasPermissionsToPaint(null)) - { - response = Game1.content.LoadString("Strings\\UI:Carpenter_CannotPaint_Permission"); - } - else - { - carpenterMenu.SetChildMenu(new BuildingPaintMenu("House", () => (farm_location.paintedHouseTexture != null) ? farm_location.paintedHouseTexture : Farm.houseTextures, farm_location.houseSource.Value, farm_location.housePaintColor.Value)); - } - }*/ - return response; - } - - public static string? Move(Building? buildingToMove, Vector2 position) - { - string? response = null; - // This code is taken from the game's code (CarpenterMenu.cs::829) - if (buildingToMove != null) - { - string? name = buildingToMove.nameOfIndoorsWithoutUnique; - name = (name == "null") ? buildingToMove.buildingType.Value : name; - - if ((int)buildingToMove.daysOfConstructionLeft.Value > 0) - { - buildingToMove = null; - return "Building under construction, cannot move"; - } - if (carpenterMenu != null && !carpenterMenu.hasPermissionsToMove(buildingToMove)) - { - buildingToMove = null; - return "You don't have permission to move this building"; - } - Game1.playSound("axchop"); - - if (((Farm)Game1.getLocationFromName("Farm")).buildStructure(buildingToMove, position, Game1.player)) - { - if (buildingToMove is ShippingBin) - { - ((ShippingBin)buildingToMove).initLid(); - } - if (buildingToMove is GreenhouseBuilding) - { - Game1.getFarm().greenhouseMoved.Value = true; - } - buildingToMove.performActionOnBuildingPlacement(); - buildingToMove = null; - Game1.playSound("axchop"); - DelayedAction.playSoundAfterDelay("dirtyHit", 50); - DelayedAction.playSoundAfterDelay("dirtyHit", 150); - - response = $"{buildingToMove} moved to {position.X}x {position.Y}y"; - } - else - { - Game1.playSound("cancel"); - response = $"Cannot move building to {position.X}x {position.Y}y"; - } - - } - - return response; - } - - public static void PurchaseAnimal(Building? selection) - { - if (selection == null) - return; - - if (purchaseAnimalsMenu == null) - return; - - int x = (selection.tileX.Value * Game1.tileSize) - Game1.viewport.X; - int y = (selection.tileY.Value * Game1.tileSize) - Game1.viewport.Y; - - if (animalBeingPurchasedOrMoved != null && !selection.buildingType.Value.Contains(animalBeingPurchasedOrMoved.buildingTypeILiveIn.Value)) - { - string warn = Game1.content.LoadString("Strings\\StringsFromCSFiles:PurchaseAnimalsMenu.cs.11326", animalBeingPurchasedOrMoved.displayType); - MainClass.ScreenReader.Say(warn, true); - return; - } - - if (((AnimalHouse)selection.indoors.Value).isFull()) - { - string warn = Game1.content.LoadString("Strings\\StringsFromCSFiles:PurchaseAnimalsMenu.cs.11321"); - MainClass.ScreenReader.Say(warn, true); - return; - } - - purchaseAnimalsMenu.receiveLeftClick(x, y); - } - - public static void MoveAnimal(Building? selection) - { - if (selection == null) - return; - - if (animalQueryMenu == null) - return; - - if (animalBeingPurchasedOrMoved == null) - return; - - // The following code is taken from the game's source code [AnimalQueryMenu.cs::receiveLeftClick] - if (selection.buildingType.Value.Contains(animalBeingPurchasedOrMoved.buildingTypeILiveIn.Value)) - { - if (((AnimalHouse)selection.indoors.Value).isFull()) - { - string warn = Game1.content.LoadString("Strings\\UI:AnimalQuery_Moving_BuildingFull"); - MainClass.ScreenReader.Say(warn, true); - return; - } - if (selection.Equals(animalBeingPurchasedOrMoved.home)) - { - string warn = Game1.content.LoadString("Strings\\UI:AnimalQuery_Moving_AlreadyHome"); - MainClass.ScreenReader.Say(warn, true); - return; - } - ((AnimalHouse)animalBeingPurchasedOrMoved.home.indoors.Value).animalsThatLiveHere.Remove(animalBeingPurchasedOrMoved.myID.Value); - if (((AnimalHouse)animalBeingPurchasedOrMoved.home.indoors.Value).animals.ContainsKey(animalBeingPurchasedOrMoved.myID.Value)) - { - ((AnimalHouse)selection.indoors.Value).animals.Add(animalBeingPurchasedOrMoved.myID.Value, animalBeingPurchasedOrMoved); - ((AnimalHouse)animalBeingPurchasedOrMoved.home.indoors.Value).animals.Remove(animalBeingPurchasedOrMoved.myID.Value); - } - animalBeingPurchasedOrMoved.home = selection; - animalBeingPurchasedOrMoved.homeLocation.Value = new Vector2((int)selection.tileX.Value, (int)selection.tileY.Value); - ((AnimalHouse)selection.indoors.Value).animalsThatLiveHere.Add(animalBeingPurchasedOrMoved.myID.Value); - animalBeingPurchasedOrMoved.makeSound(); - Game1.globalFadeToBlack(animalQueryMenu.finishedPlacingAnimal); - } - else - { - string warn = Game1.content.LoadString("Strings\\UI:AnimalQuery_Moving_CantLiveThere"); - MainClass.ScreenReader.Say(warn, true); - } - return; - } - } -} diff --git a/stardew-access/Patches/BundleMenuPatches.cs b/stardew-access/Patches/BundleMenuPatches.cs deleted file mode 100644 index fc3dc6c..0000000 --- a/stardew-access/Patches/BundleMenuPatches.cs +++ /dev/null @@ -1,360 +0,0 @@ -using StardewValley; -using StardewValley.Locations; -using StardewValley.Menus; - -namespace stardew_access.Patches -{ - internal class BundleMenuPatches - { - internal static string junimoNoteMenuQuery = ""; - internal static string currentJunimoArea = ""; - internal static string jojaCDMenuQuery = ""; - internal static bool isUsingCustomButtons = false; - internal static int currentIngredientListItem = -1, currentIngredientInputSlot = -1, currentInventorySlot = -1; - - #region Joja Mart Bundle/Quests - internal static void JojaCDMenuPatch(JojaCDMenu __instance) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - string toSpeak = ""; - - for (int i = 0; i < __instance.checkboxes.Count; i++) - { - ClickableComponent c = __instance.checkboxes[i]; - if (!c.containsPoint(x, y)) - continue; - - if (c.name.Equals("complete")) - { - toSpeak = $"Completed {getNameFromIndex(i)}"; - } - else - { - toSpeak = $"{getNameFromIndex(i)} Cost: {__instance.getPriceFromButtonNumber(i)}g Description: {__instance.getDescriptionFromButtonNumber(i)}"; - } - - break; - } - - if (jojaCDMenuQuery != toSpeak) - { - jojaCDMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - } - catch (System.Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - private static string getNameFromIndex(int i) - { - string name = i switch - { - 0 => "Bus", - 1 => "Minecarts", - 2 => "Bridge", - 3 => "Greenhouse", - 4 => "Panning", - _ => "", - }; - - if (name != "") - return $"{name} Project"; - else - return "unkown"; - } - #endregion - - #region Community Center Bundles - internal static void JunimoNoteMenuPatch(JunimoNoteMenu __instance, bool ___specificBundlePage, int ___whichArea, Bundle ___currentPageBundle) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - if (!___specificBundlePage) - { - currentIngredientListItem = -1; - isUsingCustomButtons = false; - - string areaName = __instance.scrambledText ? CommunityCenter.getAreaEnglishDisplayNameFromNumber(___whichArea) : CommunityCenter.getAreaDisplayNameFromNumber(___whichArea); - string reward = __instance.getRewardNameForArea(___whichArea); - - if (__instance.scrambledText) - { - string toSpeak = "Scrambled Text"; - if (junimoNoteMenuQuery != toSpeak) - { - junimoNoteMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (currentJunimoArea != areaName) - { - currentJunimoArea = areaName; - MainClass.ScreenReader.Say($"Area {areaName}, {reward}", true); - return; - } - - for (int i = 0; i < __instance.bundles.Count; i++) - { - if (__instance.bundles[i].containsPoint(x, y)) - { - string toSpeak = $"{__instance.bundles[i].name} bundle"; - if (junimoNoteMenuQuery != toSpeak) - { - junimoNoteMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - } - if (__instance.presentButton != null && __instance.presentButton.containsPoint(x, y)) - { - string toSpeak = "Present Button"; - if (junimoNoteMenuQuery != toSpeak) - { - junimoNoteMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - if (__instance.fromGameMenu) - { - if (__instance.areaNextButton.visible && __instance.areaNextButton.containsPoint(x, y)) - { - string toSpeak = "Next Area Button"; - if (junimoNoteMenuQuery != toSpeak) - { - junimoNoteMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - if (__instance.areaBackButton.visible && __instance.areaBackButton.containsPoint(x, y)) - { - string toSpeak = "Previous Area Button"; - if (junimoNoteMenuQuery != toSpeak) - { - junimoNoteMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - } - } - else - { - bool isIPressed = MainClass.Config.BundleMenuIngredientsKey.JustPressed(); // For the ingredients - bool isCPressed = MainClass.Config.BundleMenuInventoryItemsKey.JustPressed(); // For the items in inventory - bool isPPressed = MainClass.Config.BundleMenuPurchaseButtonKey.JustPressed(); // For the Purchase Button - bool isVPressed = MainClass.Config.BundleMenuIngredientsInputSlotKey.JustPressed(); // For the ingredient input slots - bool isBackPressed = MainClass.Config.BundleMenuBackButtonKey.JustPressed(); // For the back button - bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift); - - if (isIPressed && !isUsingCustomButtons) - { - isUsingCustomButtons = true; - JunimoNoteCustomButtons(__instance, ___currentPageBundle, 0, isLeftShiftPressed); - Task.Delay(200).ContinueWith(_ => { isUsingCustomButtons = false; }); - } - else if (isVPressed && !isUsingCustomButtons) - { - isUsingCustomButtons = true; - JunimoNoteCustomButtons(__instance, ___currentPageBundle, 1, isLeftShiftPressed); - Task.Delay(200).ContinueWith(_ => { isUsingCustomButtons = false; }); - } - else if (isCPressed && !isUsingCustomButtons) - { - isUsingCustomButtons = true; - JunimoNoteCustomButtons(__instance, ___currentPageBundle, 2, isLeftShiftPressed); - Task.Delay(200).ContinueWith(_ => { isUsingCustomButtons = false; }); - } - else if (isBackPressed && __instance.backButton != null && !__instance.backButton.containsPoint(x, y)) - { - __instance.backButton.snapMouseCursorToCenter(); - MainClass.ScreenReader.Say("Back Button", true); - } - else if (isPPressed && __instance.purchaseButton != null && !__instance.purchaseButton.containsPoint(x, y)) - { - __instance.purchaseButton.snapMouseCursorToCenter(); - MainClass.ScreenReader.Say("Purchase Button", true); - } - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - private static void JunimoNoteCustomButtons(JunimoNoteMenu __instance, Bundle ___currentPageBundle, int signal, bool isLeftShiftPressed = false) - { - try - { - - switch (signal) - { - case 0: // For ingredient list - { - if (___currentPageBundle.ingredients.Count >= 0) - { - currentIngredientListItem = currentIngredientListItem + (isLeftShiftPressed ? -1 : 1); - if (currentIngredientListItem >= ___currentPageBundle.ingredients.Count) - if (isLeftShiftPressed) - currentIngredientListItem = ___currentPageBundle.ingredients.Count - 1; - else - currentIngredientListItem = 0; - - if (currentIngredientListItem < 0) - if (isLeftShiftPressed) - currentIngredientListItem = ___currentPageBundle.ingredients.Count - 1; - else - currentIngredientListItem = 0; - - ClickableTextureComponent c = __instance.ingredientList[currentIngredientListItem]; - BundleIngredientDescription ingredient = ___currentPageBundle.ingredients[currentIngredientListItem]; - - Item item = new StardewValley.Object(ingredient.index, ingredient.stack, isRecipe: false, -1, ingredient.quality); - bool completed = false; - if (___currentPageBundle != null && ___currentPageBundle.ingredients != null && currentIngredientListItem < ___currentPageBundle.ingredients.Count && ___currentPageBundle.ingredients[currentIngredientListItem].completed) - { - completed = true; - } - - string toSpeak = item.DisplayName; - - if (!completed) - { - int quality = ingredient.quality; - if (quality == 1) - { - toSpeak = $"Silver quality {toSpeak}"; - } - else if (quality == 2 || quality == 3) - { - toSpeak = $"Gold quality {toSpeak}"; - } - else if (quality >= 4) - { - toSpeak = $"Iridium quality {toSpeak}"; - } - - toSpeak = $"{ingredient.stack} {toSpeak}"; - } - - if (completed) - toSpeak = $"Completed {toSpeak}"; - - c.snapMouseCursorToCenter(); - MainClass.ScreenReader.Say(toSpeak, true); - } - } - break; - case 1: // For input slot list - { - if (__instance.ingredientSlots.Count >= 0) - { - currentIngredientInputSlot = currentIngredientInputSlot + (isLeftShiftPressed ? -1 : 1); - if (currentIngredientInputSlot >= __instance.ingredientSlots.Count) - if (isLeftShiftPressed) - currentIngredientInputSlot = __instance.ingredientSlots.Count - 1; - else - currentIngredientInputSlot = 0; - - if (currentIngredientInputSlot < 0) - if (isLeftShiftPressed) - currentIngredientInputSlot = __instance.ingredientSlots.Count - 1; - else - currentIngredientInputSlot = 0; - - ClickableTextureComponent c = __instance.ingredientSlots[currentIngredientInputSlot]; - Item item = c.item; - string toSpeak; - - if (item == null) - { - toSpeak = $"Input Slot {currentIngredientInputSlot + 1}"; - } - else - { - toSpeak = item.DisplayName; - } - - c.snapMouseCursorToCenter(); - MainClass.ScreenReader.Say(toSpeak, true); - } - } - break; - case 2: // For inventory slots - { - if (__instance.inventory != null && __instance.inventory.actualInventory.Count >= 0) - { - int prevSlotIndex = currentInventorySlot; - currentInventorySlot = currentInventorySlot + (isLeftShiftPressed ? -1 : 1); - if (currentInventorySlot >= __instance.inventory.actualInventory.Count) - if (isLeftShiftPressed) - currentInventorySlot = __instance.inventory.actualInventory.Count - 1; - else - currentInventorySlot = 0; - - if (currentInventorySlot < 0) - if (isLeftShiftPressed) - currentInventorySlot = __instance.inventory.actualInventory.Count - 1; - else - currentInventorySlot = 0; - - Item item = __instance.inventory.actualInventory[currentInventorySlot]; - ClickableComponent c = __instance.inventory.inventory[currentInventorySlot]; - string toSpeak; - if (item != null) - { - toSpeak = item.DisplayName; - - if ((item as StardewValley.Object) != null) - { - int quality = ((StardewValley.Object)item).Quality; - if (quality == 1) - { - toSpeak = $"Silver quality {toSpeak}"; - } - else if (quality == 2 || quality == 3) - { - toSpeak = $"Gold quality {toSpeak}"; - } - else if (quality >= 4) - { - toSpeak = $"Iridium quality {toSpeak}"; - } - } - toSpeak = $"{item.Stack} {toSpeak}"; - - if (!__instance.inventory.highlightMethod(__instance.inventory.actualInventory[currentInventorySlot])) - { - toSpeak = $"{toSpeak} not usable here"; - } - } - else - { - toSpeak = "Empty Slot"; - } - c.snapMouseCursorToCenter(); - MainClass.ScreenReader.Say(toSpeak, true); - } - } - break; - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - #endregion - } -} \ No newline at end of file diff --git a/stardew-access/Patches/BundleMenuPatches/JojaCDMenuPatch.cs b/stardew-access/Patches/BundleMenuPatches/JojaCDMenuPatch.cs new file mode 100644 index 0000000..2a8e1b6 --- /dev/null +++ b/stardew-access/Patches/BundleMenuPatches/JojaCDMenuPatch.cs @@ -0,0 +1,70 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class JojaCDMenuPatch + { + internal static string jojaCDMenuQuery = ""; + + internal static void DrawPatch(JojaCDMenu __instance) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + string toSpeak = ""; + + for (int i = 0; i < __instance.checkboxes.Count; i++) + { + ClickableComponent c = __instance.checkboxes[i]; + if (!c.containsPoint(x, y)) + continue; + + if (c.name.Equals("complete")) + { + toSpeak = $"Completed {getNameFromIndex(i)}"; + } + else + { + toSpeak = $"{getNameFromIndex(i)} Cost: {__instance.getPriceFromButtonNumber(i)}g Description: {__instance.getDescriptionFromButtonNumber(i)}"; + } + + break; + } + + if (jojaCDMenuQuery != toSpeak) + { + jojaCDMenuQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + } + catch (System.Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static string getNameFromIndex(int i) + { + string name = i switch + { + 0 => "Bus", + 1 => "Minecarts", + 2 => "Bridge", + 3 => "Greenhouse", + 4 => "Panning", + _ => "", + }; + + if (name != "") + return $"{name} Project"; + else + return "unkown"; + } + + internal static void Cleanup() + { + jojaCDMenuQuery = ""; + } + } +} diff --git a/stardew-access/Patches/BundleMenuPatches/JunimoNoteMenuPatch.cs b/stardew-access/Patches/BundleMenuPatches/JunimoNoteMenuPatch.cs new file mode 100644 index 0000000..96d89c5 --- /dev/null +++ b/stardew-access/Patches/BundleMenuPatches/JunimoNoteMenuPatch.cs @@ -0,0 +1,297 @@ +using StardewValley; +using StardewValley.Locations; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class JunimoNoteMenuPatch + { + internal static string junimoNoteMenuQuery = ""; + internal static string currentJunimoArea = ""; + internal static bool isUsingCustomKeyBinds = false; + internal static int currentIngredientListItem = -1, currentIngredientInputSlot = -1, currentInventorySlot = -1; + + internal static void DrawPatch(JunimoNoteMenu __instance, bool ___specificBundlePage, int ___whichArea, Bundle ___currentPageBundle) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + if (narrateJunimoArea(__instance, ___specificBundlePage, ___whichArea, x, y)) + { + return; + } + + narrateBundlePage(__instance, ___specificBundlePage, ___currentPageBundle, x, y); + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static bool narrateJunimoArea(JunimoNoteMenu __instance, bool ___specificBundlePage, int ___whichArea, int x, int y) + { + if (___specificBundlePage) + return false; + + currentIngredientListItem = -1; + isUsingCustomKeyBinds = false; + + string areaName = __instance.scrambledText ? CommunityCenter.getAreaEnglishDisplayNameFromNumber(___whichArea) : CommunityCenter.getAreaDisplayNameFromNumber(___whichArea); + string reward = __instance.getRewardNameForArea(___whichArea); + + if (__instance.scrambledText) + { + string scrambledText = "Scrambled Text"; + if (junimoNoteMenuQuery != scrambledText) + { + junimoNoteMenuQuery = scrambledText; + MainClass.ScreenReader.Say(scrambledText, true); + } + return true; + } + + if (currentJunimoArea != areaName) + { + currentJunimoArea = areaName; + MainClass.ScreenReader.Say($"Area {areaName}, {reward}", true); + return true; + } + + string toSpeak = ""; + if (__instance.presentButton != null && __instance.presentButton.containsPoint(x, y)) + { + toSpeak = "Present Button"; + } + else if (__instance.fromGameMenu && __instance.areaNextButton.visible && __instance.areaNextButton.containsPoint(x, y)) + { + toSpeak = "Next Area Button"; + } + else if (__instance.fromGameMenu && __instance.areaBackButton.visible && __instance.areaBackButton.containsPoint(x, y)) + { + toSpeak = "Previous Area Button"; + } + else + { + for (int i = 0; i < __instance.bundles.Count; i++) + { + if (!__instance.bundles[i].containsPoint(x, y)) + continue; + + toSpeak = $"{__instance.bundles[i].name} bundle"; + break; + } + } + + if (junimoNoteMenuQuery != toSpeak) + { + junimoNoteMenuQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + return true; + } + + return false; + } + + private static void narrateBundlePage(JunimoNoteMenu __instance, bool ___specificBundlePage, Bundle ___currentPageBundle, int x, int y) + { + if (!___specificBundlePage) + return; + + bool isIPressed = MainClass.Config.BundleMenuIngredientsKey.JustPressed(); // For the ingredients + bool isCPressed = MainClass.Config.BundleMenuInventoryItemsKey.JustPressed(); // For the items in inventory + bool isPPressed = MainClass.Config.BundleMenuPurchaseButtonKey.JustPressed(); // For the Purchase Button + bool isVPressed = MainClass.Config.BundleMenuIngredientsInputSlotKey.JustPressed(); // For the ingredient input slots + bool isBackPressed = MainClass.Config.BundleMenuBackButtonKey.JustPressed(); // For the back button + bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift); + + if (isIPressed && !isUsingCustomKeyBinds) + { + isUsingCustomKeyBinds = true; + cycleThroughIngredientList(__instance, ___currentPageBundle, isLeftShiftPressed); + Task.Delay(200).ContinueWith(_ => { isUsingCustomKeyBinds = false; }); + } + else if (isVPressed && !isUsingCustomKeyBinds) + { + isUsingCustomKeyBinds = true; + cycleThroughInputSlots(__instance, ___currentPageBundle, isLeftShiftPressed); + Task.Delay(200).ContinueWith(_ => { isUsingCustomKeyBinds = false; }); + } + else if (isCPressed && !isUsingCustomKeyBinds) + { + isUsingCustomKeyBinds = true; + cycleThroughInventorySlots(__instance, ___currentPageBundle, isLeftShiftPressed); + Task.Delay(200).ContinueWith(_ => { isUsingCustomKeyBinds = false; }); + } + else if (isBackPressed && __instance.backButton != null && !__instance.backButton.containsPoint(x, y)) + { + __instance.backButton.snapMouseCursorToCenter(); + MainClass.ScreenReader.Say("Back Button", true); + } + else if (isPPressed && __instance.purchaseButton != null && !__instance.purchaseButton.containsPoint(x, y)) + { + __instance.purchaseButton.snapMouseCursorToCenter(); + MainClass.ScreenReader.Say("Purchase Button", true); + } + return; + } + + private static void cycleThroughIngredientList(JunimoNoteMenu __instance, Bundle ___currentPageBundle, bool isLeftShiftPressed = false) + { + if (___currentPageBundle.ingredients.Count < 0) + return; + + currentIngredientListItem = currentIngredientListItem + (isLeftShiftPressed ? -1 : 1); + if (currentIngredientListItem >= ___currentPageBundle.ingredients.Count) + if (isLeftShiftPressed) + currentIngredientListItem = ___currentPageBundle.ingredients.Count - 1; + else + currentIngredientListItem = 0; + + if (currentIngredientListItem < 0) + if (isLeftShiftPressed) + currentIngredientListItem = ___currentPageBundle.ingredients.Count - 1; + else + currentIngredientListItem = 0; + + ClickableTextureComponent c = __instance.ingredientList[currentIngredientListItem]; + BundleIngredientDescription ingredient = ___currentPageBundle.ingredients[currentIngredientListItem]; + + Item item = new StardewValley.Object(ingredient.index, ingredient.stack, isRecipe: false, -1, ingredient.quality); + bool completed = false; + if (___currentPageBundle != null && ___currentPageBundle.ingredients != null && currentIngredientListItem < ___currentPageBundle.ingredients.Count && ___currentPageBundle.ingredients[currentIngredientListItem].completed) + { + completed = true; + } + + string toSpeak = item.DisplayName; + + if (completed) + { + toSpeak = $"Completed {toSpeak}"; + } + else + { + int quality = ingredient.quality; + if (quality == 1) + { + toSpeak = $"Silver quality {toSpeak}"; + } + else if (quality == 2 || quality == 3) + { + toSpeak = $"Gold quality {toSpeak}"; + } + else if (quality >= 4) + { + toSpeak = $"Iridium quality {toSpeak}"; + } + + toSpeak = $"{ingredient.stack} {toSpeak}"; + } + + c.snapMouseCursorToCenter(); + MainClass.ScreenReader.Say(toSpeak, true); + } + + private static void cycleThroughInputSlots(JunimoNoteMenu __instance, Bundle ___currentPageBundle, bool isLeftShiftPressed = false) + { + if (__instance.ingredientSlots.Count < 0) + return; + + currentIngredientInputSlot = currentIngredientInputSlot + (isLeftShiftPressed ? -1 : 1); + if (currentIngredientInputSlot >= __instance.ingredientSlots.Count) + if (isLeftShiftPressed) + currentIngredientInputSlot = __instance.ingredientSlots.Count - 1; + else + currentIngredientInputSlot = 0; + + if (currentIngredientInputSlot < 0) + if (isLeftShiftPressed) + currentIngredientInputSlot = __instance.ingredientSlots.Count - 1; + else + currentIngredientInputSlot = 0; + + ClickableTextureComponent c = __instance.ingredientSlots[currentIngredientInputSlot]; + Item item = c.item; + string toSpeak; + + if (item == null) + { + toSpeak = $"Input Slot {currentIngredientInputSlot + 1}"; + } + else + { + toSpeak = item.DisplayName; + } + + c.snapMouseCursorToCenter(); + MainClass.ScreenReader.Say(toSpeak, true); + } + + private static void cycleThroughInventorySlots(JunimoNoteMenu __instance, Bundle ___currentPageBundle, bool isLeftShiftPressed = false) + { + if (__instance.inventory == null || __instance.inventory.actualInventory.Count < 0) + return; + + int prevSlotIndex = currentInventorySlot; + currentInventorySlot = currentInventorySlot + (isLeftShiftPressed ? -1 : 1); + if (currentInventorySlot >= __instance.inventory.actualInventory.Count) + if (isLeftShiftPressed) + currentInventorySlot = __instance.inventory.actualInventory.Count - 1; + else + currentInventorySlot = 0; + + if (currentInventorySlot < 0) + if (isLeftShiftPressed) + currentInventorySlot = __instance.inventory.actualInventory.Count - 1; + else + currentInventorySlot = 0; + + Item item = __instance.inventory.actualInventory[currentInventorySlot]; + ClickableComponent c = __instance.inventory.inventory[currentInventorySlot]; + string toSpeak; + if (item != null) + { + toSpeak = item.DisplayName; + + if ((item as StardewValley.Object) != null) + { + int quality = ((StardewValley.Object)item).Quality; + if (quality == 1) + { + toSpeak = $"Silver quality {toSpeak}"; + } + else if (quality == 2 || quality == 3) + { + toSpeak = $"Gold quality {toSpeak}"; + } + else if (quality >= 4) + { + toSpeak = $"Iridium quality {toSpeak}"; + } + } + toSpeak = $"{item.Stack} {toSpeak}"; + + if (!__instance.inventory.highlightMethod(__instance.inventory.actualInventory[currentInventorySlot])) + { + toSpeak = $"{toSpeak} not usable here"; + } + } + else + { + toSpeak = "Empty Slot"; + } + c.snapMouseCursorToCenter(); + MainClass.ScreenReader.Say(toSpeak, true); + } + + internal static void Cleanup() + { + JunimoNoteMenuPatch.currentIngredientListItem = -1; + JunimoNoteMenuPatch.currentIngredientInputSlot = -1; + JunimoNoteMenuPatch.currentInventorySlot = -1; + JunimoNoteMenuPatch.junimoNoteMenuQuery = ""; + } + } +} diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs deleted file mode 100644 index e131952..0000000 --- a/stardew-access/Patches/DialoguePatches.cs +++ /dev/null @@ -1,427 +0,0 @@ -using Microsoft.Xna.Framework.Graphics; -using StardewModdingAPI; -using StardewValley; -using StardewValley.Menus; - -namespace stardew_access.Patches -{ - internal class DialoguePatches - { - internal static string currentLetterText = " "; - internal static string currentDialogue = " "; - internal static bool isDialogueAppearingFirstTime = true; - - internal static void DialoguePatch(DialogueBox __instance, SpriteBatch b) - { - try - { - if (__instance.transitioning) - return; - - if (__instance.characterDialogue != null) - { - // For Normal Character dialogues - Dialogue dialogue = __instance.characterDialogue; - string speakerName = dialogue.speaker.displayName; - List responses = __instance.responses; - string toSpeak = " "; - string dialogueText = ""; - string response = ""; - bool hasResponses = dialogue.isCurrentDialogueAQuestion(); - - dialogueText = $"{speakerName} said {__instance.getCurrentString()}"; - - if (hasResponses) - { - if (__instance.selectedResponse >= 0 && __instance.selectedResponse < responses.Count) - response = $"{__instance.selectedResponse + 1}: {responses[__instance.selectedResponse].responseText}"; - else - // When the dialogue is not finished writing then the selectedResponse is <0 and this results - // in the first response not being detcted, so this sets the first response option to be the default - // if the current dialogue is a question or has responses - response = $"1: {responses[0].responseText}"; - } - - if (hasResponses) - { - if (currentDialogue != response) - { - currentDialogue = response; - - if (isDialogueAppearingFirstTime) - { - toSpeak = $"{dialogueText} \n\t {response}"; - isDialogueAppearingFirstTime = false; - } - else - toSpeak = response; - - MainClass.ScreenReader.Say(toSpeak, true); - } - } - else - { - if (currentDialogue != dialogueText) - { - currentDialogue = dialogueText; - MainClass.ScreenReader.Say(dialogueText, true); - } - } - } - else if (__instance.isQuestion) - { - // For Dialogues with responses/answers like the dialogue when we click on tv - string toSpeak = ""; - string dialogueText = ""; - string response = ""; - bool hasResponses = false; - - if (__instance.responses.Count > 0) - hasResponses = true; - - dialogueText = __instance.getCurrentString(); - - if (hasResponses) - if (__instance.selectedResponse >= 0 && __instance.selectedResponse < __instance.responses.Count) - response = $"{__instance.selectedResponse + 1}: {__instance.responses[__instance.selectedResponse].responseText}"; - else - // When the dialogue is not finished writing then the selectedResponse is <0 and this results - // in the first response not being detcted, so this sets the first response option to be the default - // if the current dialogue is a question or has responses - response = $"1: {__instance.responses[0].responseText}"; - - - if (hasResponses) - { - if (currentDialogue != response) - { - currentDialogue = response; - - if (isDialogueAppearingFirstTime) - { - toSpeak = $"{dialogueText} \n\t {response}"; - isDialogueAppearingFirstTime = false; - } - else - toSpeak = response; - - MainClass.ScreenReader.Say(toSpeak, true); - } - } - else - { - if (currentDialogue != dialogueText) - { - currentDialogue = dialogueText; - MainClass.ScreenReader.Say(dialogueText, true); - } - } - } - else if (Game1.activeClickableMenu is DialogueBox) - { - // Basic dialogues like `No mails in the mail box` - if (currentDialogue != __instance.getCurrentString()) - { - currentDialogue = __instance.getCurrentString(); - MainClass.ScreenReader.Say(__instance.getCurrentString(), true); - } - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate dialog:\n{e.StackTrace}\n{e.Message}"); - } - - } - - internal static void ClearDialogueString() - { - // CLears the currentDialogue string on closing dialog - currentDialogue = " "; - isDialogueAppearingFirstTime = true; - } - - internal static void HoverTextPatch(string? text, int moneyAmountToDisplayAtBottom = -1, string? boldTitleText = null, int extraItemToShowIndex = -1, int extraItemToShowAmount = -1, string[]? buffIconsToDisplay = null, Item? hoveredItem = null, CraftingRecipe? craftingIngredients = null) - { - try - { - #region Skip narrating hover text for certain menus - if (Game1.activeClickableMenu is TitleMenu && !(((TitleMenu)Game1.activeClickableMenu).GetChildMenu() is CharacterCustomization)) - return; - else if (Game1.activeClickableMenu is LetterViewerMenu || Game1.activeClickableMenu is QuestLog) - return; - else if (Game1.activeClickableMenu is Billboard) - return; - else if (Game1.activeClickableMenu is GeodeMenu) - return; - else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is InventoryPage) - return; - else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is CraftingPage) - return; - else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is OptionsPage) - return; - else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is ExitPage) - return; - else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is SocialPage) - return; - else if (Game1.activeClickableMenu is ItemGrabMenu) - return; - else if (Game1.activeClickableMenu is ShopMenu) - return; - else if (Game1.activeClickableMenu is ConfirmationDialog) - return; - else if (Game1.activeClickableMenu is JunimoNoteMenu) - return; - else if (Game1.activeClickableMenu is CarpenterMenu) - return; - else if (Game1.activeClickableMenu is PurchaseAnimalsMenu) - return; - else if (Game1.activeClickableMenu is CraftingPage) - return; - else if (Game1.activeClickableMenu is AnimalQueryMenu) - return; - else if (Game1.activeClickableMenu is ConfirmationDialog) - return; - else if (Game1.activeClickableMenu is ReadyCheckDialog) - return; - else if (Game1.activeClickableMenu is JojaCDMenu) - return; - else if (Game1.activeClickableMenu is TailoringMenu) - return; - else if (Game1.activeClickableMenu is PondQueryMenu) - return; - else if (Game1.activeClickableMenu is ForgeMenu) - return; - else if (Game1.activeClickableMenu is ItemListMenu) - return; - else if (Game1.activeClickableMenu is FieldOfficeMenu) - return; - else if (Game1.activeClickableMenu is MuseumMenu) - return; - #endregion - - string toSpeak = " "; - - #region Add item count before title - if (hoveredItem != null && hoveredItem.HasBeenInInventory) - { - int count = hoveredItem.Stack; - if (count > 1) - toSpeak = $"{toSpeak} {count} "; - } - #endregion - - #region Add title if any - if (boldTitleText != null) - toSpeak = $"{toSpeak} {boldTitleText}\n"; - #endregion - - #region Add quality of item - if (hoveredItem is StardewValley.Object && ((StardewValley.Object)hoveredItem).Quality > 0) - { - int quality = ((StardewValley.Object)hoveredItem).Quality; - if (quality == 1) - { - toSpeak = $"{toSpeak} Silver quality"; - } - else if (quality == 2 || quality == 3) - { - toSpeak = $"{toSpeak} Gold quality"; - } - else if (quality >= 4) - { - toSpeak = $"{toSpeak} Iridium quality"; - } - } - #endregion - - #region Narrate hovered required ingredients - if (extraItemToShowIndex != -1) - { - string itemName = Game1.objectInformation[extraItemToShowIndex].Split('/')[0]; - - if (extraItemToShowAmount != -1) - toSpeak = $"{toSpeak} Required: {extraItemToShowAmount} {itemName}"; - else - toSpeak = $"{toSpeak} Required: {itemName}"; - } - #endregion - - #region Add money - if (moneyAmountToDisplayAtBottom != -1) - toSpeak = $"{toSpeak} \nCost: {moneyAmountToDisplayAtBottom}g\n"; - #endregion - - #region Add the base text - if (text == "???") - toSpeak = "unknown"; - else - toSpeak = $"{toSpeak} {text}"; - #endregion - - #region Add crafting ingredients - if (craftingIngredients != null) - { - toSpeak = $"{toSpeak} \n{craftingIngredients.description}"; - toSpeak = $"{toSpeak} \nIngredients\n"; - - craftingIngredients.recipeList.ToList().ForEach(recipe => - { - int count = recipe.Value; - int item = recipe.Key; - string name = craftingIngredients.getNameFromIndex(item); - - toSpeak = $"{toSpeak} ,{count} {name}"; - }); - } - #endregion - - #region Add health & stamina - if (hoveredItem is StardewValley.Object && ((StardewValley.Object)hoveredItem).Edibility != -300) - { - int stamina_recovery = ((StardewValley.Object)hoveredItem).staminaRecoveredOnConsumption(); - toSpeak = $"{toSpeak} {stamina_recovery} Energy\n"; - if (stamina_recovery >= 0) - { - int health_recovery = ((StardewValley.Object)hoveredItem).healthRecoveredOnConsumption(); - toSpeak = $"{toSpeak} {health_recovery} Health"; - } - } - #endregion - - #region Add buff items (effects like +1 walking speed) - if (buffIconsToDisplay != null) - { - for (int i = 0; i < buffIconsToDisplay.Length; i++) - { - string buffName = ((Convert.ToInt32(buffIconsToDisplay[i]) > 0) ? "+" : "") + buffIconsToDisplay[i] + " "; - if (i <= 11) - { - buffName = Game1.content.LoadString("Strings\\UI:ItemHover_Buff" + i, buffName); - } - try - { - int count = int.Parse(buffName.Substring(0, buffName.IndexOf(' '))); - if (count != 0) - toSpeak = $"{toSpeak} {buffName}\n"; - } - catch (Exception) { } - } - } - #endregion - - #region Narrate toSpeak - // To prevent it from getting conflicted by two hover texts at the same time, two seperate methods are used. - // For example, sometimes `Welcome to Pierre's` and the items in seeds shop get conflicted causing it to speak infinitely. - - if (toSpeak.ToString() != " ") - { - if (Context.IsPlayerFree) - MainClass.ScreenReader.SayWithChecker(toSpeak.ToString(), true); // Normal Checker - else - MainClass.ScreenReader.SayWithMenuChecker(toSpeak.ToString(), true); // Menu Checker - } - #endregion - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate dialog:\n{e.StackTrace}\n{e.Message}"); - } - } - - internal static void LetterViewerMenuPatch(LetterViewerMenu __instance) - { - try - { - if (!__instance.IsActive()) - return; - - NarrateLetterContent(__instance); - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void NarrateLetterContent(LetterViewerMenu __instance) - { - int x = Game1.getMousePosition().X, y = Game1.getMousePosition().Y; - #region Texts in the letter - string message = __instance.mailMessage[__instance.page]; - - string toSpeak = $"{message}"; - - if (__instance.ShouldShowInteractable()) - { - if (__instance.moneyIncluded > 0) - { - string moneyText = Game1.content.LoadString("Strings\\UI:LetterViewer_MoneyIncluded", __instance.moneyIncluded); - toSpeak += $"\t\n\t ,Included money: {moneyText}"; - } - else if (__instance.learnedRecipe != null && __instance.learnedRecipe.Length > 0) - { - string recipeText = Game1.content.LoadString("Strings\\UI:LetterViewer_LearnedRecipe", __instance.cookingOrCrafting); - toSpeak += $"\t\n\t ,Learned Recipe: {recipeText}"; - } - } - - if (currentLetterText != toSpeak) - { - currentLetterText = toSpeak; - - // snap mouse to accept quest button - if (__instance.acceptQuestButton != null && __instance.questID != -1) - { - toSpeak += "\t\n Left click to accept quest."; - __instance.acceptQuestButton.snapMouseCursorToCenter(); - } - if (__instance.mailMessage.Count > 1) - toSpeak = $"Page {__instance.page + 1} of {__instance.mailMessage.Count}:\n\t{toSpeak}"; - - MainClass.ScreenReader.Say(toSpeak, true); - } - #endregion - - #region Narrate items given in the mail - if (__instance.ShouldShowInteractable()) - { - foreach (ClickableComponent c in __instance.itemsToGrab) - { - if (c.item == null) - continue; - - string name = c.item.DisplayName; - - if (c.containsPoint(x, y)) - MainClass.ScreenReader.SayWithChecker($"Left click to collect {name}", false); - } - } - #endregion - - #region Narrate buttons - if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y)) - MainClass.ScreenReader.SayWithChecker($"Previous page button", false); - - if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y)) - MainClass.ScreenReader.SayWithChecker($"Next page button", false); - - #endregion - } - - internal static void drawAboveAlwaysFrontLayerPatch(NPC __instance, string ___textAboveHead, int ___textAboveHeadTimer) - { - try - { - if (___textAboveHeadTimer > 2900 && ___textAboveHead != null) - { - MainClass.ScreenReader.SayWithChecker($"{__instance.displayName} says {___textAboveHead}", true); - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Error in patch:NPCShowTextAboveHeadPatch \n{e.Message}\n{e.StackTrace}"); - } - } - } -} diff --git a/stardew-access/Patches/DonationMenuPatches.cs b/stardew-access/Patches/DonationMenuPatches.cs deleted file mode 100644 index ffffa2f..0000000 --- a/stardew-access/Patches/DonationMenuPatches.cs +++ /dev/null @@ -1,328 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Content; -using StardewValley; -using StardewValley.Locations; -using StardewValley.Menus; - -namespace stardew_access.Patches -{ - internal class DonationMenuPatches - { - internal static string museumQueryKey = " "; - internal static string fieldOfficeMenuQuery = " "; - private static bool isMoving = false; - private static (int x, int y)[] donationTiles = - { - (26,5),(26,6),(26,7),(26,8),(26,9),(26,10),(26,11), - (29,5),(30,5),(31,5),(32,5),(33,5),(34,5),(35,5),(36,5), - (28,6),(29,6),(30,6),(31,6),(32,6),(33,6),(34,6),(35,6),(36,6),(37,6), - (28,9),(29,9),(30,9),(31,9),(32,9),(33,9),(34,9),(35,9),(36,9), - (28,10),(29,10),(30,10),(31,10),(32,10),(33,10),(34,10),(35,10),(36,10), - (30,13),(31,13),(32,13),(33,13),(34,13), - (30,14),(31,14),(32,14),(33,14),(34,14), - (28,15),(29,15),(30,15),(31,15),(32,15),(33,15),(34,15),(35,15),(36,15), - (28,16),(29,16),(30,16),(31,16),(32,16),(33,16),(34,16),(35,16),(36,16), - (39,6),(40,6),(41,6),(42,6),(43,6),(44,6),(45,6),(46,6), - (39,7),(40,7),(41,7),(42,7),(43,7),(44,7),(45,7),(46,7), - (48,5),(48,6),(48,7), - (42,15),(43,15),(44,15),(45,15),(46,15),(47,15), - (42,16),(43,16),(44,16),(45,16),(46,16),(47,16), - }; - - #region Museum - - internal static bool MuseumMenuKeyPressPatch() - { - try - { - if (isMoving) - return false; - - if (!isMoving) - { - isMoving = true; - Task.Delay(200).ContinueWith(_ => { isMoving = false; }); - } - - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - - return true; - } - - internal static void MuseumMenuPatch(MuseumMenu __instance, bool ___holdingMuseumPiece) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - - if (__instance.heldItem != null) - { - // Museum Inventory - string toSpeak = " "; - int tileX = (int)(Utility.ModifyCoordinateFromUIScale(x) + (float)Game1.viewport.X) / 64; - int tileY = (int)(Utility.ModifyCoordinateFromUIScale(y) + (float)Game1.viewport.Y) / 64; - LibraryMuseum libraryMuseum = (LibraryMuseum)Game1.currentLocation; - - if (libraryMuseum.isTileSuitableForMuseumPiece(tileX, tileY)) - toSpeak = $"slot {tileX}x {tileY}y"; - - if (museumQueryKey != toSpeak) - { - museumQueryKey = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - } - else - { - // Player Inventory - int i = InventoryUtils.narrateHoveredSlotAndReturnIndex(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y, - handleHighlightedItem: true, highlightedItemPrefix: "Donatable "); - if (i != -9999) - { - bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); // For donating hovered item - - if (isPrimaryInfoKeyPressed && __instance.inventory.actualInventory[i] != null) - { - foreach (var tile in donationTiles) - { - #region Manually donates the hovered item (https://github.com/veywrn/StardewValley/blob/3ff171b6e9e6839555d7881a391b624ccd820a83/StardewValley/Menus/MuseumMenu.cs#L206-L247) - int tileX = tile.x; - int tileY = tile.y; - - if (((LibraryMuseum)Game1.currentLocation).isTileSuitableForMuseumPiece(tileX, tileY) && ((LibraryMuseum)Game1.currentLocation).isItemSuitableForDonation(__instance.inventory.actualInventory[i])) - { - int objectID = __instance.inventory.actualInventory[i].ParentSheetIndex; - int rewardsCount = ((LibraryMuseum)Game1.currentLocation).getRewardsForPlayer(Game1.player).Count; - ((LibraryMuseum)Game1.currentLocation).museumPieces.Add(new Vector2(tileX, tileY), ((StardewValley.Object)__instance.inventory.actualInventory[i]).ParentSheetIndex); - Game1.playSound("stoneStep"); - if (((LibraryMuseum)Game1.currentLocation).getRewardsForPlayer(Game1.player).Count > rewardsCount) - { - Game1.playSound("reward"); - } - else - { - Game1.playSound("newArtifact"); - } - Game1.player.completeQuest(24); - __instance.inventory.actualInventory[i].Stack--; - if (__instance.inventory.actualInventory[i].Stack <= 0) - { - __instance.inventory.actualInventory[i] = null; - } - int pieces = ((LibraryMuseum)Game1.currentLocation).museumPieces.Count(); - Game1.stats.checkForArchaeologyAchievements(); - switch (pieces) - { - case 95: - globalChatInfoMessage("MuseumComplete", Game1.player.farmName.Value); - break; - case 40: - globalChatInfoMessage("Museum40", Game1.player.farmName.Value); - break; - default: - globalChatInfoMessage("donation", Game1.player.Name, "object:" + objectID); - break; - } - break; - } - #endregion - } - } - } - else if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - { - if (museumQueryKey != $"ok button") - { - museumQueryKey = $"ok button"; - MainClass.ScreenReader.Say("ok button", true); - } - } - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - - #region These methods are taken from the game's source code, https://github.com/veywrn/StardewValley/blob/3ff171b6e9e6839555d7881a391b624ccd820a83/StardewValley/Multiplayer.cs#L1331-L1395 - internal static void globalChatInfoMessage(string messageKey, params string[] args) - { - if (Game1.IsMultiplayer || Game1.multiplayerMode != 0) - { - receiveChatInfoMessage(Game1.player, messageKey, args); - sendChatInfoMessage(messageKey, args); - } - } - - internal static void sendChatInfoMessage(string messageKey, params string[] args) - { - if (Game1.IsClient) - { - Game1.client.sendMessage(15, messageKey, args); - } - else if (Game1.IsServer) - { - foreach (long id in Game1.otherFarmers.Keys) - { - Game1.server.sendMessage(id, 15, Game1.player, messageKey, args); - } - } - } - - internal static void receiveChatInfoMessage(Farmer sourceFarmer, string messageKey, string[] args) - { - if (Game1.chatBox != null) - { - try - { - string[] processedArgs = args.Select(delegate (string arg) - { - if (arg.StartsWith("achievement:")) - { - int key = Convert.ToInt32(arg.Substring("achievement:".Length)); - return Game1.content.Load>("Data\\Achievements")[key].Split('^')[0]; - } - return arg.StartsWith("object:") ? new StardewValley.Object(Convert.ToInt32(arg.Substring("object:".Length)), 1).DisplayName : arg; - }).ToArray(); - ChatBox chatBox = Game1.chatBox; - LocalizedContentManager content = Game1.content; - string path = "Strings\\UI:Chat_" + messageKey; - object[] substitutions = processedArgs; - chatBox.addInfoMessage(content.LoadString(path, substitutions)); - } - catch (ContentLoadException) - { - } - catch (FormatException) - { - } - catch (OverflowException) - { - } - catch (KeyNotFoundException) - { - } - } - } - #endregion - - #endregion - - #region Field Office - - internal static void FieldOfficeMenuPatch(FieldOfficeMenu __instance) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - string toSpeak = " "; - - if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y)) - { - toSpeak = "Trashcan"; - } - else if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - { - toSpeak = "ok button"; - } - else if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) - { - toSpeak = "drop item"; - } - else - { - if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) - return; - - for (int i = 0; i < __instance.pieceHolders.Count; i++) - { - if (!__instance.pieceHolders[i].containsPoint(x, y)) - continue; - - if (__instance.pieceHolders[i].item == null) - toSpeak = i switch - { - 0 => "Center skeleton slot", - 1 => "Center skeleton slot", - 2 => "Center skeleton slot", - 3 => "Center skeleton slot", - 4 => "Center skeleton slot", - 5 => "Center skeleton slot", - 6 => "Snake slot", - 7 => "Snake slot", - 8 => "Snake slot", - 9 => "Bat slot", - 10 => "Frog slot", - _ => "Donation slot" - }; - else - toSpeak = $"Slot {i + 1} finished: {__instance.pieceHolders[i].item.DisplayName}"; - - if (!MainClass.Config.DisableInventoryVerbosity && __instance.heldItem != null && __instance.pieceHolders[i].item == null) - { - int highlight = getPieceIndexForDonationItem(__instance.heldItem.ParentSheetIndex); - if (highlight != -1 && highlight == i) - toSpeak += "Donatable "; - } - - if (fieldOfficeMenuQuery != $"{toSpeak}:{i}") - { - fieldOfficeMenuQuery = $"{toSpeak}:{i}"; - MainClass.ScreenReader.Say(toSpeak, true); - } - - return; - } - } - - if (fieldOfficeMenuQuery != toSpeak) - { - fieldOfficeMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - - if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) - Game1.playSound("drop_item"); - } - } - catch (System.Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static int getPieceIndexForDonationItem(int itemIndex) - { - switch (itemIndex) - { - case 820: - return 5; - case 821: - return 4; - case 822: - return 3; - case 823: - return 0; - case 824: - return 1; - case 825: - return 8; - case 826: - return 7; - case 827: - return 9; - case 828: - return 10; - default: - return -1; - } - } - - #endregion - } -} diff --git a/stardew-access/Patches/DonationMenuPatches/FieldOfficeMenuPatch.cs b/stardew-access/Patches/DonationMenuPatches/FieldOfficeMenuPatch.cs new file mode 100644 index 0000000..5eec4ba --- /dev/null +++ b/stardew-access/Patches/DonationMenuPatches/FieldOfficeMenuPatch.cs @@ -0,0 +1,123 @@ +using StardewValley; +using stardew_access.Features; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class FieldOfficeMenuPatch + { + private static string fieldOfficeMenuQuery = ""; + + internal static void DrawPatch(FieldOfficeMenu __instance) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + string toSpeak = " "; + + if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y)) + { + toSpeak = "Trashcan"; + } + else if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + { + toSpeak = "ok button"; + } + else if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) + { + toSpeak = "drop item"; + } + else + { + if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) + return; + + for (int i = 0; i < __instance.pieceHolders.Count; i++) + { + if (!__instance.pieceHolders[i].containsPoint(x, y)) + continue; + + if (__instance.pieceHolders[i].item == null) + toSpeak = i switch + { + 0 => "Center skeleton slot", + 1 => "Center skeleton slot", + 2 => "Center skeleton slot", + 3 => "Center skeleton slot", + 4 => "Center skeleton slot", + 5 => "Center skeleton slot", + 6 => "Snake slot", + 7 => "Snake slot", + 8 => "Snake slot", + 9 => "Bat slot", + 10 => "Frog slot", + _ => "Donation slot" + }; + else + toSpeak = $"Slot {i + 1} finished: {__instance.pieceHolders[i].item.DisplayName}"; + + if (!MainClass.Config.DisableInventoryVerbosity && __instance.heldItem != null && __instance.pieceHolders[i].item == null) + { + int highlight = getPieceIndexForDonationItem(__instance.heldItem.ParentSheetIndex); + if (highlight != -1 && highlight == i) + toSpeak += "Donatable "; + } + + if (fieldOfficeMenuQuery != $"{toSpeak}:{i}") + { + fieldOfficeMenuQuery = $"{toSpeak}:{i}"; + MainClass.ScreenReader.Say(toSpeak, true); + } + + return; + } + } + + if (fieldOfficeMenuQuery != toSpeak) + { + fieldOfficeMenuQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + + if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) + Game1.playSound("drop_item"); + } + } + catch (System.Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static int getPieceIndexForDonationItem(int itemIndex) + { + switch (itemIndex) + { + case 820: + return 5; + case 821: + return 4; + case 822: + return 3; + case 823: + return 0; + case 824: + return 1; + case 825: + return 8; + case 826: + return 7; + case 827: + return 9; + case 828: + return 10; + default: + return -1; + } + } + + internal static void Cleanup() + { + fieldOfficeMenuQuery = ""; + } + } +} diff --git a/stardew-access/Patches/DonationMenuPatches/MuseumMenuPatch.cs b/stardew-access/Patches/DonationMenuPatches/MuseumMenuPatch.cs new file mode 100644 index 0000000..bd76c97 --- /dev/null +++ b/stardew-access/Patches/DonationMenuPatches/MuseumMenuPatch.cs @@ -0,0 +1,251 @@ +using Microsoft.Xna.Framework; +using StardewValley; +using stardew_access.Features; +using StardewValley.Locations; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class MuseumMenuPatch + { + private static string museumQueryKey = ""; + private static bool isMoving = false; + private static (int x, int y)[] donationTiles = + { + (26,5),(26,6),(26,7),(26,8),(26,9),(26,10),(26,11), + (29,5),(30,5),(31,5),(32,5),(33,5),(34,5),(35,5),(36,5), + (28,6),(29,6),(30,6),(31,6),(32,6),(33,6),(34,6),(35,6),(36,6),(37,6), + (28,9),(29,9),(30,9),(31,9),(32,9),(33,9),(34,9),(35,9),(36,9), + (28,10),(29,10),(30,10),(31,10),(32,10),(33,10),(34,10),(35,10),(36,10), + (30,13),(31,13),(32,13),(33,13),(34,13), + (30,14),(31,14),(32,14),(33,14),(34,14), + (28,15),(29,15),(30,15),(31,15),(32,15),(33,15),(34,15),(35,15),(36,15), + (28,16),(29,16),(30,16),(31,16),(32,16),(33,16),(34,16),(35,16),(36,16), + (39,6),(40,6),(41,6),(42,6),(43,6),(44,6),(45,6),(46,6), + (39,7),(40,7),(41,7),(42,7),(43,7),(44,7),(45,7),(46,7), + (48,5),(48,6),(48,7), + (42,15),(43,15),(44,15),(45,15),(46,15),(47,15), + (42,16),(43,16),(44,16),(45,16),(46,16),(47,16), + }; + + internal static bool RecieveKeyPressPatch() + { + try + { + if (isMoving) + return false; + + if (!isMoving) + { + isMoving = true; + Task.Delay(200).ContinueWith(_ => { isMoving = false; }); + } + + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + + return true; + } + + internal static void DrawPatch(MuseumMenu __instance, bool ___holdingMuseumPiece) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + narrateMuseumInventory(__instance, x, y); + + narratePlayerInventory(__instance, x, y); + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static void narrateMuseumInventory(MuseumMenu __instance, int x, int y) + { + if (__instance.heldItem == null) return; + + string toSpeak = ""; + int tileX = (int)(Utility.ModifyCoordinateFromUIScale(x) + (float)Game1.viewport.X) / 64; + int tileY = (int)(Utility.ModifyCoordinateFromUIScale(y) + (float)Game1.viewport.Y) / 64; + LibraryMuseum libraryMuseum = (LibraryMuseum)Game1.currentLocation; + + if (libraryMuseum.isTileSuitableForMuseumPiece(tileX, tileY)) + toSpeak = $"slot {tileX}x {tileY}y"; + + if (museumQueryKey != toSpeak) + { + museumQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + } + + private static void narratePlayerInventory(MuseumMenu __instance, int x, int y) + { + if (__instance.heldItem != null) return; + + if (narrateHoveredButtons(__instance, x, y)) return; + + int hoveredItemIndex = InventoryUtils.narrateHoveredSlotAndReturnIndex(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y, + handleHighlightedItem: true, highlightedItemPrefix: "Donatable "); + if (hoveredItemIndex != -9999) + { + bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); // For donating hovered item + + if (isPrimaryInfoKeyPressed && hoveredItemIndex >= 0 && hoveredItemIndex < __instance.inventory.actualInventory.Count && __instance.inventory.actualInventory[hoveredItemIndex] != null) + { + manuallyDonateItem(__instance, hoveredItemIndex); + } + } + } + + private static bool narrateHoveredButtons(MuseumMenu __instance, int x, int y) + { + string toSpeak = ""; + bool isDropItemButton = false; + + if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + { + toSpeak = "Ok button"; + } + else if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) + { + toSpeak = "Drop Item"; + isDropItemButton = true; + } + else + { + return false; + } + + if (museumQueryKey != toSpeak) + { + museumQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + if (isDropItemButton) Game1.playSound("drop_item"); + } + + return true; + } + + private static void manuallyDonateItem(MuseumMenu __instance, int i) + { + foreach (var tile in donationTiles) + { + #region Manually donates the hovered item (https://github.com/veywrn/StardewValley/blob/3ff171b6e9e6839555d7881a391b624ccd820a83/StardewValley/Menus/MuseumMenu.cs#L206-L247) + int tileX = tile.x; + int tileY = tile.y; + + if (((LibraryMuseum)Game1.currentLocation).isTileSuitableForMuseumPiece(tileX, tileY) && ((LibraryMuseum)Game1.currentLocation).isItemSuitableForDonation(__instance.inventory.actualInventory[i])) + { + int objectID = __instance.inventory.actualInventory[i].ParentSheetIndex; + int rewardsCount = ((LibraryMuseum)Game1.currentLocation).getRewardsForPlayer(Game1.player).Count; + ((LibraryMuseum)Game1.currentLocation).museumPieces.Add(new Vector2(tileX, tileY), ((StardewValley.Object)__instance.inventory.actualInventory[i]).ParentSheetIndex); + Game1.playSound("stoneStep"); + if (((LibraryMuseum)Game1.currentLocation).getRewardsForPlayer(Game1.player).Count > rewardsCount) + { + Game1.playSound("reward"); + } + else + { + Game1.playSound("newArtifact"); + } + Game1.player.completeQuest(24); + __instance.inventory.actualInventory[i].Stack--; + if (__instance.inventory.actualInventory[i].Stack <= 0) + { + __instance.inventory.actualInventory[i] = null; + } + int pieces = ((LibraryMuseum)Game1.currentLocation).museumPieces.Count(); + Game1.stats.checkForArchaeologyAchievements(); + switch (pieces) + { + case 95: + globalChatInfoMessage("MuseumComplete", Game1.player.farmName.Value); + break; + case 40: + globalChatInfoMessage("Museum40", Game1.player.farmName.Value); + break; + default: + globalChatInfoMessage("donation", Game1.player.Name, "object:" + objectID); + break; + } + break; + } + #endregion + } + } + + #region These methods are taken from the game's source code, https://github.com/veywrn/StardewValley/blob/3ff171b6e9e6839555d7881a391b624ccd820a83/StardewValley/Multiplayer.cs#L1331-L1395 + internal static void globalChatInfoMessage(string messageKey, params string[] args) + { + if (Game1.IsMultiplayer || Game1.multiplayerMode != 0) + { + receiveChatInfoMessage(Game1.player, messageKey, args); + sendChatInfoMessage(messageKey, args); + } + } + + internal static void sendChatInfoMessage(string messageKey, params string[] args) + { + if (Game1.IsClient) + { + Game1.client.sendMessage(15, messageKey, args); + } + else if (Game1.IsServer) + { + foreach (long id in Game1.otherFarmers.Keys) + { + Game1.server.sendMessage(id, 15, Game1.player, messageKey, args); + } + } + } + + internal static void receiveChatInfoMessage(Farmer sourceFarmer, string messageKey, string[] args) + { + if (Game1.chatBox != null) + { + try + { + string[] processedArgs = args.Select(delegate (string arg) + { + if (arg.StartsWith("achievement:")) + { + int key = Convert.ToInt32(arg.Substring("achievement:".Length)); + return Game1.content.Load>("Data\\Achievements")[key].Split('^')[0]; + } + return arg.StartsWith("object:") ? new StardewValley.Object(Convert.ToInt32(arg.Substring("object:".Length)), 1).DisplayName : arg; + }).ToArray(); + ChatBox chatBox = Game1.chatBox; + LocalizedContentManager content = Game1.content; + string path = "Strings\\UI:Chat_" + messageKey; + object[] substitutions = processedArgs; + chatBox.addInfoMessage(content.LoadString(path, substitutions)); + } + catch (Microsoft.Xna.Framework.Content.ContentLoadException) + { + } + catch (FormatException) + { + } + catch (OverflowException) + { + } + catch (KeyNotFoundException) + { + } + } + } + #endregion + + internal static void Cleanup() + { + museumQueryKey = ""; + } + } +} diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs deleted file mode 100644 index f5d0ea2..0000000 --- a/stardew-access/Patches/GameMenuPatches.cs +++ /dev/null @@ -1,1196 +0,0 @@ -using StardewValley; -using StardewValley.Menus; -using StardewValley.Objects; - -namespace stardew_access.Patches -{ - // Menus in the game menu i.e., the menu which opens when we press `e` - internal class GameMenuPatches - { - internal static string hoveredItemQueryKey = ""; - internal static string geodeMenuQueryKey = ""; - internal static string gameMenuQueryKey = ""; - internal static string itemGrabMenuQueryKey = ""; - internal static string craftingPageQueryKey = ""; - internal static string inventoryPageQueryKey = ""; - internal static string exitPageQueryKey = ""; - internal static string optionsPageQueryKey = ""; - internal static string shopMenuQueryKey = ""; - internal static string socialPageQuery = ""; - internal static string profilePageQuery = ""; - internal static int currentSelectedCraftingRecipe = -1; - internal static bool isSelectingRecipe = false; - - internal static void CollectionsPagePatch(CollectionsPage __instance) - { - try - { - int x = Game1.getMousePosition().X, y = Game1.getMousePosition().Y; - if (__instance.letterviewerSubMenu != null) - { - DialoguePatches.NarrateLetterContent(__instance.letterviewerSubMenu); - } - } - catch (System.Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void SocialPagePatch(SocialPage __instance, List ___sprites, int ___slotPosition, List ___kidsNames) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - for (int i = ___slotPosition; i < ___slotPosition + 5; i++) - { - if (i < ___sprites.Count) - { - if (__instance.names[i] is string) - { - #region For NPCs - if (__instance.characterSlots[i].bounds.Contains(Game1.getMouseX(true), Game1.getMouseY(true))) - { - string name = $"{__instance.names[i] as string}"; - int heartLevel = Game1.player.getFriendshipHeartLevelForNPC(name); - bool datable = SocialPage.isDatable(name); - Friendship friendship = __instance.getFriendship(name); - int giftsThisWeek = friendship.GiftsThisWeek; - bool hasTalked = Game1.player.hasPlayerTalkedToNPC(name); - bool spouse = friendship.IsMarried(); - bool housemate = spouse && SocialPage.isRoommateOfAnyone(name); - ___kidsNames.Add("Robin"); - ___kidsNames.Add("Pierre"); - ___kidsNames.Add("Caroline"); - ___kidsNames.Add("Jodi"); - ___kidsNames.Add("Kent"); - ___kidsNames.Add("George"); - ___kidsNames.Add("Evelyn"); - ___kidsNames.Add("Demetrius"); - - - - string toSpeak = $"{name}"; - - if (!hasTalked) - { - toSpeak = $"{toSpeak}, not talked yet"; - } - - - if (datable | housemate) - { - string text2 = (LocalizedContentManager.CurrentLanguageCode != LocalizedContentManager.LanguageCode.pt) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11635") : ((__instance.getGender(name) == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11635").Split('/').First() : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11635").Split('/').Last()); - if (housemate) - { - text2 = Game1.content.LoadString("Strings\\StringsFromCSFiles:Housemate"); - } - else if (spouse) - { - text2 = ((__instance.getGender(name) == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11636") : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11637")); - } - else if (__instance.isMarriedToAnyone(name)) - { - text2 = ((__instance.getGender(name) == 0) ? Game1.content.LoadString("Strings\\UI:SocialPage_MarriedToOtherPlayer_MaleNPC") : Game1.content.LoadString("Strings\\UI:SocialPage_MarriedToOtherPlayer_FemaleNPC")); - } - else if (!Game1.player.isMarried() && friendship.IsDating()) - { - text2 = ((__instance.getGender(name) == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11639") : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11640")); - } - else if (__instance.getFriendship(name).IsDivorced()) - { - text2 = ((__instance.getGender(name) == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11642") : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11643")); - } - - toSpeak = $"{toSpeak}, {text2}"; - } - if (!__instance.getFriendship(name).IsMarried() && ___kidsNames.Contains(name)) - { - toSpeak = $"{toSpeak}, married"; - } - if (spouse) - { - toSpeak = $"{toSpeak}, spouse"; - } - else if (friendship.IsDating()) - { - toSpeak = $"{toSpeak}, dating"; - } - - toSpeak = $"{toSpeak}, {heartLevel} hearts, {giftsThisWeek} gifts given this week."; - - - if (socialPageQuery != toSpeak) - { - socialPageQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - #endregion - } - else if (__instance.names[i] is long) - { - #region For Farmers - - long farmerID = (long)__instance.names[i]; - Farmer farmer = Game1.getFarmerMaybeOffline(farmerID); - if (farmer != null) - { - int gender = (!farmer.IsMale) ? 1 : 0; - ClickableTextureComponent clickableTextureComponent = ___sprites[i]; - if (clickableTextureComponent.containsPoint(x, y)) - { - Friendship friendship = Game1.player.team.GetFriendship(Game1.player.UniqueMultiplayerID, farmerID); - bool spouse = friendship.IsMarried(); - string toSpeak = ""; - - string text2 = (LocalizedContentManager.CurrentLanguageCode != LocalizedContentManager.LanguageCode.pt) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11635") : ((gender == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11635").Split('/').First() : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11635").Split('/').Last()); - if (spouse) - { - text2 = ((gender == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11636") : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11637")); - } - else if (farmer.isMarried() && !farmer.hasRoommate()) - { - text2 = ((gender == 0) ? Game1.content.LoadString("Strings\\UI:SocialPage_MarriedToOtherPlayer_MaleNPC") : Game1.content.LoadString("Strings\\UI:SocialPage_MarriedToOtherPlayer_FemaleNPC")); - } - else if (!Game1.player.isMarried() && friendship.IsDating()) - { - text2 = ((gender == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11639") : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11640")); - } - else if (friendship.IsDivorced()) - { - text2 = ((gender == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11642") : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11643")); - } - - toSpeak = $"{farmer.displayName}, {text2}"; - - if (socialPageQuery != toSpeak) - { - socialPageQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - } - - #endregion - } - } - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void ShopMenuPatch(ShopMenu __instance) - { - 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) - { - __instance.inventory.inventory[0].snapMouseCursorToCenter(); - __instance.setCurrentlySnappedComponentTo(__instance.inventory.inventory[0].myID); - } - else if (MainClass.Config.SnapToFirstSecondaryInventorySlotKey.JustPressed() && __instance.forSaleButtons.Count > 0) - { - __instance.forSaleButtons[0].snapMouseCursorToCenter(); - __instance.setCurrentlySnappedComponentTo(__instance.forSaleButtons[0].myID); - } - - #region Narrate buttons in the menu - if (__instance.inventory.dropItemInvisibleButton != null && __instance.inventory.dropItemInvisibleButton.containsPoint(x, y)) - { - string toSpeak = "Drop Item"; - if (shopMenuQueryKey != toSpeak) - { - shopMenuQueryKey = toSpeak; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - Game1.playSound("drop_item"); - } - return; - } - if (__instance.upArrow != null && __instance.upArrow.containsPoint(x, y)) - { - string toSpeak = "Up Arrow Button"; - if (shopMenuQueryKey != toSpeak) - { - shopMenuQueryKey = toSpeak; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - if (__instance.downArrow != null && __instance.downArrow.containsPoint(x, y)) - { - string toSpeak = "Down Arrow Button"; - if (shopMenuQueryKey != toSpeak) - { - shopMenuQueryKey = toSpeak; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - #endregion - - #region Narrate hovered item - if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y, hoverPrice: __instance.hoverPrice)) - { - shopMenuQueryKey = ""; - return; - } - #endregion - - #region Narrate hovered selling item - if (__instance.hoveredItem != null) - { - string name = __instance.hoveredItem.DisplayName; - string price = $"Buy Price: {__instance.hoverPrice} g"; - string description = __instance.hoveredItem.getDescription(); - string requirements = ""; - - #region Narrate required items for item - int itemIndex = -1, itemAmount = 5; - - if (__instance.itemPriceAndStock[__instance.hoveredItem].Length > 2) - itemIndex = __instance.itemPriceAndStock[__instance.hoveredItem][2]; - - if (__instance.itemPriceAndStock[__instance.hoveredItem].Length > 3) - itemAmount = __instance.itemPriceAndStock[__instance.hoveredItem][3]; - - if (itemIndex != -1) - { - string itemName = Game1.objectInformation[itemIndex].Split('/')[0]; - - if (itemAmount != -1) - requirements = $"Required: {itemAmount} {itemName}"; - else - requirements = $"Required: {itemName}"; - } - #endregion - - string toSpeak = $"{name}, {requirements}, {price}, \n\t{description}"; - if (shopMenuQueryKey != toSpeak) - { - shopMenuQueryKey = toSpeak; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - } - #endregion - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void GameMenuPatch(GameMenu __instance) - { - try - { - // Continue if only in the Inventory Page or Crafting Page - if (__instance.currentTab != 0 && __instance.currentTab != 4 && __instance.currentTab != 6 && __instance.currentTab != 7) - return; - - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - - for (int i = 0; i < __instance.tabs.Count; i++) - { - if (__instance.tabs[i].containsPoint(x, y)) - { - string toSpeak = $"{GameMenu.getLabelOfTabFromIndex(i)} Tab"; - if (gameMenuQueryKey != toSpeak) - { - gameMenuQueryKey = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void GeodeMenuPatch(GeodeMenu __instance) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - - #region Narrate the treasure recieved on breaking the geode - if (__instance.geodeTreasure != null) - { - string name = __instance.geodeTreasure.DisplayName; - int stack = __instance.geodeTreasure.Stack; - - string toSpeak = $"Recieved {stack} {name}"; - - if (geodeMenuQueryKey != toSpeak) - { - geodeMenuQueryKey = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - #endregion - - #region Narrate hovered buttons in the menu - if (__instance.geodeSpot != null && __instance.geodeSpot.containsPoint(x, y)) - { - string toSpeak = "Place geode here"; - if (geodeMenuQueryKey != toSpeak) - { - geodeMenuQueryKey = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) - { - string toSpeak = "Drop item here"; - - if (geodeMenuQueryKey != toSpeak) - { - geodeMenuQueryKey = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - Game1.playSound("drop_item"); - } - return; - } - - if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y)) - { - string toSpeak = "Trash can"; - - if (geodeMenuQueryKey != toSpeak) - { - geodeMenuQueryKey = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - { - string toSpeak = "Ok button"; - - if (geodeMenuQueryKey != toSpeak) - { - geodeMenuQueryKey = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - #endregion - - #region Narrate hovered item - if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) - geodeMenuQueryKey = ""; - #endregion - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void ItemGrabMenuPatch(ItemGrabMenu __instance) - { - 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) - { - __instance.setCurrentlySnappedComponentTo(__instance.inventory.inventory[0].myID); - __instance.inventory.inventory[0].snapMouseCursorToCenter(); - } - else if (MainClass.Config.SnapToFirstSecondaryInventorySlotKey.JustPressed() && __instance.ItemsToGrabMenu.inventory.Count > 0 && !__instance.shippingBin) - { - __instance.setCurrentlySnappedComponentTo(__instance.ItemsToGrabMenu.inventory[0].myID); - __instance.ItemsToGrabMenu.inventory[0].snapMouseCursorToCenter(); - } - - #region Narrate buttons in the menu - if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - { - string toSpeak = "Ok Button"; - if (itemGrabMenuQueryKey != toSpeak) - { - itemGrabMenuQueryKey = toSpeak; - hoveredItemQueryKey = ""; - gameMenuQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y)) - { - string toSpeak = "Trash Can"; - if (itemGrabMenuQueryKey != toSpeak) - { - itemGrabMenuQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.organizeButton != null && __instance.organizeButton.containsPoint(x, y)) - { - string toSpeak = "Organize Button"; - if (itemGrabMenuQueryKey != toSpeak) - { - itemGrabMenuQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.fillStacksButton != null && __instance.fillStacksButton.containsPoint(x, y)) - { - string toSpeak = "Add to existing stacks button"; - if (itemGrabMenuQueryKey != toSpeak) - { - itemGrabMenuQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.specialButton != null && __instance.specialButton.containsPoint(x, y)) - { - string toSpeak = "Special Button"; - if (itemGrabMenuQueryKey != toSpeak) - { - itemGrabMenuQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.colorPickerToggleButton != null && __instance.colorPickerToggleButton.containsPoint(x, y)) - { - - string toSpeak = "Color Picker: " + (__instance.chestColorPicker.visible ? "Enabled" : "Disabled"); - if (itemGrabMenuQueryKey != toSpeak) - { - itemGrabMenuQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.junimoNoteIcon != null && __instance.junimoNoteIcon.containsPoint(x, y)) - { - - string toSpeak = "Community Center Button"; - if (itemGrabMenuQueryKey != toSpeak) - { - itemGrabMenuQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) - { - string toSpeak = "Drop Item"; - if (itemGrabMenuQueryKey != toSpeak) - { - itemGrabMenuQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - Game1.playSound("drop_item"); - } - return; - } - - // FIXME - /*if (__instance.discreteColorPickerCC.Count > 0) { - for (int i = 0; i < __instance.discreteColorPickerCC.Count; i++) - { - if (__instance.discreteColorPickerCC[i].containsPoint(x, y)) - { - MainClass.monitor.Log(i.ToString(), LogLevel.Debug); - string toSpeak = getChestColorName(i); - if (itemGrabMenuQueryKey != toSpeak) - { - itemGrabMenuQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - ScreenReader.say(toSpeak, true); - Game1.playSound("sa_drop_item"); - } - return; - } - } - }*/ - #endregion - - #region Narrate the last shipped item if in the shipping bin - if (__instance.shippingBin && Game1.getFarm().lastItemShipped != null && __instance.lastShippedHolder.containsPoint(x, y)) - { - Item lastShippedItem = Game1.getFarm().lastItemShipped; - string name = lastShippedItem.DisplayName; - int count = lastShippedItem.Stack; - - string toSpeak = $"Last Shipped: {count} {name}"; - - if (itemGrabMenuQueryKey != toSpeak) - { - itemGrabMenuQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - #endregion - - #region Narrate hovered item - if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y, true)) - { - gameMenuQueryKey = ""; - itemGrabMenuQueryKey = ""; - return; - } - - if (InventoryUtils.narrateHoveredSlot(__instance.ItemsToGrabMenu, __instance.ItemsToGrabMenu.inventory, __instance.ItemsToGrabMenu.actualInventory, x, y, true)) - { - gameMenuQueryKey = ""; - itemGrabMenuQueryKey = ""; - return; - } - - #endregion - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - // TODO Add color names - private static string getChestColorName(int i) - { - string toReturn = ""; - switch (i) - { - case 0: - toReturn = "Default chest color"; - break; - case 1: - toReturn = "Default chest color"; - break; - case 2: - toReturn = "Default chest color"; - break; - case 3: - toReturn = "Default chest color"; - break; - case 4: - toReturn = "Default chest color"; - break; - case 5: - toReturn = "Default chest color"; - break; - case 6: - toReturn = "Default chest color"; - break; - case 7: - toReturn = "Default chest color"; - break; - case 8: - toReturn = "Default chest color"; - break; - case 9: - toReturn = "Default chest color"; - break; - case 10: - toReturn = "Default chest color"; - break; - case 11: - toReturn = "Default chest color"; - break; - case 12: - toReturn = "Default chest color"; - break; - case 13: - toReturn = "Default chest color"; - break; - case 14: - toReturn = "Default chest color"; - break; - case 15: - toReturn = "Default chest color"; - break; - case 16: - toReturn = "Default chest color"; - break; - case 17: - toReturn = "Default chest color"; - break; - case 18: - toReturn = "Default chest color"; - break; - case 19: - toReturn = "Default chest color"; - break; - case 20: - toReturn = "Default chest color"; - break; - } - return toReturn; - } - - 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 - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - - #region Narrate buttons in the menu - if (__instance.inventory.dropItemInvisibleButton != null && __instance.inventory.dropItemInvisibleButton.containsPoint(x, y)) - { - string toSpeak = "Drop Item"; - if (inventoryPageQueryKey != toSpeak) - { - inventoryPageQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - Game1.playSound("drop_item"); - } - } - - if (__instance.organizeButton != null && __instance.organizeButton.containsPoint(x, y)) - { - string toSpeak = "Organize Inventory Button"; - if (inventoryPageQueryKey != toSpeak) - { - inventoryPageQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - } - - if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y)) - { - string toSpeak = "Trash Can"; - if (inventoryPageQueryKey != toSpeak) - { - inventoryPageQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - } - - if (__instance.organizeButton != null && __instance.organizeButton.containsPoint(x, y)) - { - string toSpeak = "Organize Button"; - if (itemGrabMenuQueryKey != toSpeak) - { - itemGrabMenuQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - } - - if (__instance.junimoNoteIcon != null && __instance.junimoNoteIcon.containsPoint(x, y)) - { - - string toSpeak = "Community Center Button"; - if (itemGrabMenuQueryKey != toSpeak) - { - itemGrabMenuQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - } - #endregion - - #region Narrate equipment slots - for (int i = 0; i < __instance.equipmentIcons.Count; i++) - { - if (__instance.equipmentIcons[i].containsPoint(x, y)) - { - string toSpeak = ""; - - #region Get name and description of the item - switch (__instance.equipmentIcons[i].name) - { - case "Hat": - { - if (Game1.player.hat.Value != null) - { - toSpeak = $"{Game1.player.hat.Value.DisplayName}, {Game1.player.hat.Value.getDescription()}"; - } - else - { - toSpeak = "Hat slot"; - } - } - break; - case "Left Ring": - { - if (Game1.player.leftRing.Value != null) - { - toSpeak = $"{Game1.player.leftRing.Value.DisplayName}, {Game1.player.leftRing.Value.getDescription()}"; - } - else - { - toSpeak = "Left Ring slot"; - } - } - break; - case "Right Ring": - { - if (Game1.player.rightRing.Value != null) - { - toSpeak = $"{Game1.player.rightRing.Value.DisplayName}, {Game1.player.rightRing.Value.getDescription()}"; - } - else - { - toSpeak = "Right ring slot"; - } - } - break; - case "Boots": - { - if (Game1.player.boots.Value != null) - { - toSpeak = $"{Game1.player.boots.Value.DisplayName}, {Game1.player.boots.Value.getDescription()}"; - } - else - { - toSpeak = "Boots slot"; - } - } - break; - case "Shirt": - { - if (Game1.player.shirtItem.Value != null) - { - toSpeak = $"{Game1.player.shirtItem.Value.DisplayName}, {Game1.player.shirtItem.Value.getDescription()}"; - } - else - { - toSpeak = "Shirt slot"; - } - } - break; - case "Pants": - { - if (Game1.player.pantsItem.Value != null) - { - toSpeak = $"{Game1.player.pantsItem.Value.DisplayName}, {Game1.player.pantsItem.Value.getDescription()}"; - } - else - { - toSpeak = "Pants slot"; - } - } - break; - } - #endregion - - if (inventoryPageQueryKey != toSpeak) - { - inventoryPageQueryKey = toSpeak; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.ScreenReader.Say(toSpeak, true); - } - } - } - #endregion - - #region Narrate hovered item - if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y, true)) - { - gameMenuQueryKey = ""; - inventoryPageQueryKey = ""; - } - #endregion - - if (MainClass.Config.MoneyKey.JustPressed()) - { - string farmName = Game1.content.LoadString("Strings\\UI:Inventory_FarmName", Game1.player.farmName.Value); - string currentFunds = Game1.content.LoadString("Strings\\UI:Inventory_CurrentFunds" + (Game1.player.useSeparateWallets ? "_Separate" : ""), Utility.getNumberWithCommas(Game1.player.Money)); - string totalEarnings = Game1.content.LoadString("Strings\\UI:Inventory_TotalEarnings" + (Game1.player.useSeparateWallets ? "_Separate" : ""), Utility.getNumberWithCommas((int)Game1.player.totalMoneyEarned)); - int festivalScore = Game1.player.festivalScore; - int walnut = Game1.netWorldState.Value.GoldenWalnuts.Value; - int qiGems = Game1.player.QiGems; - int qiCoins = Game1.player.clubCoins; - - string toSpeak = $"{farmName}\n{currentFunds}\n{totalEarnings}"; - - if (festivalScore > 0) - toSpeak = $"{toSpeak}\nFestival Score: {festivalScore}"; - - if (walnut > 0) - toSpeak = $"{toSpeak}\nGolden Walnut: {walnut}"; - - if (qiGems > 0) - toSpeak = $"{toSpeak}\nQi Gems: {qiGems}"; - - if (qiCoins > 0) - toSpeak = $"{toSpeak}\nQi Club Coins: {qiCoins}"; - - MainClass.ScreenReader.Say(toSpeak, true); - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void OptionsPagePatch(OptionsPage __instance) - { - try - { - int currentItemIndex = Math.Max(0, Math.Min(__instance.options.Count - 7, __instance.currentItemIndex)); - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); - for (int i = 0; i < __instance.optionSlots.Count; i++) - { - if (__instance.optionSlots[i].bounds.Contains(x, y) && currentItemIndex + i < __instance.options.Count && __instance.options[currentItemIndex + i].bounds.Contains(x - __instance.optionSlots[i].bounds.X, y - __instance.optionSlots[i].bounds.Y)) - { - OptionsElement optionsElement = __instance.options[currentItemIndex + i]; - string toSpeak = optionsElement.label; - - if (optionsElement is OptionsButton) - toSpeak = $" {toSpeak} Button"; - else if (optionsElement is OptionsCheckbox) - toSpeak = (((OptionsCheckbox)optionsElement).isChecked ? "Enabled" : "Disabled") + $" {toSpeak} Checkbox"; - else if (optionsElement is OptionsDropDown) - toSpeak = $"{toSpeak} Dropdown, option {((OptionsDropDown)optionsElement).dropDownDisplayOptions[((OptionsDropDown)optionsElement).selectedOption]} selected"; - else if (optionsElement is OptionsSlider) - toSpeak = $"{((OptionsSlider)optionsElement).value}% {toSpeak} Slider"; - else if (optionsElement is OptionsPlusMinus) - toSpeak = $"{((OptionsPlusMinus)optionsElement).displayOptions[((OptionsPlusMinus)optionsElement).selected]} selected of {toSpeak}"; - else if (optionsElement is OptionsInputListener) - { - string buttons = ""; - ((OptionsInputListener)optionsElement).buttonNames.ForEach(name => { buttons += $", {name}"; }); - toSpeak = $"{toSpeak} is bound to {buttons}. Left click to change."; - } - else - { - if (toSpeak.Contains(":")) - toSpeak = toSpeak.Replace(":", ""); - - toSpeak = $"{toSpeak} Options:"; - } - - if (optionsPageQueryKey != toSpeak) - { - gameMenuQueryKey = ""; - optionsPageQueryKey = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void ExitPagePatch(ExitPage __instance) - { - try - { - if (__instance.exitToTitle.visible && - __instance.exitToTitle.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) - { - string toSpeak = "Exit to Title Button"; - if (exitPageQueryKey != toSpeak) - { - gameMenuQueryKey = ""; - exitPageQueryKey = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - if (__instance.exitToDesktop.visible && - __instance.exitToDesktop.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) - { - string toSpeak = "Exit to Desktop Button"; - if (exitPageQueryKey != toSpeak) - { - gameMenuQueryKey = ""; - exitPageQueryKey = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - } -} diff --git a/stardew-access/Patches/GameMenuPatches/CollectionsPagePatch.cs b/stardew-access/Patches/GameMenuPatches/CollectionsPagePatch.cs new file mode 100644 index 0000000..fdbaee4 --- /dev/null +++ b/stardew-access/Patches/GameMenuPatches/CollectionsPagePatch.cs @@ -0,0 +1,21 @@ +namespace stardew_access.Patches +{ + internal class CollectionsPagePatch + { + internal static void DrawPatch(StardewValley.Menus.CollectionsPage __instance) + { + try + { + int x = StardewValley.Game1.getMousePosition().X, y = StardewValley.Game1.getMousePosition().Y; + if (__instance.letterviewerSubMenu != null) + { + LetterViwerMenuPatch.narrateLetterContent(__instance.letterviewerSubMenu); + } + } + catch (System.Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + } +} diff --git a/stardew-access/Patches/GameMenuPatches/CraftingPagePatch.cs b/stardew-access/Patches/GameMenuPatches/CraftingPagePatch.cs new file mode 100644 index 0000000..1cad73e --- /dev/null +++ b/stardew-access/Patches/GameMenuPatches/CraftingPagePatch.cs @@ -0,0 +1,251 @@ +using StardewValley; +using stardew_access.Features; +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 + + handleKeyBinds(__instance, ___currentCraftingPage); + + if (narrateMenuButtons(__instance, x, y)) + { + return; + } + + if (narrateHoveredRecipe(__instance, ___currentCraftingPage, ___hoverRecipe, x, y)) + { + return; + } + + if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) + { + craftingPageQueryKey = ""; + return; + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static void handleKeyBinds(CraftingPage __instance, int ___currentCraftingPage) + { + 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.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.CraftingMenuCycleThroughRecipiesKey.JustPressed() && !isSelectingRecipe) + { + isSelectingRecipe = true; + CycleThroughRecipies(__instance.pagesOfCraftingRecipes, ___currentCraftingPage, __instance); + Task.Delay(200).ContinueWith(_ => { isSelectingRecipe = false; }); + } + } + + private static bool narrateMenuButtons(CraftingPage __instance, int x, int y) + { + string? toSpeak = null; + bool isDropItemButton = false; + + if (__instance.upButton != null && __instance.upButton.containsPoint(x, y)) + { + toSpeak = "Previous Recipe List"; + } + else if (__instance.downButton != null && __instance.downButton.containsPoint(x, y)) + { + toSpeak = "Next Recipe List"; + } + else if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y)) + { + toSpeak = "Trash Can"; + } + else if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) + { + toSpeak = "Drop Item"; + isDropItemButton = true; + } + else + { + return false; + } + + if (toSpeak != null && craftingPageQueryKey != toSpeak) + { + craftingPageQueryKey = toSpeak; + hoveredItemQueryKey = ""; + MainClass.ScreenReader.Say(toSpeak, true); + if (isDropItemButton) Game1.playSound("drop_item"); + } + + return true; + } + + private static bool narrateHoveredRecipe(CraftingPage __instance, int ___currentCraftingPage, CraftingRecipe ___hoverRecipe, int x, int y) + { + if (___hoverRecipe == null) + { + var isRecipeInFocus = false; + foreach (var item in __instance.pagesOfCraftingRecipes[___currentCraftingPage]) + { + if (!item.Key.containsPoint(x, y)) + continue; + + isRecipeInFocus = true; + break; + } + + if (!isRecipeInFocus) + return false; + + string query = $"unknown recipe:{__instance.getCurrentlySnappedComponent().myID}"; + + if (craftingPageQueryKey != query) + { + craftingPageQueryKey = query; + hoveredItemQueryKey = ""; + MainClass.ScreenReader.Say("unknown recipe", true); + } + return true; + } + + 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 true; + } + + 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/GameMenuPatches/ExitPagePatch.cs b/stardew-access/Patches/GameMenuPatches/ExitPagePatch.cs new file mode 100644 index 0000000..81431f7 --- /dev/null +++ b/stardew-access/Patches/GameMenuPatches/ExitPagePatch.cs @@ -0,0 +1,48 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class ExitPagePatch + { + internal static string exitPageQueryKey = ""; + + internal static void DrawPatch(ExitPage __instance) + { + try + { + if (__instance.exitToTitle.visible && + __instance.exitToTitle.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) + { + string toSpeak = "Exit to Title Button"; + if (exitPageQueryKey != toSpeak) + { + exitPageQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + return; + } + if (__instance.exitToDesktop.visible && + __instance.exitToDesktop.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) + { + string toSpeak = "Exit to Desktop Button"; + if (exitPageQueryKey != toSpeak) + { + exitPageQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + return; + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static void Cleanup() + { + exitPageQueryKey = ""; + } + } +} diff --git a/stardew-access/Patches/GameMenuPatches/GameMenuPatch.cs b/stardew-access/Patches/GameMenuPatches/GameMenuPatch.cs new file mode 100644 index 0000000..0255dbb --- /dev/null +++ b/stardew-access/Patches/GameMenuPatches/GameMenuPatch.cs @@ -0,0 +1,48 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class GameMenuPatch + { + internal static string gameMenuQueryKey = ""; + + internal static void DrawPatch(GameMenu __instance) + { + try + { + // Skip if in map page + if (__instance.currentTab == 3) + return; + + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + for (int i = 0; i < __instance.tabs.Count; i++) + { + if (!__instance.tabs[i].containsPoint(x, y)) + continue; + + string toSpeak = $"{GameMenu.getLabelOfTabFromIndex(i)} Tab" + ((i == __instance.currentTab) ? " Active" : ""); + if (gameMenuQueryKey != toSpeak) + { + gameMenuQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + return; + } + + // If not hovering on any tab button + Cleanup(); + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static void Cleanup() + { + gameMenuQueryKey = ""; + } + } +} diff --git a/stardew-access/Patches/GameMenuPatches/InventoryPagePatch.cs b/stardew-access/Patches/GameMenuPatches/InventoryPagePatch.cs new file mode 100644 index 0000000..cbb8dd5 --- /dev/null +++ b/stardew-access/Patches/GameMenuPatches/InventoryPagePatch.cs @@ -0,0 +1,157 @@ +using stardew_access.Features; +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class InventoryPagePatch + { + internal static string inventoryPageQueryKey = ""; + internal static string hoveredItemQueryKey = ""; + + internal static void DrawPatch(InventoryPage __instance) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + handleKeyBinds(); + + if (narrateHoveredButton(__instance, x, y)) + { + return; + } + + if (narrateHoveredEquipmentSlot(__instance, x, y)) + { + return; + } + + if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y, true)) + { + inventoryPageQueryKey = ""; + return; + } + + // If no slot or button is hovered + Cleanup(); + } + catch (Exception e) + { + MainClass.ErrorLog($"An error occured in InventoryPagePatch()->DrawPatch():\n{e.Message}\n{e.StackTrace}"); + } + } + + private static void handleKeyBinds() + { + if (!MainClass.Config.MoneyKey.JustPressed()) + return; + + string farmName = Game1.content.LoadString("Strings\\UI:Inventory_FarmName", Game1.player.farmName.Value); + string currentFunds = Game1.content.LoadString("Strings\\UI:Inventory_CurrentFunds" + (Game1.player.useSeparateWallets ? "_Separate" : ""), Utility.getNumberWithCommas(Game1.player.Money)); + string totalEarnings = Game1.content.LoadString("Strings\\UI:Inventory_TotalEarnings" + (Game1.player.useSeparateWallets ? "_Separate" : ""), Utility.getNumberWithCommas((int)Game1.player.totalMoneyEarned)); + int festivalScore = Game1.player.festivalScore; + int walnut = Game1.netWorldState.Value.GoldenWalnuts.Value; + int qiGems = Game1.player.QiGems; + int qiCoins = Game1.player.clubCoins; + + string toSpeak = $"{farmName}\n{currentFunds}\n{totalEarnings}"; + + if (festivalScore > 0) + toSpeak = $"{toSpeak}\nFestival Score: {festivalScore}"; + + if (walnut > 0) + toSpeak = $"{toSpeak}\nGolden Walnut: {walnut}"; + + if (qiGems > 0) + toSpeak = $"{toSpeak}\nQi Gems: {qiGems}"; + + if (qiCoins > 0) + toSpeak = $"{toSpeak}\nQi Club Coins: {qiCoins}"; + + MainClass.ScreenReader.Say(toSpeak, true); + } + + private static bool narrateHoveredButton(InventoryPage __instance, int x, int y) + { + string? toSpeak = null; + bool isDropItemButton = false; + + if (__instance.inventory.dropItemInvisibleButton != null && __instance.inventory.dropItemInvisibleButton.containsPoint(x, y)) + { + toSpeak = "Drop Item"; + isDropItemButton = true; + } + else if (__instance.organizeButton != null && __instance.organizeButton.containsPoint(x, y)) + { + toSpeak = "Organize Inventory Button"; + } + else if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y)) + { + toSpeak = "Trash Can"; + } + else if (__instance.organizeButton != null && __instance.organizeButton.containsPoint(x, y)) + { + toSpeak = "Organize Button"; + } + else if (__instance.junimoNoteIcon != null && __instance.junimoNoteIcon.containsPoint(x, y)) + { + toSpeak = "Community Center Button"; + } + else + { + return false; + } + + if (toSpeak != null && inventoryPageQueryKey != toSpeak) + { + inventoryPageQueryKey = toSpeak; + hoveredItemQueryKey = ""; + MainClass.ScreenReader.Say(toSpeak, true); + if (isDropItemButton) Game1.playSound("drop_item"); + } + + return true; + } + + private static bool narrateHoveredEquipmentSlot(InventoryPage __instance, int mouseX, int mouseY) + { + for (int i = 0; i < __instance.equipmentIcons.Count; i++) + { + if (!__instance.equipmentIcons[i].containsPoint(mouseX, mouseY)) + continue; + + string toSpeak = getNameAndDescriptionOfItem(__instance.equipmentIcons[i].name); + + if (inventoryPageQueryKey != toSpeak) + { + inventoryPageQueryKey = toSpeak; + hoveredItemQueryKey = ""; + MainClass.ScreenReader.Say(toSpeak, true); + } + + return true; + } + + return false; + } + + private static string getNameAndDescriptionOfItem(string slotName) => slotName switch + { + "Hat" => (Game1.player.hat.Value != null) ? $"{Game1.player.hat.Value.DisplayName}, {Game1.player.hat.Value.getDescription()}" : "Hat slot", + "Left Ring" => (Game1.player.leftRing.Value != null) ? $"{Game1.player.leftRing.Value.DisplayName}, {Game1.player.leftRing.Value.getDescription()}" : "Left Ring slot", + "Right Ring" => (Game1.player.rightRing.Value != null) ? $"{Game1.player.rightRing.Value.DisplayName}, {Game1.player.rightRing.Value.getDescription()}" : "Right ring slot", + "Boots" => (Game1.player.boots.Value != null) ? $"{Game1.player.boots.Value.DisplayName}, {Game1.player.boots.Value.getDescription()}" : "Boots slot", + "Shirt" => (Game1.player.shirtItem.Value != null) ? $"{Game1.player.shirtItem.Value.DisplayName}, {Game1.player.shirtItem.Value.getDescription()}" : "Shirt slot", + "Pants" => (Game1.player.pantsItem.Value != null) ? $"{Game1.player.pantsItem.Value.DisplayName}, {Game1.player.pantsItem.Value.getDescription()}" : "Pants slot", + _ => "unkown slot" + }; + + internal static void Cleanup() + { + InventoryUtils.Cleanup(); + inventoryPageQueryKey = ""; + hoveredItemQueryKey = ""; + } + } +} diff --git a/stardew-access/Patches/GameMenuPatches/OptionsPagePatch.cs b/stardew-access/Patches/GameMenuPatches/OptionsPagePatch.cs new file mode 100644 index 0000000..f4d9879 --- /dev/null +++ b/stardew-access/Patches/GameMenuPatches/OptionsPagePatch.cs @@ -0,0 +1,67 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class OptionsPagePatch + { + internal static string optionsPageQueryKey = ""; + + internal static void DrawPatch(OptionsPage __instance) + { + try + { + int currentItemIndex = Math.Max(0, Math.Min(__instance.options.Count - 7, __instance.currentItemIndex)); + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); + for (int i = 0; i < __instance.optionSlots.Count; i++) + { + if (!__instance.optionSlots[i].bounds.Contains(x, y) || currentItemIndex + i >= __instance.options.Count || !__instance.options[currentItemIndex + i].bounds.Contains(x - __instance.optionSlots[i].bounds.X, y - __instance.optionSlots[i].bounds.Y)) + continue; + + OptionsElement optionsElement = __instance.options[currentItemIndex + i]; + string toSpeak = optionsElement.label; + + if (optionsElement is OptionsButton) + toSpeak = $" {toSpeak} Button"; + else if (optionsElement is OptionsCheckbox) + toSpeak = (((OptionsCheckbox)optionsElement).isChecked ? "Enabled" : "Disabled") + $" {toSpeak} Checkbox"; + else if (optionsElement is OptionsDropDown) + toSpeak = $"{toSpeak} Dropdown, option {((OptionsDropDown)optionsElement).dropDownDisplayOptions[((OptionsDropDown)optionsElement).selectedOption]} selected"; + else if (optionsElement is OptionsSlider) + toSpeak = $"{((OptionsSlider)optionsElement).value}% {toSpeak} Slider"; + else if (optionsElement is OptionsPlusMinus) + toSpeak = $"{((OptionsPlusMinus)optionsElement).displayOptions[((OptionsPlusMinus)optionsElement).selected]} selected of {toSpeak}"; + else if (optionsElement is OptionsInputListener) + { + string buttons = ""; + ((OptionsInputListener)optionsElement).buttonNames.ForEach(name => { buttons += $", {name}"; }); + toSpeak = $"{toSpeak} is bound to {buttons}. Left click to change."; + } + else + { + if (toSpeak.Contains(":")) + toSpeak = toSpeak.Replace(":", ""); + + toSpeak = $"{toSpeak} Options:"; + } + + if (optionsPageQueryKey != toSpeak) + { + optionsPageQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + return; + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static void Cleanup() + { + optionsPageQueryKey = ""; + } + } +} diff --git a/stardew-access/Patches/GameMenuPatches/SocialPagePatch.cs b/stardew-access/Patches/GameMenuPatches/SocialPagePatch.cs new file mode 100644 index 0000000..81b2ad0 --- /dev/null +++ b/stardew-access/Patches/GameMenuPatches/SocialPagePatch.cs @@ -0,0 +1,166 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class SocialPagePatch + { + internal static string socialPageQuery = ""; + + internal static void DrawPatch(SocialPage __instance, List ___sprites, int ___slotPosition, List ___kidsNames) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + for (int i = ___slotPosition; i < ___slotPosition + 5; i++) + { + if (i >= ___sprites.Count) + continue; + + if (__instance.names[i] is string && narrateNPCDetails(__instance, i, ___kidsNames, x, y)) + { + return; + } + else if (__instance.names[i] is long && narrateFarmerDetails(__instance, i, ___sprites, x, y)) + { + return; + } + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static bool narrateNPCDetails(SocialPage __instance, int i, List ___kidsNames, int x, int y) + { + if (!__instance.characterSlots[i].bounds.Contains(x, y)) + return false; + + string name = $"{__instance.names[i] as string}"; + int heartLevel = Game1.player.getFriendshipHeartLevelForNPC(name); + bool datable = SocialPage.isDatable(name); + Friendship friendship = __instance.getFriendship(name); + int giftsThisWeek = friendship.GiftsThisWeek; + bool hasTalked = Game1.player.hasPlayerTalkedToNPC(name); + bool spouse = friendship.IsMarried(); + bool housemate = spouse && SocialPage.isRoommateOfAnyone(name); + ___kidsNames.Add("Robin"); + ___kidsNames.Add("Pierre"); + ___kidsNames.Add("Caroline"); + ___kidsNames.Add("Jodi"); + ___kidsNames.Add("Kent"); + ___kidsNames.Add("George"); + ___kidsNames.Add("Evelyn"); + ___kidsNames.Add("Demetrius"); + + string toSpeak = $"{name}"; + + if (!hasTalked) + { + toSpeak = $"{toSpeak}, not talked yet"; + } + + + if (datable | housemate) + { + string text2 = (LocalizedContentManager.CurrentLanguageCode != LocalizedContentManager.LanguageCode.pt) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11635") : ((__instance.getGender(name) == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11635").Split('/').First() : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11635").Split('/').Last()); + if (housemate) + { + text2 = Game1.content.LoadString("Strings\\StringsFromCSFiles:Housemate"); + } + else if (spouse) + { + text2 = ((__instance.getGender(name) == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11636") : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11637")); + } + else if (__instance.isMarriedToAnyone(name)) + { + text2 = ((__instance.getGender(name) == 0) ? Game1.content.LoadString("Strings\\UI:SocialPage_MarriedToOtherPlayer_MaleNPC") : Game1.content.LoadString("Strings\\UI:SocialPage_MarriedToOtherPlayer_FemaleNPC")); + } + else if (!Game1.player.isMarried() && friendship.IsDating()) + { + text2 = ((__instance.getGender(name) == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11639") : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11640")); + } + else if (__instance.getFriendship(name).IsDivorced()) + { + text2 = ((__instance.getGender(name) == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11642") : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11643")); + } + + toSpeak = $"{toSpeak}, {text2}"; + } + + if (!__instance.getFriendship(name).IsMarried() && ___kidsNames.Contains(name)) + { + toSpeak = $"{toSpeak}, married"; + } + + if (spouse) + { + toSpeak = $"{toSpeak}, spouse"; + } + else if (friendship.IsDating()) + { + toSpeak = $"{toSpeak}, dating"; + } + + toSpeak = $"{toSpeak}, {heartLevel} hearts, {giftsThisWeek} gifts given this week."; + + if (socialPageQuery != toSpeak) + { + socialPageQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + return true; + } + + private static bool narrateFarmerDetails(SocialPage __instance, int i, List ___sprites, int x, int y) + { + long farmerID = (long)__instance.names[i]; + Farmer farmer = Game1.getFarmerMaybeOffline(farmerID); + if (farmer == null) + return false; + + int gender = (!farmer.IsMale) ? 1 : 0; + ClickableTextureComponent clickableTextureComponent = ___sprites[i]; + if (!clickableTextureComponent.containsPoint(x, y)) + return false; + + Friendship friendship = Game1.player.team.GetFriendship(Game1.player.UniqueMultiplayerID, farmerID); + bool spouse = friendship.IsMarried(); + string toSpeak = ""; + + string text2 = (LocalizedContentManager.CurrentLanguageCode != LocalizedContentManager.LanguageCode.pt) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11635") : ((gender == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11635").Split('/').First() : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11635").Split('/').Last()); + if (spouse) + { + text2 = ((gender == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11636") : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11637")); + } + else if (farmer.isMarried() && !farmer.hasRoommate()) + { + text2 = ((gender == 0) ? Game1.content.LoadString("Strings\\UI:SocialPage_MarriedToOtherPlayer_MaleNPC") : Game1.content.LoadString("Strings\\UI:SocialPage_MarriedToOtherPlayer_FemaleNPC")); + } + else if (!Game1.player.isMarried() && friendship.IsDating()) + { + text2 = ((gender == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11639") : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11640")); + } + else if (friendship.IsDivorced()) + { + text2 = ((gender == 0) ? Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11642") : Game1.content.LoadString("Strings\\StringsFromCSFiles:SocialPage.cs.11643")); + } + + toSpeak = $"{farmer.displayName}, {text2}"; + + if (socialPageQuery != toSpeak) + { + socialPageQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + return true; + } + + internal static void Cleanup() + { + socialPageQuery = ""; + } + } +} diff --git a/stardew-access/Patches/IClickableMenuPatch.cs b/stardew-access/Patches/IClickableMenuPatch.cs deleted file mode 100644 index 1a3992a..0000000 --- a/stardew-access/Patches/IClickableMenuPatch.cs +++ /dev/null @@ -1,123 +0,0 @@ -using StardewValley.Menus; - -namespace stardew_access.Patches -{ - // These patches are global, i.e. work on every menus - internal class IClickableMenuPatch - { - internal static void ExitThisMenuPatch(IClickableMenu __instance) - { - try - { - Cleanup(__instance); - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void Cleanup(IClickableMenu menu) - { - if (menu is LetterViewerMenu) - { - DialoguePatches.currentLetterText = " "; - } - else if (menu is LevelUpMenu) - { - MenuPatches.currentLevelUpTitle = " "; - } - else if (menu is Billboard) - { - QuestPatches.currentDailyQuestText = " "; - } - else if (menu is GameMenu) - { - GameMenuPatches.gameMenuQueryKey = ""; - GameMenuPatches.craftingPageQueryKey = ""; - GameMenuPatches.inventoryPageQueryKey = ""; - GameMenuPatches.exitPageQueryKey = ""; - GameMenuPatches.optionsPageQueryKey = ""; - GameMenuPatches.socialPageQuery = ""; - GameMenuPatches.currentSelectedCraftingRecipe = -1; - GameMenuPatches.isSelectingRecipe = false; - } - else if (menu is JunimoNoteMenu) - { - BundleMenuPatches.currentIngredientListItem = -1; - BundleMenuPatches.currentIngredientInputSlot = -1; - BundleMenuPatches.currentInventorySlot = -1; - BundleMenuPatches.junimoNoteMenuQuery = ""; - } - else if (menu is ShopMenu) - { - GameMenuPatches.shopMenuQueryKey = ""; - } - else if (menu is ItemGrabMenu) - { - GameMenuPatches.itemGrabMenuQueryKey = ""; - } - else if (menu is GeodeMenu) - { - GameMenuPatches.geodeMenuQueryKey = ""; - } - else if (menu is CarpenterMenu) - { - BuildingNAnimalMenuPatches.carpenterMenuQuery = ""; - BuildingNAnimalMenuPatches.isUpgrading = false; - BuildingNAnimalMenuPatches.isDemolishing = false; - BuildingNAnimalMenuPatches.isPainting = false; - BuildingNAnimalMenuPatches.isMoving = false; - BuildingNAnimalMenuPatches.isConstructing = false; - BuildingNAnimalMenuPatches.carpenterMenu = null; - } - else if (menu is PurchaseAnimalsMenu) - { - BuildingNAnimalMenuPatches.purchaseAnimalMenuQuery = ""; - BuildingNAnimalMenuPatches.firstTimeInNamingMenu = true; - BuildingNAnimalMenuPatches.purchaseAnimalsMenu = null; - } - else if (menu is DialogueBox) - { - DialoguePatches.isDialogueAppearingFirstTime = true; - DialoguePatches.currentDialogue = " "; - } - else if (menu is JojaCDMenu) - { - BundleMenuPatches.jojaCDMenuQuery = ""; - } - else if (menu is QuestLog) - { - QuestPatches.questLogQuery = " "; - } - else if (menu is TailoringMenu) - { - MenuPatches.tailoringMenuQuery = " "; - } - else if (menu is ForgeMenu) - { - MenuPatches.forgeMenuQuery = " "; - } - else if (menu is ItemListMenu) - { - MenuPatches.itemListMenuQuery = " "; - } - else if (menu is FieldOfficeMenu) - { - DonationMenuPatches.fieldOfficeMenuQuery = " "; - } - else if (menu is MuseumMenu) - { - DonationMenuPatches.museumQueryKey = " "; - } - else if (menu is PondQueryMenu) - { - MenuPatches.pondQueryMenuQuery = " "; - } - - InventoryUtils.hoveredItemQueryKey = ""; - InventoryUtils.prevSlotIndex = -999; - TextBoxPatch.activeTextBoxes = ""; - } - } -} diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs deleted file mode 100644 index 2576c40..0000000 --- a/stardew-access/Patches/MenuPatches.cs +++ /dev/null @@ -1,625 +0,0 @@ -using Microsoft.Xna.Framework; -using stardew_access.Features; -using StardewModdingAPI; -using StardewValley; -using StardewValley.Buildings; -using StardewValley.Menus; - -namespace stardew_access.Patches -{ - internal class MenuPatches - { - internal static string currentLevelUpTitle = " "; - internal static bool firstTimeInNamingMenu = true; - internal static bool isNarratingPondInfo = false; - internal static string tailoringMenuQuery = " "; - internal static string pondQueryMenuQuery = " "; - internal static string forgeMenuQuery = " "; - internal static string itemListMenuQuery = " "; - internal static int prevSlotIndex = -999; - public static Vector2? prevTile = null; - - internal static void ItemListMenuPatch(ItemListMenu __instance, string ___title, int ___currentTab, int ___totalValueOfItems, List ___itemsToList) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - string toSpeak = " ", currentList = " "; - - for (int i = ___currentTab * __instance.itemsPerCategoryPage; i < ___currentTab * __instance.itemsPerCategoryPage + __instance.itemsPerCategoryPage; i++) - { - if (i == 0) - currentList = ___title; - - if (___itemsToList.Count > i) - { - if (___itemsToList[i] == null) - { - currentList = $"{currentList}, \n" + Game1.content.LoadString("Strings\\UI:ItemList_ItemsLostValue", ___totalValueOfItems); - continue; - } - - currentList = $"{currentList}, \n {___itemsToList[i].Stack} {___itemsToList[i].DisplayName}"; - } - } - - if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - toSpeak = $"Page {___currentTab + 1} of {((int)___itemsToList.Count / __instance.itemsPerCategoryPage) + 1} \n {currentList} \n ok button"; - else if (__instance.forwardButton != null && __instance.forwardButton.containsPoint(x, y)) - toSpeak = "Next page button"; - else if (__instance.backButton != null && __instance.backButton.containsPoint(x, y)) - toSpeak = "Previous page button"; - - if (itemListMenuQuery != toSpeak) - { - itemListMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - } - catch (System.Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void ForgeMenuPatch(ForgeMenu __instance) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - string toSpeak = " "; - - if (__instance.leftIngredientSpot != null && __instance.leftIngredientSpot.containsPoint(x, y)) - { - if (__instance.leftIngredientSpot.item == null) - { - toSpeak = "Input weapon or tool here"; - } - else - { - Item item = __instance.leftIngredientSpot.item; - toSpeak = $"Weapon slot: {item.Stack} {item.DisplayName}"; - } - } - else if (__instance.rightIngredientSpot != null && __instance.rightIngredientSpot.containsPoint(x, y)) - { - if (__instance.rightIngredientSpot.item == null) - { - toSpeak = "Input gemstone here"; - } - else - { - Item item = __instance.rightIngredientSpot.item; - toSpeak = $"Gemstone slot: {item.Stack} {item.DisplayName}"; - } - } - else if (__instance.startTailoringButton != null && __instance.startTailoringButton.containsPoint(x, y)) - { - toSpeak = "Star forging button"; - } - else if (__instance.unforgeButton != null && __instance.unforgeButton.containsPoint(x, y)) - { - toSpeak = "Unforge button"; - } - else if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y)) - { - toSpeak = "Trashcan"; - } - else if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - { - toSpeak = "ok button"; - } - else if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) - { - toSpeak = "drop item"; - } - else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[0].containsPoint(x, y)) - { - toSpeak = "Left ring Slot"; - - if (Game1.player.leftRing.Value != null) - toSpeak = $"{toSpeak}: {Game1.player.leftRing.Value.DisplayName}"; - } - else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[1].containsPoint(x, y)) - { - toSpeak = "Right ring Slot"; - - if (Game1.player.rightRing.Value != null) - toSpeak = $"{toSpeak}: {Game1.player.rightRing.Value.DisplayName}"; - } - - if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) - return; - - if (forgeMenuQuery != toSpeak) - { - forgeMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - - if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) - Game1.playSound("drop_item"); - } - } - catch (System.Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void PondQueryMenuPatch(PondQueryMenu __instance, StardewValley.Object ____fishItem, FishPond ____pond, string ____statusText, bool ___confirmingEmpty) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); - string toSpeak = " ", extra = ""; - - if (___confirmingEmpty) - { - if (__instance.yesButton != null && __instance.yesButton.containsPoint(x, y)) - toSpeak = "Confirm button"; - else if (__instance.noButton != null && __instance.noButton.containsPoint(x, y)) - toSpeak = "Cancel button"; - } - else - { - if (isPrimaryInfoKeyPressed && !isNarratingPondInfo) - { - string pond_name_text = Game1.content.LoadString("Strings\\UI:PondQuery_Name", ____fishItem.DisplayName); - string population_text = Game1.content.LoadString("Strings\\UI:PondQuery_Population", string.Concat(____pond.FishCount), ____pond.maxOccupants.Value); - bool has_unresolved_needs = ____pond.neededItem.Value != null && ____pond.HasUnresolvedNeeds() && !____pond.hasCompletedRequest.Value; - string bring_text = ""; - - if (has_unresolved_needs && ____pond.neededItem.Value != null) - bring_text = Game1.content.LoadString("Strings\\UI:PondQuery_StatusRequest_Bring") + $": {____pond.neededItemCount} {____pond.neededItem.Value.DisplayName}"; - - extra = $"{pond_name_text} {population_text} {bring_text} Status: {____statusText}"; - pondQueryMenuQuery = " "; - - isNarratingPondInfo = true; - Task.Delay(200).ContinueWith(_ => { isNarratingPondInfo = false; }); - } - - if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - toSpeak = "Ok button"; - else if (__instance.changeNettingButton != null && __instance.changeNettingButton.containsPoint(x, y)) - toSpeak = "Change netting button"; - else if (__instance.emptyButton != null && __instance.emptyButton.containsPoint(x, y)) - toSpeak = "Empty pond button"; - } - - if (pondQueryMenuQuery != toSpeak) - { - pondQueryMenuQuery = toSpeak; - MainClass.ScreenReader.Say(extra + " \n\t" + toSpeak, true); - } - } - catch (System.Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void TailoringMenuPatch(TailoringMenu __instance) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - string toSpeak = " "; - - if (__instance.leftIngredientSpot != null && __instance.leftIngredientSpot.containsPoint(x, y)) - { - if (__instance.leftIngredientSpot.item == null) - { - toSpeak = "Input cloth here"; - } - else - { - Item item = __instance.leftIngredientSpot.item; - toSpeak = $"Cloth slot: {item.Stack} {item.DisplayName}"; - } - } - else if (__instance.rightIngredientSpot != null && __instance.rightIngredientSpot.containsPoint(x, y)) - { - if (__instance.rightIngredientSpot.item == null) - { - toSpeak = "Input ingredient here"; - } - else - { - Item item = __instance.rightIngredientSpot.item; - toSpeak = $"Ingredient slot: {item.Stack} {item.DisplayName}"; - } - } - else if (__instance.startTailoringButton != null && __instance.startTailoringButton.containsPoint(x, y)) - { - toSpeak = "Star tailoring button"; - } - else if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y)) - { - toSpeak = "Trashcan"; - } - else if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - { - toSpeak = "ok button"; - } - else if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) - { - toSpeak = "drop item"; - } - else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[0].containsPoint(x, y)) - { - toSpeak = "Hat Slot"; - - if (Game1.player.hat.Value != null) - toSpeak = $"{toSpeak}: {Game1.player.hat.Value.DisplayName}"; - } - else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[1].containsPoint(x, y)) - { - toSpeak = "Shirt Slot"; - - if (Game1.player.shirtItem.Value != null) - toSpeak = $"{toSpeak}: {Game1.player.shirtItem.Value.DisplayName}"; - } - else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[2].containsPoint(x, y)) - { - toSpeak = "Pants Slot"; - - if (Game1.player.pantsItem.Value != null) - toSpeak = $"{toSpeak}: {Game1.player.pantsItem.Value.DisplayName}"; - } - - if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) - return; - - - if (tailoringMenuQuery != toSpeak) - { - tailoringMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - - if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) - Game1.playSound("drop_item"); - } - } - catch (System.Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void ChooseFromListMenuPatch(ChooseFromListMenu __instance, List ___options, int ___index, bool ___isJukebox) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - string toSpeak = ""; - - if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - toSpeak = "Select " + (___isJukebox ? Utility.getSongTitleFromCueName(___options[___index]) : ___options[___index]) + " button"; - else if (__instance.cancelButton != null && __instance.cancelButton.containsPoint(x, y)) - toSpeak = "Cancel button"; - else if (__instance.backButton != null && __instance.backButton.containsPoint(x, y)) - toSpeak = "Previous option: " + (___isJukebox ? Utility.getSongTitleFromCueName(___options[Math.Max(0, ___index - 1)]) : ___options[Math.Max(0, ___index - 1)]) + " button"; - else if (__instance.forwardButton != null && __instance.forwardButton.containsPoint(x, y)) - toSpeak = "Next option: " + (___isJukebox ? Utility.getSongTitleFromCueName(___options[Math.Min(___options.Count, ___index + 1)]) : ___options[Math.Min(___options.Count, ___index + 1)]) + " button"; - - MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true); - } - catch (System.Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static bool PlaySoundPatch(string cueName) - { - try - { - if (!Context.IsPlayerFree) - return true; - - if (!Game1.player.isMoving()) - return true; - - if (cueName == "grassyStep" || cueName == "sandyStep" || cueName == "snowyStep" || cueName == "stoneStep" || cueName == "thudStep" || cueName == "woodyStep") - { - Vector2 nextTile = CurrentPlayer.FacingTile; - if (TileInfo.isCollidingAtTile((int)nextTile.X, (int)nextTile.Y)) - { - if (prevTile != nextTile) - { - prevTile = nextTile; - //Game1.playSound("colliding"); - } - return false; - } - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - - return true; - } - - internal static void LanguageSelectionMenuPatch(LanguageSelectionMenu __instance) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - - if (__instance.nextPageButton != null && __instance.nextPageButton.containsPoint(x, y)) - { - MainClass.ScreenReader.SayWithMenuChecker($"Next Page Button", true); - return; - } - - if (__instance.previousPageButton != null && __instance.previousPageButton.containsPoint(x, y)) - { - MainClass.ScreenReader.SayWithMenuChecker($"Previous Page Button", true); - return; - } - - for (int i = 0; i < __instance.languages.Count; i++) - { - if (__instance.languages[i].containsPoint(x, y)) - { - MainClass.ScreenReader.SayWithMenuChecker($"{__instance.languageList[i]} Button", true); - break; - } - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void MineElevatorMenuPatch(List ___elevators) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - for (int i = 0; i < ___elevators.Count; i++) - { - if (___elevators[i].containsPoint(x, y)) - { - MainClass.ScreenReader.SayWithMenuChecker($"{___elevators[i].name} level", true); - break; - } - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void TitleTextInputMenuPatch(TitleTextInputMenu __instance) - { - try - { - string toSpeak = ""; - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - - if (__instance.pasteButton != null && __instance.pasteButton.containsPoint(x, y)) - toSpeak = $"Paste button"; - - if (toSpeak != "") - MainClass.ScreenReader.SayWithChecker(toSpeak, true); - } - catch (System.Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void NamingMenuPatch(NamingMenu __instance, TextBox ___textBox, string ___title) - { - try - { - if (firstTimeInNamingMenu) - { - firstTimeInNamingMenu = false; - ___textBox.Selected = false; - } - - if (TextBoxPatch.isAnyTextBoxActive) return; - - string toSpeak = ""; - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box - - if (__instance.textBoxCC != null && __instance.textBoxCC.containsPoint(x, y)) - toSpeak = $"{___title} text box"; - else if (__instance.doneNamingButton != null && __instance.doneNamingButton.containsPoint(x, y)) - toSpeak = $"Done naming button"; - else if (__instance.randomButton != null && __instance.randomButton.containsPoint(x, y)) - toSpeak = $"Random button"; - - if (toSpeak != "") - MainClass.ScreenReader.SayWithChecker(toSpeak, true); - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void ConfirmationDialogPatch(ConfirmationDialog __instance, string ___message) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); - string toSpeak = ___message; - - if (__instance.okButton.containsPoint(x, y)) - { - toSpeak += "\n\tOk Button"; - } - else if (__instance.cancelButton.containsPoint(x, y)) - { - toSpeak += "\n\tCancel Button"; - } - - MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true); - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void LevelUpMenuPatch(LevelUpMenu __instance, List ___professionsToChoose, List ___leftProfessionDescription, List ___rightProfessionDescription, List ___extraInfoForLevel, List ___newCraftingRecipes, string ___title, bool ___isActive, bool ___isProfessionChooser) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); - string leftProfession = " ", rightProfession = " ", extraInfo = " ", newCraftingRecipe = " ", toSpeak = " "; - - if (!__instance.informationUp) - { - return; - } - if (__instance.isProfessionChooser) - { - if (___professionsToChoose.Count() == 0) - { - return; - } - for (int j = 0; j < ___leftProfessionDescription.Count; j++) - { - leftProfession += ___leftProfessionDescription[j] + ", "; - } - for (int i = 0; i < ___rightProfessionDescription.Count; i++) - { - rightProfession += ___rightProfessionDescription[i] + ", "; - } - - if (__instance.leftProfession.containsPoint(x, y)) - { - if ((MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) && __instance.readyToClose()) - { - Game1.player.professions.Add(___professionsToChoose[0]); - __instance.getImmediateProfessionPerk(___professionsToChoose[0]); - ___isActive = false; - __instance.informationUp = false; - ___isProfessionChooser = false; - __instance.RemoveLevelFromLevelList(); - __instance.exitThisMenu(); - return; - } - - toSpeak = $"Selected: {leftProfession} Left click to choose."; - } - - if (__instance.rightProfession.containsPoint(x, y)) - { - if ((MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) && __instance.readyToClose()) - { - Game1.player.professions.Add(___professionsToChoose[1]); - __instance.getImmediateProfessionPerk(___professionsToChoose[1]); - ___isActive = false; - __instance.informationUp = false; - ___isProfessionChooser = false; - __instance.RemoveLevelFromLevelList(); - __instance.exitThisMenu(); - return; - } - - toSpeak = $"Selected: {rightProfession} Left click to choose."; - } - } - else - { - foreach (string s2 in ___extraInfoForLevel) - { - extraInfo += s2 + ", "; - } - foreach (CraftingRecipe s in ___newCraftingRecipes) - { - string cookingOrCrafting = Game1.content.LoadString("Strings\\UI:LearnedRecipe_" + (s.isCookingRecipe ? "cooking" : "crafting")); - string message = Game1.content.LoadString("Strings\\UI:LevelUp_NewRecipe", cookingOrCrafting, s.DisplayName); - - newCraftingRecipe += $"{message}, "; - } - } - - if (__instance.okButton.containsPoint(x, y)) - { - if (MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) - __instance.okButtonClicked(); - - toSpeak = $"{___title} {extraInfo} {newCraftingRecipe}. Left click to close."; - } - - if (toSpeak != " ") - MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true); - else if (__instance.isProfessionChooser && currentLevelUpTitle != $"{___title}. Select a new profession.") - { - MainClass.ScreenReader.SayWithMenuChecker($"{___title}. Select a new profession.", true); - currentLevelUpTitle = $"{___title}. Select a new profession."; - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void ShippingMenuPatch(ShippingMenu __instance, List ___categoryTotals) - { - try - { - - if (__instance.currentPage == -1) - { - int total = ___categoryTotals[5]; - string toSpeak; - if (__instance.okButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) - { - // Perform Left Click - if (MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) - { - Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); - } - toSpeak = $"{total}g in total. Press left mouse button to save."; - MainClass.ScreenReader.SayWithChecker(toSpeak, true); - } - for (int i = 0; i < __instance.categories.Count; i++) - { - if (__instance.categories[i].containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) - { - toSpeak = $"Money recieved from {__instance.getCategoryName(i)}: {___categoryTotals[i]}g."; - MainClass.ScreenReader.SayWithChecker(toSpeak, true); - } - } - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void Game1ExitActiveMenuPatch() - { - try - { - IClickableMenuPatch.Cleanup(Game1.activeClickableMenu); - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void ExitEventPatch() - { - if (MainClass.ScreenReader != null) - MainClass.ScreenReader.CloseScreenReader(); - } - } -} diff --git a/stardew-access/Patches/MenuWithInventoryPatches/ForgeMenuPatch.cs b/stardew-access/Patches/MenuWithInventoryPatches/ForgeMenuPatch.cs new file mode 100644 index 0000000..d5b6671 --- /dev/null +++ b/stardew-access/Patches/MenuWithInventoryPatches/ForgeMenuPatch.cs @@ -0,0 +1,116 @@ +using stardew_access.Features; +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class ForgeMenuPatch + { + private static string forgeMenuQuery = ""; + + internal static void DrawPatch(ForgeMenu __instance) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + if (narrateHoveredButton(__instance, x, y)) return; + + if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) + { + Cleanup(); + } + + } + catch (System.Exception e) + { + MainClass.ErrorLog($"An error occured in forge menu patch:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static bool narrateHoveredButton(ForgeMenu __instance, int x, int y) + { + string toSpeak = ""; + bool isDropItemButton = false; + + if (__instance.leftIngredientSpot != null && __instance.leftIngredientSpot.containsPoint(x, y)) + { + if (__instance.leftIngredientSpot.item == null) + { + toSpeak = "Input weapon or tool here"; + } + else + { + Item item = __instance.leftIngredientSpot.item; + toSpeak = $"Weapon slot: {item.Stack} {item.DisplayName}"; + } + } + else if (__instance.rightIngredientSpot != null && __instance.rightIngredientSpot.containsPoint(x, y)) + { + if (__instance.rightIngredientSpot.item == null) + { + toSpeak = "Input gemstone here"; + } + else + { + Item item = __instance.rightIngredientSpot.item; + toSpeak = $"Gemstone slot: {item.Stack} {item.DisplayName}"; + } + } + else if (__instance.startTailoringButton != null && __instance.startTailoringButton.containsPoint(x, y)) + { + toSpeak = "Star forging button"; + } + else if (__instance.unforgeButton != null && __instance.unforgeButton.containsPoint(x, y)) + { + toSpeak = "Unforge button"; + } + else if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y)) + { + toSpeak = "Trashcan"; + } + else if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + { + toSpeak = "ok button"; + } + else if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) + { + toSpeak = "drop item"; + isDropItemButton = true; + } + else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[0].containsPoint(x, y)) + { + toSpeak = "Left ring Slot"; + + if (Game1.player.leftRing.Value != null) + toSpeak = $"{toSpeak}: {Game1.player.leftRing.Value.DisplayName}"; + } + else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[1].containsPoint(x, y)) + { + toSpeak = "Right ring Slot"; + + if (Game1.player.rightRing.Value != null) + toSpeak = $"{toSpeak}: {Game1.player.rightRing.Value.DisplayName}"; + } + else + { + return false; + } + + if (forgeMenuQuery != toSpeak) + { + forgeMenuQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + + if (isDropItemButton) Game1.playSound("drop_item"); + } + + return true; + } + + internal static void Cleanup() + { + forgeMenuQuery = ""; + } + } +} diff --git a/stardew-access/Patches/MenuWithInventoryPatches/GeodeMenuPatch.cs b/stardew-access/Patches/MenuWithInventoryPatches/GeodeMenuPatch.cs new file mode 100644 index 0000000..e9ca166 --- /dev/null +++ b/stardew-access/Patches/MenuWithInventoryPatches/GeodeMenuPatch.cs @@ -0,0 +1,88 @@ +using stardew_access.Features; +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class GeodeMenuPatch + { + private static string geodeMenuQueryKey = ""; + + internal static void DrawPatch(GeodeMenu __instance) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + if (narrateRecievedTreasure(__instance)) return; + if (narrateHoveredButton(__instance, x, y)) return; + + if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) + geodeMenuQueryKey = ""; + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static bool narrateRecievedTreasure(GeodeMenu __instance) + { + // Narrates the treasure recieved on breaking the geode + if (__instance.geodeTreasure == null) return false; + + string name = __instance.geodeTreasure.DisplayName; + int stack = __instance.geodeTreasure.Stack; + + string toSpeak = $"Recieved {stack} {name}"; + + if (geodeMenuQueryKey != toSpeak) + { + geodeMenuQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + return true; + } + + private static bool narrateHoveredButton(GeodeMenu __instance, int x, int y) + { + string toSpeak = ""; + bool isDropItemButton = false; + + if (__instance.geodeSpot != null && __instance.geodeSpot.containsPoint(x, y)) + { + toSpeak = "Place geode here"; + } + else if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) + { + toSpeak = "Drop item here"; + isDropItemButton = true; + } + else if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y)) + { + toSpeak = "Trash can"; + } + else if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + { + toSpeak = "Ok button"; + } + else + { + return false; + } + + if (geodeMenuQueryKey == toSpeak) return true; + + geodeMenuQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + if (isDropItemButton) Game1.playSound("drop_item"); + + return true; + } + + internal static void Cleanup() + { + geodeMenuQueryKey = ""; + } + } +} diff --git a/stardew-access/Patches/MenuWithInventoryPatches/ItemGrabMenuPatch.cs b/stardew-access/Patches/MenuWithInventoryPatches/ItemGrabMenuPatch.cs new file mode 100644 index 0000000..b9b7674 --- /dev/null +++ b/stardew-access/Patches/MenuWithInventoryPatches/ItemGrabMenuPatch.cs @@ -0,0 +1,230 @@ +using stardew_access.Features; +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class ItemGrabMenuPatch + { + internal static string itemGrabMenuQueryKey = ""; + internal static string hoveredItemQueryKey = ""; + + internal static void DrawPatch(ItemGrabMenu __instance) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + if (MainClass.Config.SnapToFirstSecondaryInventorySlotKey.JustPressed() && __instance.ItemsToGrabMenu.inventory.Count > 0 && !__instance.shippingBin) + { + __instance.setCurrentlySnappedComponentTo(__instance.ItemsToGrabMenu.inventory[0].myID); + __instance.ItemsToGrabMenu.inventory[0].snapMouseCursorToCenter(); + } + else if (MainClass.Config.SnapToFirstInventorySlotKey.JustPressed() && __instance.inventory.inventory.Count > 0) + { + __instance.setCurrentlySnappedComponentTo(__instance.inventory.inventory[0].myID); + __instance.inventory.inventory[0].snapMouseCursorToCenter(); + } + + if (narrateHoveredButton(__instance, x, y)) + { + InventoryUtils.Cleanup(); + return; + } + if (narrateLastShippedItem(__instance, x, y)) + { + InventoryUtils.Cleanup(); + return; + } + + if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y, true)) + { + itemGrabMenuQueryKey = ""; + return; + } + + if (InventoryUtils.narrateHoveredSlot(__instance.ItemsToGrabMenu, __instance.ItemsToGrabMenu.inventory, __instance.ItemsToGrabMenu.actualInventory, x, y, true)) + { + itemGrabMenuQueryKey = ""; + return; + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static bool narrateHoveredButton(ItemGrabMenu __instance, int x, int y) + { + string toSpeak = ""; + bool isDropItemButton = false; + + if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + { + toSpeak = "Ok Button"; + } + else if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y)) + { + toSpeak = "Trash Can"; + } + else if (__instance.organizeButton != null && __instance.organizeButton.containsPoint(x, y)) + { + toSpeak = "Organize Button"; + } + else if (__instance.fillStacksButton != null && __instance.fillStacksButton.containsPoint(x, y)) + { + toSpeak = "Add to existing stacks button"; + } + else if (__instance.specialButton != null && __instance.specialButton.containsPoint(x, y)) + { + toSpeak = "Special Button"; + } + else if (__instance.colorPickerToggleButton != null && __instance.colorPickerToggleButton.containsPoint(x, y)) + { + toSpeak = "Color Picker: " + (__instance.chestColorPicker.visible ? "Enabled" : "Disabled"); + } + else if (__instance.junimoNoteIcon != null && __instance.junimoNoteIcon.containsPoint(x, y)) + { + toSpeak = "Community Center Button"; + } + else if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) + { + toSpeak = "Drop Item"; + isDropItemButton = true; + } + else + { + return false; + } + + // FIXME + /*if (__instance.discreteColorPickerCC.Count > 0) { + for (int i = 0; i < __instance.discreteColorPickerCC.Count; i++) + { + if (__instance.discreteColorPickerCC[i].containsPoint(x, y)) + { + MainClass.monitor.Log(i.ToString(), LogLevel.Debug); + string toSpeak = getChestColorName(i); + if (itemGrabMenuQueryKey != toSpeak) + { + itemGrabMenuQueryKey = toSpeak; + hoveredItemQueryKey = ""; + ScreenReader.say(toSpeak, true); + Game1.playSound("sa_drop_item"); + } + return; + } + } + }*/ + + if (itemGrabMenuQueryKey == toSpeak) return true; + + itemGrabMenuQueryKey = toSpeak; + hoveredItemQueryKey = ""; + MainClass.ScreenReader.Say(toSpeak, true); + if (isDropItemButton) Game1.playSound("drop_item"); + + return true; + } + + private static bool narrateLastShippedItem(ItemGrabMenu __instance, int x, int y) + { + if (!__instance.shippingBin || Game1.getFarm().lastItemShipped == null || !__instance.lastShippedHolder.containsPoint(x, y)) + return false; + + Item lastShippedItem = Game1.getFarm().lastItemShipped; + string name = lastShippedItem.DisplayName; + int count = lastShippedItem.Stack; + + string toSpeak = $"Last Shipped: {count} {name}"; + + if (itemGrabMenuQueryKey != toSpeak) + { + itemGrabMenuQueryKey = toSpeak; + hoveredItemQueryKey = ""; + MainClass.ScreenReader.Say(toSpeak, true); + } + return true; + } + + // TODO Add color names + private static string getChestColorName(int i) + { + string toReturn = ""; + switch (i) + { + case 0: + toReturn = "Default chest color"; + break; + case 1: + toReturn = "Default chest color"; + break; + case 2: + toReturn = "Default chest color"; + break; + case 3: + toReturn = "Default chest color"; + break; + case 4: + toReturn = "Default chest color"; + break; + case 5: + toReturn = "Default chest color"; + break; + case 6: + toReturn = "Default chest color"; + break; + case 7: + toReturn = "Default chest color"; + break; + case 8: + toReturn = "Default chest color"; + break; + case 9: + toReturn = "Default chest color"; + break; + case 10: + toReturn = "Default chest color"; + break; + case 11: + toReturn = "Default chest color"; + break; + case 12: + toReturn = "Default chest color"; + break; + case 13: + toReturn = "Default chest color"; + break; + case 14: + toReturn = "Default chest color"; + break; + case 15: + toReturn = "Default chest color"; + break; + case 16: + toReturn = "Default chest color"; + break; + case 17: + toReturn = "Default chest color"; + break; + case 18: + toReturn = "Default chest color"; + break; + case 19: + toReturn = "Default chest color"; + break; + case 20: + toReturn = "Default chest color"; + break; + } + return toReturn; + } + + internal static void Cleanup() + { + hoveredItemQueryKey = ""; + itemGrabMenuQueryKey = ""; + } + } +} diff --git a/stardew-access/Patches/MenuWithInventoryPatches/ShopMenuPatch.cs b/stardew-access/Patches/MenuWithInventoryPatches/ShopMenuPatch.cs new file mode 100644 index 0000000..8e000d8 --- /dev/null +++ b/stardew-access/Patches/MenuWithInventoryPatches/ShopMenuPatch.cs @@ -0,0 +1,121 @@ +using stardew_access.Features; +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class ShopMenuPatch + { + internal static string shopMenuQueryKey = ""; + internal static string hoveredItemQueryKey = ""; + + internal static void DrawPatch(ShopMenu __instance) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + if (MainClass.Config.SnapToFirstSecondaryInventorySlotKey.JustPressed() && __instance.forSaleButtons.Count > 0) + { + __instance.forSaleButtons[0].snapMouseCursorToCenter(); + __instance.setCurrentlySnappedComponentTo(__instance.forSaleButtons[0].myID); + } + else if (MainClass.Config.SnapToFirstInventorySlotKey.JustPressed() && __instance.inventory.inventory.Count > 0) + { + __instance.inventory.inventory[0].snapMouseCursorToCenter(); + __instance.setCurrentlySnappedComponentTo(__instance.inventory.inventory[0].myID); + } + + if (narrateHoveredButton(__instance, x, y)) return; + + if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y, hoverPrice: __instance.hoverPrice)) + { + shopMenuQueryKey = ""; + return; + } + + narrateHoveredSellingItem(__instance); + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static bool narrateHoveredButton(ShopMenu __instance, int x, int y) + { + string toSpeak = ""; + bool isDropItemButton = false; + + if (__instance.inventory.dropItemInvisibleButton != null && __instance.inventory.dropItemInvisibleButton.containsPoint(x, y)) + { + toSpeak = "Drop Item"; + isDropItemButton = true; + } + else if (__instance.upArrow != null && __instance.upArrow.containsPoint(x, y)) + { + toSpeak = "Up Arrow Button"; + } + else if (__instance.downArrow != null && __instance.downArrow.containsPoint(x, y)) + { + toSpeak = "Down Arrow Button"; + } + else + { + return false; + } + + if (shopMenuQueryKey == toSpeak) return true; + + shopMenuQueryKey = toSpeak; + hoveredItemQueryKey = ""; + MainClass.ScreenReader.Say(toSpeak, true); + if (isDropItemButton) Game1.playSound("drop_item"); + + return true; + } + + private static void narrateHoveredSellingItem(ShopMenu __instance) + { + if (__instance.hoveredItem == null) return; + + string name = __instance.hoveredItem.DisplayName; + string price = $"Buy Price: {__instance.hoverPrice} g"; + string description = __instance.hoveredItem.getDescription(); + string requirements = ""; + + #region get required items for item + int itemIndex = -1, itemAmount = 5; + + if (__instance.itemPriceAndStock[__instance.hoveredItem].Length > 2) + itemIndex = __instance.itemPriceAndStock[__instance.hoveredItem][2]; + + if (__instance.itemPriceAndStock[__instance.hoveredItem].Length > 3) + itemAmount = __instance.itemPriceAndStock[__instance.hoveredItem][3]; + + if (itemIndex != -1) + { + string itemName = Game1.objectInformation[itemIndex].Split('/')[0]; + + if (itemAmount != -1) + requirements = $"Required: {itemAmount} {itemName}"; + else + requirements = $"Required: {itemName}"; + } + #endregion + + string toSpeak = $"{name}, {requirements}, {price}, \n\t{description}"; + if (shopMenuQueryKey == toSpeak) return; + + shopMenuQueryKey = toSpeak; + hoveredItemQueryKey = ""; + MainClass.ScreenReader.Say(toSpeak, true); + } + + internal static void Cleanup() + { + shopMenuQueryKey = ""; + hoveredItemQueryKey = ""; + } + } +} diff --git a/stardew-access/Patches/MenuWithInventoryPatches/TailoringMenuPatch.cs b/stardew-access/Patches/MenuWithInventoryPatches/TailoringMenuPatch.cs new file mode 100644 index 0000000..3718730 --- /dev/null +++ b/stardew-access/Patches/MenuWithInventoryPatches/TailoringMenuPatch.cs @@ -0,0 +1,115 @@ +using stardew_access.Features; +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class TailoringMenuPatch + { + internal static string tailoringMenuQuery = ""; + + internal static void DrawPatch(TailoringMenu __instance) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + if (InventoryUtils.narrateHoveredSlot(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) + return; + + + } + catch (System.Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static bool narrateHoveredButton(TailoringMenu __instance, int x, int y) + { + string toSpeak = ""; + bool isDropItemButton = false; + + if (__instance.leftIngredientSpot != null && __instance.leftIngredientSpot.containsPoint(x, y)) + { + if (__instance.leftIngredientSpot.item == null) + { + toSpeak = "Input cloth here"; + } + else + { + Item item = __instance.leftIngredientSpot.item; + toSpeak = $"Cloth slot: {item.Stack} {item.DisplayName}"; + } + } + else if (__instance.rightIngredientSpot != null && __instance.rightIngredientSpot.containsPoint(x, y)) + { + if (__instance.rightIngredientSpot.item == null) + { + toSpeak = "Input ingredient here"; + } + else + { + Item item = __instance.rightIngredientSpot.item; + toSpeak = $"Ingredient slot: {item.Stack} {item.DisplayName}"; + } + } + else if (__instance.startTailoringButton != null && __instance.startTailoringButton.containsPoint(x, y)) + { + toSpeak = "Star tailoring button"; + } + else if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y)) + { + toSpeak = "Trashcan"; + } + else if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + { + toSpeak = "ok button"; + } + else if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) + { + toSpeak = "drop item"; + isDropItemButton = true; + } + else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[0].containsPoint(x, y)) + { + toSpeak = "Hat Slot"; + + if (Game1.player.hat.Value != null) + toSpeak = $"{toSpeak}: {Game1.player.hat.Value.DisplayName}"; + } + else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[1].containsPoint(x, y)) + { + toSpeak = "Shirt Slot"; + + if (Game1.player.shirtItem.Value != null) + toSpeak = $"{toSpeak}: {Game1.player.shirtItem.Value.DisplayName}"; + } + else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[2].containsPoint(x, y)) + { + toSpeak = "Pants Slot"; + + if (Game1.player.pantsItem.Value != null) + toSpeak = $"{toSpeak}: {Game1.player.pantsItem.Value.DisplayName}"; + } + else { + return false; + } + + if (tailoringMenuQuery != toSpeak) + { + tailoringMenuQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + + if (isDropItemButton) Game1.playSound("drop_item"); + } + + return true; + } + + internal static void Cleanup() + { + tailoringMenuQuery = ""; + } + } +} diff --git a/stardew-access/Patches/MiniGamesPatches.cs b/stardew-access/Patches/MiniGamesPatches/GrandpaStoryPatch.cs similarity index 70% rename from stardew-access/Patches/MiniGamesPatches.cs rename to stardew-access/Patches/MiniGamesPatches/GrandpaStoryPatch.cs index 8addd64..90e93d2 100644 --- a/stardew-access/Patches/MiniGamesPatches.cs +++ b/stardew-access/Patches/MiniGamesPatches/GrandpaStoryPatch.cs @@ -4,43 +4,11 @@ using StardewValley.Minigames; namespace stardew_access.Patches { - public class MiniGamesPatches + public class GrandpaStoryPatch { public static string grandpaStoryQuery = " "; - public static string introQuery = " "; - internal static void IntroPatch(Intro __instance, int ___currentState) - { - try - { - if (MainClass.ModHelper == null) - return; - - string toSpeak = " "; - - if (___currentState == 3) - { - toSpeak = MainClass.ModHelper.Translation.Get("intro.scene3"); - } - else if (___currentState == 4) - { - toSpeak = MainClass.ModHelper.Translation.Get("intro.scene4"); - } - - if (toSpeak != " " && introQuery != toSpeak) - { - introQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, false); - return; - } - } - catch (System.Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void GrandpaStoryPatch(GrandpaStory __instance, StardewValley.Menus.LetterViewerMenu ___letterView, bool ___drawGrandpa, bool ___letterReceived, bool ___mouseActive, Queue ___grandpaSpeech, int ___grandpaSpeechTimer, int ___totalMilliseconds, int ___scene, int ___parallaxPan) + internal static void DrawPatch(GrandpaStory __instance, StardewValley.Menus.LetterViewerMenu ___letterView, bool ___drawGrandpa, bool ___letterReceived, bool ___mouseActive, Queue ___grandpaSpeech, int ___grandpaSpeechTimer, int ___totalMilliseconds, int ___scene, int ___parallaxPan) { try { @@ -49,7 +17,7 @@ namespace stardew_access.Patches if (___letterView != null) { - DialoguePatches.NarrateLetterContent(___letterView); + LetterViwerMenuPatch.narrateLetterContent(___letterView); } if (MainClass.ModHelper == null) @@ -117,4 +85,4 @@ namespace stardew_access.Patches return new Rectangle((int)Utility.getTopLeftPositionForCenteringOnScreen(Game1.viewport, 1294, 730).X + (286 - ___parallaxPan) * 4, (int)Utility.getTopLeftPositionForCenteringOnScreen(Game1.viewport, 1294, 730).Y + 218 + Math.Max(0, Math.Min(60, (___grandpaSpeechTimer - 5000) / 8)), 524, 344); } } -} \ No newline at end of file +} diff --git a/stardew-access/Patches/MiniGamesPatches/IntroPatch.cs b/stardew-access/Patches/MiniGamesPatches/IntroPatch.cs new file mode 100644 index 0000000..08759a7 --- /dev/null +++ b/stardew-access/Patches/MiniGamesPatches/IntroPatch.cs @@ -0,0 +1,40 @@ +using StardewValley.Minigames; + +namespace stardew_access.Patches +{ + public class IntroPatch + { + public static string introQuery = " "; + + internal static void DrawPatch(Intro __instance, int ___currentState) + { + try + { + if (MainClass.ModHelper == null) + return; + + string toSpeak = " "; + + if (___currentState == 3) + { + toSpeak = MainClass.ModHelper.Translation.Get("intro.scene3"); + } + else if (___currentState == 4) + { + toSpeak = MainClass.ModHelper.Translation.Get("intro.scene4"); + } + + if (toSpeak != " " && introQuery != toSpeak) + { + introQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, false); + return; + } + } + catch (System.Exception e) + { + MainClass.ErrorLog($"An error occured in intro minigame patch:\n{e.Message}\n{e.StackTrace}"); + } + } + } +} diff --git a/stardew-access/Patches/ChatMenuPatches.cs b/stardew-access/Patches/MiscPatches/ChatBoxPatch.cs similarity index 65% rename from stardew-access/Patches/ChatMenuPatches.cs rename to stardew-access/Patches/MiscPatches/ChatBoxPatch.cs index 9d3272d..8ded908 100644 --- a/stardew-access/Patches/ChatMenuPatches.cs +++ b/stardew-access/Patches/MiscPatches/ChatBoxPatch.cs @@ -2,12 +2,12 @@ namespace stardew_access.Patches { - internal class ChatMenuPatches + internal class ChatBoxPatch { private static int currentChatMessageIndex = 0; private static bool isChatRunning = false; - internal static void ChatBoxPatch(ChatBox __instance, List ___messages) + internal static void UpdatePatch(ChatBox __instance, List ___messages) { try { @@ -18,23 +18,22 @@ namespace stardew_access.Patches bool isPrevButtonPressed = MainClass.Config.ChatMenuNextKey.JustPressed(); bool isNextButtonPressed = MainClass.Config.ChatMenuPreviousKey.JustPressed(); - if (___messages.Count > 0) + if (___messages.Count <= 0) return; + + #region To narrate previous and next chat messages + if (isNextButtonPressed && !isChatRunning) { - #region To narrate previous and next chat messages - if (isNextButtonPressed && !isChatRunning) - { - isChatRunning = true; - CycleThroughChatMessages(true, ___messages); - Task.Delay(200).ContinueWith(_ => { isChatRunning = false; }); - } - else if (isPrevButtonPressed && !isChatRunning) - { - isChatRunning = true; - CycleThroughChatMessages(false, ___messages); - Task.Delay(200).ContinueWith(_ => { isChatRunning = false; }); - } - #endregion + isChatRunning = true; + CycleThroughChatMessages(true, ___messages); + Task.Delay(200).ContinueWith(_ => { isChatRunning = false; }); } + else if (isPrevButtonPressed && !isChatRunning) + { + isChatRunning = true; + CycleThroughChatMessages(false, ___messages); + Task.Delay(200).ContinueWith(_ => { isChatRunning = false; }); + } + #endregion } else if (___messages.Count > 0) { diff --git a/stardew-access/Patches/MiscPatches/DialogueBoxPatch.cs b/stardew-access/Patches/MiscPatches/DialogueBoxPatch.cs new file mode 100644 index 0000000..bc471d6 --- /dev/null +++ b/stardew-access/Patches/MiscPatches/DialogueBoxPatch.cs @@ -0,0 +1,128 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class DialogueBoxPatch + { + private static string currentDialogue = ""; + private static bool isDialogueAppearingFirstTime = true; + + internal static void DrawPatch(DialogueBox __instance) + { + try + { + if (__instance.transitioning) return; + + if (narrateCharacterDialogue(__instance)) return; + if (narrateQuestionDialogue(__instance)) return; + narrateBasicDialogue(__instance.getCurrentString()); + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate dialog:\n{e.StackTrace}\n{e.Message}"); + } + } + + internal static void RecieveLeftClickPatch() + { + // CLears the currentDialogue string on closing dialog + Cleanup(); + } + + private static bool narrateCharacterDialogue(DialogueBox __instance) + { + if (__instance.characterDialogue == null) return false; + + // For Normal Character dialogues + Dialogue dialogue = __instance.characterDialogue; + string speakerName = dialogue.speaker.displayName; + string dialogueText = ""; + string responseText = ""; + bool hasResponses = dialogue.isCurrentDialogueAQuestion(); + + dialogueText = $"{speakerName} said {__instance.getCurrentString()}"; + + if (hasResponses) + { + responseText = getCurrentResponseText(__instance); + + CheckAndSpeak(isDialogueAppearingFirstTime ? $"{dialogueText} \n\t {responseText}" : responseText, responseText); + if (isDialogueAppearingFirstTime) isDialogueAppearingFirstTime = false; + } + else + { + CheckAndSpeak(dialogueText); + } + + return true; + } + + private static bool narrateQuestionDialogue(DialogueBox __instance) + { + if (!__instance.isQuestion) return false; + + // For Dialogues with responses/answers like the dialogue when we click on tv + string questionText = ""; + string responseText = ""; + bool hasResponses = false; + + if (__instance.responses.Count > 0) hasResponses = true; + if (!hasResponses) return false; + + questionText = __instance.getCurrentString(); + + responseText = getCurrentResponseText(__instance); + + CheckAndSpeak(isDialogueAppearingFirstTime ? $"{questionText} \n\t {responseText}" : responseText, responseText); + if (isDialogueAppearingFirstTime) isDialogueAppearingFirstTime = false; + + return true; + } + + private static void narrateBasicDialogue(string dialogue) + { + // Basic dialogues like `No mails in the mail box` + if (Game1.activeClickableMenu is not DialogueBox) return; + CheckAndSpeak(dialogue); + } + + private static string getCurrentResponseText(DialogueBox __instance) + { + List responses = __instance.responses; + if (__instance.selectedResponse >= 0 && __instance.selectedResponse < responses.Count) + { + return $"{__instance.selectedResponse + 1}: {responses[__instance.selectedResponse].responseText}"; + } + else + { + // When the dialogue is not finished writing then the selectedResponse is <0 and this results + // in the first response not being detcted, so this sets the first response option to be the default + // if the current dialogue is a question or has responses + return $"1: {responses[0].responseText}"; + } + } + + private static void CheckAndSpeak(string toSpeak) + { + if (currentDialogue == toSpeak) return; + currentDialogue = toSpeak; + + MainClass.ScreenReader.Say(toSpeak, true); + } + + private static void CheckAndSpeak(string toSpeak, string checkQuery) + { + if (currentDialogue == checkQuery) return; + currentDialogue = checkQuery; + + MainClass.ScreenReader.Say(toSpeak, true); + } + + internal static void Cleanup() + { + currentDialogue = ""; + isDialogueAppearingFirstTime = true; + } + } +} diff --git a/stardew-access/Patches/MiscPatches/Game1Patch.cs b/stardew-access/Patches/MiscPatches/Game1Patch.cs new file mode 100644 index 0000000..04ba91f --- /dev/null +++ b/stardew-access/Patches/MiscPatches/Game1Patch.cs @@ -0,0 +1,56 @@ +using Microsoft.Xna.Framework; +using stardew_access.Features; +using StardewModdingAPI; +using StardewValley; + +namespace stardew_access.Patches +{ + internal class Game1Patch + { + private static Vector2? prevTile = null; + + internal static void ExitActiveMenuPatch() + { + try + { + IClickableMenuPatch.Cleanup(Game1.activeClickableMenu); + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static bool PlaySoundPatch(string cueName) + { + try + { + if (!Context.IsPlayerFree) + return true; + + if (!Game1.player.isMoving()) + return true; + + if (cueName == "grassyStep" || cueName == "sandyStep" || cueName == "snowyStep" || cueName == "stoneStep" || cueName == "thudStep" || cueName == "woodyStep") + { + Vector2 nextTile = CurrentPlayer.FacingTile; + if (TileInfo.isCollidingAtTile((int)nextTile.X, (int)nextTile.Y)) + { + if (prevTile != nextTile) + { + prevTile = nextTile; + //Game1.playSound("colliding"); + } + return false; + } + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + + return true; + } + } +} diff --git a/stardew-access/Patches/MiscPatches/IClickableMenuPatch.cs b/stardew-access/Patches/MiscPatches/IClickableMenuPatch.cs new file mode 100644 index 0000000..7069fb5 --- /dev/null +++ b/stardew-access/Patches/MiscPatches/IClickableMenuPatch.cs @@ -0,0 +1,326 @@ +using stardew_access.Features; +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + // These patches are global, i.e. work on every menus + internal class IClickableMenuPatch + { + internal static void DrawHoverTextPatch(string? text, int moneyAmountToDisplayAtBottom = -1, string? boldTitleText = null, int extraItemToShowIndex = -1, int extraItemToShowAmount = -1, string[]? buffIconsToDisplay = null, Item? hoveredItem = null, CraftingRecipe? craftingIngredients = null) + { + try + { + #region Skip narrating hover text for certain menus + if (Game1.activeClickableMenu is TitleMenu && !(((TitleMenu)Game1.activeClickableMenu).GetChildMenu() is CharacterCustomization)) + return; + else if (Game1.activeClickableMenu is LetterViewerMenu || Game1.activeClickableMenu is QuestLog) + return; + else if (Game1.activeClickableMenu is Billboard) + return; + else if (Game1.activeClickableMenu is GeodeMenu) + return; + else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is InventoryPage) + return; + else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is CraftingPage) + return; + else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is OptionsPage) + return; + else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is ExitPage) + return; + else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is SocialPage) + return; + else if (Game1.activeClickableMenu is ItemGrabMenu) + return; + else if (Game1.activeClickableMenu is ShopMenu) + return; + else if (Game1.activeClickableMenu is ConfirmationDialog) + return; + else if (Game1.activeClickableMenu is JunimoNoteMenu) + return; + else if (Game1.activeClickableMenu is CarpenterMenu) + return; + else if (Game1.activeClickableMenu is PurchaseAnimalsMenu) + return; + else if (Game1.activeClickableMenu is CraftingPage) + return; + else if (Game1.activeClickableMenu is AnimalQueryMenu) + return; + else if (Game1.activeClickableMenu is ConfirmationDialog) + return; + else if (Game1.activeClickableMenu is ReadyCheckDialog) + return; + else if (Game1.activeClickableMenu is JojaCDMenu) + return; + else if (Game1.activeClickableMenu is TailoringMenu) + return; + else if (Game1.activeClickableMenu is PondQueryMenu) + return; + else if (Game1.activeClickableMenu is ForgeMenu) + return; + else if (Game1.activeClickableMenu is ItemListMenu) + return; + else if (Game1.activeClickableMenu is FieldOfficeMenu) + return; + else if (Game1.activeClickableMenu is MuseumMenu) + return; + #endregion + + string toSpeak = " "; + + #region Add item count before title + if (hoveredItem != null && hoveredItem.HasBeenInInventory) + { + int count = hoveredItem.Stack; + if (count > 1) + toSpeak = $"{toSpeak} {count} "; + } + #endregion + + #region Add title if any + if (boldTitleText != null) + toSpeak = $"{toSpeak} {boldTitleText}\n"; + #endregion + + #region Add quality of item + if (hoveredItem is StardewValley.Object && ((StardewValley.Object)hoveredItem).Quality > 0) + { + int quality = ((StardewValley.Object)hoveredItem).Quality; + if (quality == 1) + { + toSpeak = $"{toSpeak} Silver quality"; + } + else if (quality == 2 || quality == 3) + { + toSpeak = $"{toSpeak} Gold quality"; + } + else if (quality >= 4) + { + toSpeak = $"{toSpeak} Iridium quality"; + } + } + #endregion + + #region Narrate hovered required ingredients + if (extraItemToShowIndex != -1) + { + string itemName = Game1.objectInformation[extraItemToShowIndex].Split('/')[0]; + + if (extraItemToShowAmount != -1) + toSpeak = $"{toSpeak} Required: {extraItemToShowAmount} {itemName}"; + else + toSpeak = $"{toSpeak} Required: {itemName}"; + } + #endregion + + #region Add money + if (moneyAmountToDisplayAtBottom != -1) + toSpeak = $"{toSpeak} \nCost: {moneyAmountToDisplayAtBottom}g\n"; + #endregion + + #region Add the base text + if (text == "???") + toSpeak = "unknown"; + else + toSpeak = $"{toSpeak} {text}"; + #endregion + + #region Add crafting ingredients + if (craftingIngredients != null) + { + toSpeak = $"{toSpeak} \n{craftingIngredients.description}"; + toSpeak = $"{toSpeak} \nIngredients\n"; + + craftingIngredients.recipeList.ToList().ForEach(recipe => + { + int count = recipe.Value; + int item = recipe.Key; + string name = craftingIngredients.getNameFromIndex(item); + + toSpeak = $"{toSpeak} ,{count} {name}"; + }); + } + #endregion + + #region Add health & stamina + if (hoveredItem is StardewValley.Object && ((StardewValley.Object)hoveredItem).Edibility != -300) + { + int stamina_recovery = ((StardewValley.Object)hoveredItem).staminaRecoveredOnConsumption(); + toSpeak = $"{toSpeak} {stamina_recovery} Energy\n"; + if (stamina_recovery >= 0) + { + int health_recovery = ((StardewValley.Object)hoveredItem).healthRecoveredOnConsumption(); + toSpeak = $"{toSpeak} {health_recovery} Health"; + } + } + #endregion + + #region Add buff items (effects like +1 walking speed) + if (buffIconsToDisplay != null) + { + for (int i = 0; i < buffIconsToDisplay.Length; i++) + { + string buffName = ((Convert.ToInt32(buffIconsToDisplay[i]) > 0) ? "+" : "") + buffIconsToDisplay[i] + " "; + if (i <= 11) + { + buffName = Game1.content.LoadString("Strings\\UI:ItemHover_Buff" + i, buffName); + } + try + { + int count = int.Parse(buffName.Substring(0, buffName.IndexOf(' '))); + if (count != 0) + toSpeak = $"{toSpeak} {buffName}\n"; + } + catch (Exception) { } + } + } + #endregion + + #region Narrate toSpeak + // To prevent it from getting conflicted by two hover texts at the same time, two seperate methods are used. + // For example, sometimes `Welcome to Pierre's` and the items in seeds shop get conflicted causing it to speak infinitely. + + if (toSpeak.ToString() != " ") + { + if (StardewModdingAPI.Context.IsPlayerFree) + MainClass.ScreenReader.SayWithChecker(toSpeak.ToString(), true); // Normal Checker + else + MainClass.ScreenReader.SayWithMenuChecker(toSpeak.ToString(), true); // Menu Checker + } + #endregion + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate dialog:\n{e.StackTrace}\n{e.Message}"); + } + } + + internal static void ExitThisMenuPatch(IClickableMenu __instance) + { + try + { + Cleanup(__instance); + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static void Cleanup(IClickableMenu menu) + { + if (menu is TitleMenu) + { + TitleMenuPatch.Cleanup(); + } + else if (menu is CoopMenu) + { + CoopMenuPatch.Cleanup(); + } + else if (menu is LoadGameMenu) + { + LoadGameMenuPatch.Cleanup(); + } + else if (menu is AdvancedGameOptions) + { + AdvancedGameOptionsPatch.Cleanup(); + } + else if (menu is LetterViewerMenu) + { + LetterViwerMenuPatch.Cleanup(); + } + else if (menu is LevelUpMenu) + { + LevelUpMenuPatch.Cleanup(); + } + else if (menu is Billboard) + { + BillboardPatch.Cleanup(); + } + else if (menu is GameMenu) + { + GameMenuPatch.Cleanup(); + ExitPagePatch.Cleanup(); + OptionsPagePatch.Cleanup(); + SocialPagePatch.Cleanup(); + InventoryPagePatch.Cleanup(); + CraftingPagePatch.Cleanup(); + } + else if (menu is JunimoNoteMenu) + { + JunimoNoteMenuPatch.Cleanup(); + } + else if (menu is ShopMenu) + { + ShopMenuPatch.Cleanup(); + } + else if (menu is ItemGrabMenu) + { + ItemGrabMenuPatch.Cleanup(); + } + else if (menu is GeodeMenu) + { + GeodeMenuPatch.Cleanup(); + } + else if (menu is CarpenterMenu) + { + CarpenterMenuPatch.Cleanup(); + } + else if (menu is PurchaseAnimalsMenu) + { + PurchaseAnimalsMenuPatch.Cleanup(); + } + else if (menu is AnimalQueryMenu) + { + AnimalQueryMenuPatch.Cleanup(); + } + else if (menu is DialogueBox) + { + DialogueBoxPatch.Cleanup(); + } + else if (menu is JojaCDMenu) + { + JojaCDMenuPatch.Cleanup(); + } + else if (menu is QuestLog) + { + QuestLogPatch.Cleaup(); + } + else if (menu is TailoringMenu) + { + TailoringMenuPatch.Cleanup(); + } + else if (menu is ForgeMenu) + { + ForgeMenuPatch.Cleanup(); + } + else if (menu is ItemListMenu) + { + ItemListMenuPatch.Cleanup(); + } + else if (menu is FieldOfficeMenu) + { + FieldOfficeMenuPatch.Cleanup(); + } + else if (menu is MuseumMenu) + { + MuseumMenuPatch.Cleanup(); + } + else if (menu is PondQueryMenu) + { + PondQueryMenuPatch.Cleanup(); + } + else if (menu is GeodeMenu) + { + GeodeMenuPatch.Cleanup(); + } + else if (menu is SpecialOrdersBoard) + { + SpecialOrdersBoardPatch.Cleanup(); + } + + InventoryUtils.Cleanup(); + TextBoxPatch.activeTextBoxes = ""; + } + } +} diff --git a/stardew-access/Patches/MiscPatches/InstanceGamePatch.cs b/stardew-access/Patches/MiscPatches/InstanceGamePatch.cs new file mode 100644 index 0000000..93affb1 --- /dev/null +++ b/stardew-access/Patches/MiscPatches/InstanceGamePatch.cs @@ -0,0 +1,11 @@ +namespace stardew_access.Patches +{ + internal class InstanceGamePatch + { + internal static void ExitPatch() + { + if (MainClass.ScreenReader != null) + MainClass.ScreenReader.CloseScreenReader(); + } + } +} diff --git a/stardew-access/Patches/MiscPatches/NPCPatch.cs b/stardew-access/Patches/MiscPatches/NPCPatch.cs new file mode 100644 index 0000000..41f9306 --- /dev/null +++ b/stardew-access/Patches/MiscPatches/NPCPatch.cs @@ -0,0 +1,22 @@ +using StardewValley; + +namespace stardew_access.Patches +{ + internal class NPCPatch + { + internal static void DrawAboveAlwaysFrontLayerPatch(NPC __instance, string ___textAboveHead, int ___textAboveHeadTimer) + { + try + { + if (___textAboveHeadTimer > 2900 && ___textAboveHead != null) + { + MainClass.ScreenReader.SayWithChecker($"{__instance.displayName} says {___textAboveHead}", true); + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Error in patch:NPCShowTextAboveHeadPatch \n{e.Message}\n{e.StackTrace}"); + } + } + } +} diff --git a/stardew-access/Patches/TextBoxPatch.cs b/stardew-access/Patches/MiscPatches/TextBoxPatch.cs similarity index 100% rename from stardew-access/Patches/TextBoxPatch.cs rename to stardew-access/Patches/MiscPatches/TextBoxPatch.cs diff --git a/stardew-access/Patches/OtherMenuPatches/AnimalQueryMenuPatch.cs b/stardew-access/Patches/OtherMenuPatches/AnimalQueryMenuPatch.cs new file mode 100644 index 0000000..334e580 --- /dev/null +++ b/stardew-access/Patches/OtherMenuPatches/AnimalQueryMenuPatch.cs @@ -0,0 +1,93 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class AnimalQueryMenuPatch + { + internal static bool isNarratingAnimalInfo = false; + internal static string animalQueryMenuQuery = ""; + internal static AnimalQueryMenu? animalQueryMenu; + internal static FarmAnimal? animalBeingMoved = null; + internal static bool isOnFarm = false; + + internal static void DrawPatch(AnimalQueryMenu __instance, bool ___confirmingSell, FarmAnimal ___animal, TextBox ___textBox, string ___parentName, bool ___movingAnimal) + { + try + { + if (TextBoxPatch.isAnyTextBoxActive) return; + + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + isOnFarm = ___movingAnimal; + animalQueryMenu = __instance; + animalBeingMoved = ___animal; + + narrateAnimalDetailsOnKeyPress(___animal, ___parentName); + + narrateHoveredButton(__instance, ___animal, ___confirmingSell, x, y); + } + catch (System.Exception e) + { + MainClass.ErrorLog($"An error occured in AnimalQueryMenuPatch()->DrawPatch():\n{e.Message}\n{e.StackTrace}"); + } + } + + private static void narrateAnimalDetailsOnKeyPress(FarmAnimal ___animal, string ___parentName) + { + bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); + if (!isPrimaryInfoKeyPressed | isNarratingAnimalInfo) + return; + + string name = ___animal.displayName; + string type = ___animal.displayType; + int age = (___animal.GetDaysOwned() + 1) / 28 + 1; + string ageText = (age <= 1) ? Game1.content.LoadString("Strings\\UI:AnimalQuery_Age1") : Game1.content.LoadString("Strings\\UI:AnimalQuery_AgeN", age); + string parent = ""; + if ((int)___animal.age.Value < (byte)___animal.ageWhenMature.Value) + { + ageText += Game1.content.LoadString("Strings\\UI:AnimalQuery_AgeBaby"); + } + if (___parentName != null) + { + parent = Game1.content.LoadString("Strings\\UI:AnimalQuery_Parent", ___parentName); + } + + isNarratingAnimalInfo = true; + Task.Delay(200).ContinueWith(_ => { isNarratingAnimalInfo = false; }); // Adds delay + + MainClass.ScreenReader.Say($"Name: {name} Type: {type} \n\t Age: {ageText} {parent}", true); + } + + private static void narrateHoveredButton(AnimalQueryMenu __instance, FarmAnimal ___animal, bool ___confirmingSell, int x, int y) + { + string toSpeak = ""; + if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + toSpeak = "OK button"; + else if (__instance.sellButton != null && __instance.sellButton.containsPoint(x, y)) + toSpeak = $"Sell for {___animal.getSellPrice()}g button"; + else if (___confirmingSell && __instance.yesButton != null && __instance.yesButton.containsPoint(x, y)) + toSpeak = "Confirm selling animal"; + else if (___confirmingSell && __instance.noButton != null && __instance.noButton.containsPoint(x, y)) + toSpeak = "Cancel selling animal"; + else if (__instance.moveHomeButton != null && __instance.moveHomeButton.containsPoint(x, y)) + toSpeak = "Change home building button"; + else if (__instance.allowReproductionButton != null && __instance.allowReproductionButton.containsPoint(x, y)) + toSpeak = ((___animal.allowReproduction.Value) ? "Enabled" : "Disabled") + " allow reproduction button"; + else if (__instance.textBoxCC != null && __instance.textBoxCC.containsPoint(x, y)) + toSpeak = "Animal name text box"; + + if (animalQueryMenuQuery != toSpeak) + { + animalQueryMenuQuery = toSpeak; + MainClass.ScreenReader.Say($"{toSpeak}", true); + } + } + + internal static void Cleanup() + { + AnimalQueryMenuPatch.animalQueryMenuQuery = ""; + AnimalQueryMenuPatch.animalQueryMenu = null; + } + } +} diff --git a/stardew-access/Patches/OtherMenuPatches/CarpenterMenuPatch.cs b/stardew-access/Patches/OtherMenuPatches/CarpenterMenuPatch.cs new file mode 100644 index 0000000..81f4d29 --- /dev/null +++ b/stardew-access/Patches/OtherMenuPatches/CarpenterMenuPatch.cs @@ -0,0 +1,174 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class CarpenterMenuPatch + { + internal static CarpenterMenu? carpenterMenu = null; + internal static string carpenterMenuQuery = ""; + internal static bool isSayingBlueprintInfo = false; + internal static string prevBlueprintInfo = ""; + internal static bool isOnFarm = false, isUpgrading = false, isDemolishing = false, isPainting = false, isConstructing = false, isMoving = false, isMagicalConstruction = false; + + internal static void DrawPatch( + CarpenterMenu __instance, bool ___onFarm, List ___ingredients, int ___price, + List ___blueprints, int ___currentBlueprintIndex, bool ___upgrading, bool ___demolishing, bool ___moving, + bool ___painting, bool ___magicalConstruction) + { + try + { + isOnFarm = ___onFarm; + carpenterMenu = __instance; + isMagicalConstruction = ___magicalConstruction; + if (!___onFarm) + { + isUpgrading = false; + isDemolishing = false; + isPainting = false; + isMoving = false; + isConstructing = false; + + BluePrint currentBlueprint = __instance.CurrentBlueprint; + if (currentBlueprint == null) + return; + + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); + string blueprintInfo = getCurrentBlueprintInfo(currentBlueprint, ___price, ___ingredients); + + if (isPrimaryInfoKeyPressed && !isSayingBlueprintInfo) + { + SpeakAndWait(blueprintInfo); + } + else if (prevBlueprintInfo != blueprintInfo) + { + prevBlueprintInfo = blueprintInfo; + SpeakAndWait(blueprintInfo); + } + else + { + narrateHoveredButton(__instance, ___blueprints, ___currentBlueprintIndex, x, y); + } + } + else + { + if (___demolishing) + isDemolishing = true; + else if (___upgrading) + isUpgrading = true; + else if (___painting) + isPainting = true; + else if (___moving) + isMoving = true; + else + isConstructing = true; + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static string getCurrentBlueprintInfo(BluePrint currentBlueprint, int ___price, List ___ingredients) + { + string ingredients = ""; + string name = currentBlueprint.displayName; + string upgradeName = currentBlueprint.nameOfBuildingToUpgrade; + string description = currentBlueprint.description; + string price = $"{___price}g"; + int width = currentBlueprint.tilesWidth; + int height = currentBlueprint.tilesHeight; + + #region Get ingredients + for (int i = 0; i < ___ingredients.Count; i++) + { + string itemName = ___ingredients[i].DisplayName; + int itemStack = ___ingredients[i].Stack; + string itemQuality = ""; + + int qualityValue = ((StardewValley.Object)___ingredients[i]).Quality; + if (qualityValue == 1) + { + itemQuality = "Silver quality"; + } + else if (qualityValue == 2 || qualityValue == 3) + { + itemQuality = "Gold quality"; + } + else if (qualityValue >= 4) + { + itemQuality = "Iridium quality"; + } + + ingredients = $"{ingredients}, {itemStack} {itemName} {itemQuality}"; + } + #endregion + + return $"{name}, Price: {price}, Ingredients: {ingredients}, Dimensions: {width} width and {height} height, Description: {description}"; + } + + private static async void SpeakAndWait(string toSpeak) + { + isSayingBlueprintInfo = true; + MainClass.ScreenReader.Say(toSpeak, true); + await Task.Delay(300); + isSayingBlueprintInfo = false; + } + + private static void narrateHoveredButton(CarpenterMenu __instance, List ___blueprints, int ___currentBlueprintIndex, int x, int y) + { + string toSpeak = ""; + if (__instance.backButton != null && __instance.backButton.containsPoint(x, y)) + { + toSpeak = "Previous Blueprint"; + } + else if (__instance.forwardButton != null && __instance.forwardButton.containsPoint(x, y)) + { + toSpeak = "Next Blueprint"; + } + else if (__instance.demolishButton != null && __instance.demolishButton.containsPoint(x, y)) + { + toSpeak = $"Demolish Building" + (__instance.CanDemolishThis(___blueprints[___currentBlueprintIndex]) ? "" : ", cannot demolish building"); + } + else if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + { + toSpeak = "Construct Building" + (___blueprints[___currentBlueprintIndex].doesFarmerHaveEnoughResourcesToBuild() ? "" : ", cannot cunstrut building, not enough resources to build."); + } + else if (__instance.moveButton != null && __instance.moveButton.containsPoint(x, y)) + { + toSpeak = "Move Building"; + } + else if (__instance.paintButton != null && __instance.paintButton.containsPoint(x, y)) + { + toSpeak = "Paint Building"; + } + else if (__instance.cancelButton != null && __instance.cancelButton.containsPoint(x, y)) + { + toSpeak = "Cancel Button"; + } + else + { + return; + } + + if (carpenterMenuQuery != toSpeak) + { + carpenterMenuQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + } + + internal static void Cleanup() + { + CarpenterMenuPatch.carpenterMenuQuery = ""; + CarpenterMenuPatch.isUpgrading = false; + CarpenterMenuPatch.isDemolishing = false; + CarpenterMenuPatch.isPainting = false; + CarpenterMenuPatch.isMoving = false; + CarpenterMenuPatch.isConstructing = false; + CarpenterMenuPatch.carpenterMenu = null; + } + } +} diff --git a/stardew-access/Patches/OtherMenuPatches/ChooseFromListMenuPatch.cs b/stardew-access/Patches/OtherMenuPatches/ChooseFromListMenuPatch.cs new file mode 100644 index 0000000..2e19781 --- /dev/null +++ b/stardew-access/Patches/OtherMenuPatches/ChooseFromListMenuPatch.cs @@ -0,0 +1,32 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class ChooseFromListMenuPatch + { + internal static void DrawPatch(ChooseFromListMenu __instance, List ___options, int ___index, bool ___isJukebox) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + string toSpeak = ""; + + if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + toSpeak = "Select " + (___isJukebox ? Utility.getSongTitleFromCueName(___options[___index]) : ___options[___index]) + " button"; + else if (__instance.cancelButton != null && __instance.cancelButton.containsPoint(x, y)) + toSpeak = "Cancel button"; + else if (__instance.backButton != null && __instance.backButton.containsPoint(x, y)) + toSpeak = "Previous option: " + (___isJukebox ? Utility.getSongTitleFromCueName(___options[Math.Max(0, ___index - 1)]) : ___options[Math.Max(0, ___index - 1)]) + " button"; + else if (__instance.forwardButton != null && __instance.forwardButton.containsPoint(x, y)) + toSpeak = "Next option: " + (___isJukebox ? Utility.getSongTitleFromCueName(___options[Math.Min(___options.Count, ___index + 1)]) : ___options[Math.Min(___options.Count, ___index + 1)]) + " button"; + + MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true); + } + catch (System.Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + } +} diff --git a/stardew-access/Patches/OtherMenuPatches/ConfirmationDialogMenuPatch.cs b/stardew-access/Patches/OtherMenuPatches/ConfirmationDialogMenuPatch.cs new file mode 100644 index 0000000..ffde9ec --- /dev/null +++ b/stardew-access/Patches/OtherMenuPatches/ConfirmationDialogMenuPatch.cs @@ -0,0 +1,32 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class ConfirmationDialogMenuPatch + { + internal static void DrawPatch(ConfirmationDialog __instance, string ___message) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); + string toSpeak = ___message; + + if (__instance.okButton.containsPoint(x, y)) + { + toSpeak += "\n\tOk Button"; + } + else if (__instance.cancelButton.containsPoint(x, y)) + { + toSpeak += "\n\tCancel Button"; + } + + MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true); + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + } +} diff --git a/stardew-access/Patches/OtherMenuPatches/ItemListMenuPatch.cs b/stardew-access/Patches/OtherMenuPatches/ItemListMenuPatch.cs new file mode 100644 index 0000000..e94a206 --- /dev/null +++ b/stardew-access/Patches/OtherMenuPatches/ItemListMenuPatch.cs @@ -0,0 +1,55 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class ItemListMenuPatch + { + private static string itemListMenuQuery = ""; + + internal static void DrawPatch(ItemListMenu __instance, string ___title, int ___currentTab, int ___totalValueOfItems, List ___itemsToList) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + string toSpeak = "", currentList = ""; + + for (int i = ___currentTab * __instance.itemsPerCategoryPage; i < ___currentTab * __instance.itemsPerCategoryPage + __instance.itemsPerCategoryPage; i++) + { + if (i == 0) currentList = ___title; + if (___itemsToList.Count <= i) continue; + + if (___itemsToList[i] == null) + { + currentList = $"{currentList}, \n" + Game1.content.LoadString("Strings\\UI:ItemList_ItemsLostValue", ___totalValueOfItems); + continue; + } + + currentList = $"{currentList}, \n {___itemsToList[i].Stack} {___itemsToList[i].DisplayName}"; + } + + if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + toSpeak = $"Page {___currentTab + 1} of {((int)___itemsToList.Count / __instance.itemsPerCategoryPage) + 1} \n {currentList} \n ok button"; + else if (__instance.forwardButton != null && __instance.forwardButton.containsPoint(x, y)) + toSpeak = "Next page button"; + else if (__instance.backButton != null && __instance.backButton.containsPoint(x, y)) + toSpeak = "Previous page button"; + + if (itemListMenuQuery != toSpeak) + { + itemListMenuQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + } + catch (System.Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static void Cleanup() + { + itemListMenuQuery = ""; + } + } +} diff --git a/stardew-access/Patches/OtherMenuPatches/LanguageSelectionMenuPatch.cs b/stardew-access/Patches/OtherMenuPatches/LanguageSelectionMenuPatch.cs new file mode 100644 index 0000000..968a171 --- /dev/null +++ b/stardew-access/Patches/OtherMenuPatches/LanguageSelectionMenuPatch.cs @@ -0,0 +1,41 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class LanguageSelectionMenuPatch + { + internal static void DrawPatch(LanguageSelectionMenu __instance) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + if (__instance.nextPageButton != null && __instance.nextPageButton.containsPoint(x, y)) + { + MainClass.ScreenReader.SayWithMenuChecker($"Next Page Button", true); + return; + } + + if (__instance.previousPageButton != null && __instance.previousPageButton.containsPoint(x, y)) + { + MainClass.ScreenReader.SayWithMenuChecker($"Previous Page Button", true); + return; + } + + for (int i = 0; i < __instance.languages.Count; i++) + { + if (__instance.languages[i].containsPoint(x, y)) + { + MainClass.ScreenReader.SayWithMenuChecker($"{__instance.languageList[i]} Button", true); + break; + } + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + } +} diff --git a/stardew-access/Patches/OtherMenuPatches/LetterViewerMenuPatch.cs b/stardew-access/Patches/OtherMenuPatches/LetterViewerMenuPatch.cs new file mode 100644 index 0000000..de7eabb --- /dev/null +++ b/stardew-access/Patches/OtherMenuPatches/LetterViewerMenuPatch.cs @@ -0,0 +1,95 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class LetterViwerMenuPatch + { + private static string currentLetterText = ""; + + internal static void DrawPatch(LetterViewerMenu __instance) + { + try + { + if (!__instance.IsActive()) + return; + + narrateLetterContent(__instance); + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static void narrateLetterContent(LetterViewerMenu __instance) + { + int x = Game1.getMousePosition().X, y = Game1.getMousePosition().Y; + #region Texts in the letter + string message = __instance.mailMessage[__instance.page]; + + string toSpeak = $"{message}"; + + if (__instance.ShouldShowInteractable()) + { + if (__instance.moneyIncluded > 0) + { + string moneyText = Game1.content.LoadString("Strings\\UI:LetterViewer_MoneyIncluded", __instance.moneyIncluded); + toSpeak += $"\t\n\t ,Included money: {moneyText}"; + } + else if (__instance.learnedRecipe != null && __instance.learnedRecipe.Length > 0) + { + string recipeText = Game1.content.LoadString("Strings\\UI:LetterViewer_LearnedRecipe", __instance.cookingOrCrafting); + toSpeak += $"\t\n\t ,Learned Recipe: {recipeText}"; + } + } + + if (currentLetterText != toSpeak) + { + currentLetterText = toSpeak; + + // snap mouse to accept quest button + if (__instance.acceptQuestButton != null && __instance.questID != -1) + { + toSpeak += "\t\n Left click to accept quest."; + __instance.acceptQuestButton.snapMouseCursorToCenter(); + } + if (__instance.mailMessage.Count > 1) + toSpeak = $"Page {__instance.page + 1} of {__instance.mailMessage.Count}:\n\t{toSpeak}"; + + MainClass.ScreenReader.Say(toSpeak, true); + } + #endregion + + #region Narrate items given in the mail + if (__instance.ShouldShowInteractable()) + { + foreach (ClickableComponent c in __instance.itemsToGrab) + { + if (c.item == null) + continue; + + string name = c.item.DisplayName; + + if (c.containsPoint(x, y)) + MainClass.ScreenReader.SayWithChecker($"Left click to collect {name}", false); + } + } + #endregion + + #region Narrate buttons + if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y)) + MainClass.ScreenReader.SayWithChecker($"Previous page button", false); + + if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y)) + MainClass.ScreenReader.SayWithChecker($"Next page button", false); + + #endregion + } + + internal static void Cleanup() + { + currentLetterText = ""; + } + } +} diff --git a/stardew-access/Patches/OtherMenuPatches/LevelUpMenuPatch.cs b/stardew-access/Patches/OtherMenuPatches/LevelUpMenuPatch.cs new file mode 100644 index 0000000..cb4535b --- /dev/null +++ b/stardew-access/Patches/OtherMenuPatches/LevelUpMenuPatch.cs @@ -0,0 +1,109 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class LevelUpMenuPatch + { + private static string currentLevelUpTitle = ""; + + internal static void DrawPatch(LevelUpMenu __instance, List ___professionsToChoose, List ___leftProfessionDescription, List ___rightProfessionDescription, List ___extraInfoForLevel, List ___newCraftingRecipes, string ___title, bool ___isActive, bool ___isProfessionChooser) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); + string leftProfession = "", rightProfession = "", extraInfo = "", newCraftingRecipe = "", toSpeak = ""; + + if (!__instance.informationUp) + return; + + if (__instance.isProfessionChooser) + { + if (___professionsToChoose.Count() == 0) return; + + for (int j = 0; j < ___leftProfessionDescription.Count; j++) + { + leftProfession += ___leftProfessionDescription[j] + ", "; + } + for (int i = 0; i < ___rightProfessionDescription.Count; i++) + { + rightProfession += ___rightProfessionDescription[i] + ", "; + } + + if (__instance.leftProfession.containsPoint(x, y)) + { + if ((MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) && __instance.readyToClose()) + { + Game1.player.professions.Add(___professionsToChoose[0]); + __instance.getImmediateProfessionPerk(___professionsToChoose[0]); + ___isActive = false; + __instance.informationUp = false; + ___isProfessionChooser = false; + __instance.RemoveLevelFromLevelList(); + __instance.exitThisMenu(); + return; + } + + toSpeak = $"Selected: {leftProfession} Left click to choose."; + } + + if (__instance.rightProfession.containsPoint(x, y)) + { + if ((MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) && __instance.readyToClose()) + { + Game1.player.professions.Add(___professionsToChoose[1]); + __instance.getImmediateProfessionPerk(___professionsToChoose[1]); + ___isActive = false; + __instance.informationUp = false; + ___isProfessionChooser = false; + __instance.RemoveLevelFromLevelList(); + __instance.exitThisMenu(); + return; + } + + toSpeak = $"Selected: {rightProfession} Left click to choose."; + } + } + else + { + foreach (string s2 in ___extraInfoForLevel) + { + extraInfo += s2 + ", "; + } + foreach (CraftingRecipe s in ___newCraftingRecipes) + { + string cookingOrCrafting = Game1.content.LoadString("Strings\\UI:LearnedRecipe_" + (s.isCookingRecipe ? "cooking" : "crafting")); + string message = Game1.content.LoadString("Strings\\UI:LevelUp_NewRecipe", cookingOrCrafting, s.DisplayName); + + newCraftingRecipe += $"{message}, "; + } + } + + if (__instance.okButton.containsPoint(x, y)) + { + if (MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) + __instance.okButtonClicked(); + + toSpeak = $"{___title} {extraInfo} {newCraftingRecipe}. Left click to close."; + } + + if (toSpeak != "") + MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true); + else if (__instance.isProfessionChooser && currentLevelUpTitle != $"{___title}. Select a new profession.") + { + MainClass.ScreenReader.SayWithMenuChecker($"{___title}. Select a new profession.", true); + currentLevelUpTitle = $"{___title}. Select a new profession."; + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static void Cleanup() + { + currentLevelUpTitle = ""; + } + } +} diff --git a/stardew-access/Patches/OtherMenuPatches/MineElevatorMenuPatch.cs b/stardew-access/Patches/OtherMenuPatches/MineElevatorMenuPatch.cs new file mode 100644 index 0000000..c1110f1 --- /dev/null +++ b/stardew-access/Patches/OtherMenuPatches/MineElevatorMenuPatch.cs @@ -0,0 +1,28 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class MineElevatorMenuPatch + { + internal static void DrawPatch(List ___elevators) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + for (int i = 0; i < ___elevators.Count; i++) + { + if (___elevators[i].containsPoint(x, y)) + { + MainClass.ScreenReader.SayWithMenuChecker($"{___elevators[i].name} level", true); + break; + } + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + } +} diff --git a/stardew-access/Patches/OtherMenuPatches/NamingMenuPatch.cs b/stardew-access/Patches/OtherMenuPatches/NamingMenuPatch.cs new file mode 100644 index 0000000..06e8f0e --- /dev/null +++ b/stardew-access/Patches/OtherMenuPatches/NamingMenuPatch.cs @@ -0,0 +1,42 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class NamingMenuPatch + { + internal static bool firstTimeInNamingMenu = true; + + internal static void DrawPatch(NamingMenu __instance, TextBox ___textBox, string ___title) + { + try + { + if (firstTimeInNamingMenu) + { + firstTimeInNamingMenu = false; + ___textBox.Selected = false; + } + + if (TextBoxPatch.isAnyTextBoxActive) return; + + string toSpeak = ""; + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box + + if (__instance.textBoxCC != null && __instance.textBoxCC.containsPoint(x, y)) + toSpeak = $"{___title} text box"; + else if (__instance.doneNamingButton != null && __instance.doneNamingButton.containsPoint(x, y)) + toSpeak = $"Done naming button"; + else if (__instance.randomButton != null && __instance.randomButton.containsPoint(x, y)) + toSpeak = $"Random button"; + + if (toSpeak != "") + MainClass.ScreenReader.SayWithChecker(toSpeak, true); + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + } +} diff --git a/stardew-access/Patches/OtherMenuPatches/PondQuerMenuPatch.cs b/stardew-access/Patches/OtherMenuPatches/PondQuerMenuPatch.cs new file mode 100644 index 0000000..a370fc1 --- /dev/null +++ b/stardew-access/Patches/OtherMenuPatches/PondQuerMenuPatch.cs @@ -0,0 +1,72 @@ +using StardewValley; +using StardewValley.Buildings; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class PondQueryMenuPatch + { + private static string pondQueryMenuQuery = ""; + private static bool isNarratingPondInfo = false; + + internal static void DrawPatch(PondQueryMenu __instance, StardewValley.Object ____fishItem, FishPond ____pond, string ____statusText, bool ___confirmingEmpty) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); + string toSpeak = "", extra = ""; + + if (___confirmingEmpty) + { + if (__instance.yesButton != null && __instance.yesButton.containsPoint(x, y)) + toSpeak = "Confirm button"; + else if (__instance.noButton != null && __instance.noButton.containsPoint(x, y)) + toSpeak = "Cancel button"; + } + else + { + if (isPrimaryInfoKeyPressed && !isNarratingPondInfo) + { + string pond_name_text = Game1.content.LoadString("Strings\\UI:PondQuery_Name", ____fishItem.DisplayName); + string population_text = Game1.content.LoadString("Strings\\UI:PondQuery_Population", string.Concat(____pond.FishCount), ____pond.maxOccupants.Value); + bool has_unresolved_needs = ____pond.neededItem.Value != null && ____pond.HasUnresolvedNeeds() && !____pond.hasCompletedRequest.Value; + string bring_text = ""; + + if (has_unresolved_needs && ____pond.neededItem.Value != null) + bring_text = Game1.content.LoadString("Strings\\UI:PondQuery_StatusRequest_Bring") + $": {____pond.neededItemCount} {____pond.neededItem.Value.DisplayName}"; + + extra = $"{pond_name_text} {population_text} {bring_text} Status: {____statusText}"; + pondQueryMenuQuery = ""; + + isNarratingPondInfo = true; + Task.Delay(200).ContinueWith(_ => { isNarratingPondInfo = false; }); + } + + if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + toSpeak = "Ok button"; + else if (__instance.changeNettingButton != null && __instance.changeNettingButton.containsPoint(x, y)) + toSpeak = "Change netting button"; + else if (__instance.emptyButton != null && __instance.emptyButton.containsPoint(x, y)) + toSpeak = "Empty pond button"; + } + + if (pondQueryMenuQuery != toSpeak) + { + pondQueryMenuQuery = toSpeak; + MainClass.ScreenReader.Say(extra + " \n\t" + toSpeak, true); + } + } + catch (System.Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static void Cleanup() + { + pondQueryMenuQuery = ""; + isNarratingPondInfo = false; + } + } +} diff --git a/stardew-access/Patches/OtherMenuPatches/PurchaseAnimalsMenuPatch.cs b/stardew-access/Patches/OtherMenuPatches/PurchaseAnimalsMenuPatch.cs new file mode 100644 index 0000000..8ff6dff --- /dev/null +++ b/stardew-access/Patches/OtherMenuPatches/PurchaseAnimalsMenuPatch.cs @@ -0,0 +1,113 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class PurchaseAnimalsMenuPatch + { + internal static FarmAnimal? animalBeingPurchased = null; + internal static bool isOnFarm = false; + internal static string purchaseAnimalMenuQuery = ""; + internal static PurchaseAnimalsMenu? purchaseAnimalsMenu; + internal static bool firstTimeInNamingMenu = true; + + internal static void DrawPatch(PurchaseAnimalsMenu __instance, bool ___onFarm, bool ___namingAnimal, TextBox ___textBox, FarmAnimal ___animalBeingPurchased) + { + try + { + if (TextBoxPatch.isAnyTextBoxActive) return; + + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + purchaseAnimalsMenu = __instance; + isOnFarm = ___onFarm; + animalBeingPurchased = ___animalBeingPurchased; + + if (___onFarm && ___namingAnimal) + { + narrateNamingMenu(__instance, x, y); + } + else if (___onFarm && !___namingAnimal) + { + firstTimeInNamingMenu = true; + } + else if (!___onFarm && !___namingAnimal) + { + firstTimeInNamingMenu = true; + narratePurchasingMenu(__instance); + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static void narrateNamingMenu(PurchaseAnimalsMenu __instance, int x, int y) + { + string toSpeak = ""; + if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + { + toSpeak = "Cancel Button"; + } + else if (__instance.doneNamingButton != null && __instance.doneNamingButton.containsPoint(x, y)) + { + toSpeak = "OK Button"; + } + else if (__instance.randomButton != null && __instance.randomButton.containsPoint(x, y)) + { + toSpeak = "Random Name Button"; + } + else if (__instance.textBoxCC != null && __instance.textBoxCC.containsPoint(x, y)) + { + toSpeak = "Name Text Box"; + // string? value = ___textBox.Text; + // if (value != "" && value != null && value != "null") + // toSpeak = $"{toSpeak}, Value: {value}"; + } + + if (purchaseAnimalMenuQuery == toSpeak) return; + + purchaseAnimalMenuQuery = toSpeak; + + if (firstTimeInNamingMenu) + { + toSpeak = $"Enter the name of animal in the name text box. {toSpeak}"; + firstTimeInNamingMenu = false; + } + + MainClass.ScreenReader.Say(toSpeak, true); + } + + private static void narratePurchasingMenu(PurchaseAnimalsMenu __instance) + { + if (__instance.hovered == null) + return; + + string toSpeak = ""; + if (((StardewValley.Object)__instance.hovered.item).Type != null) + { + toSpeak = ((StardewValley.Object)__instance.hovered.item).Type; + } + else + { + string displayName = PurchaseAnimalsMenu.getAnimalTitle(__instance.hovered.hoverText); + int price = __instance.hovered.item.salePrice(); + string description = PurchaseAnimalsMenu.getAnimalDescription(__instance.hovered.hoverText); + + toSpeak = $"{displayName}, Price: {price}g, Description: {description}"; + } + + if (purchaseAnimalMenuQuery == toSpeak) return; + + purchaseAnimalMenuQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + + internal static void Cleanup() + { + purchaseAnimalMenuQuery = ""; + firstTimeInNamingMenu = true; + purchaseAnimalsMenu = null; + } + } +} diff --git a/stardew-access/Patches/OtherMenuPatches/ShippingMenuPatch.cs b/stardew-access/Patches/OtherMenuPatches/ShippingMenuPatch.cs new file mode 100644 index 0000000..692d9ee --- /dev/null +++ b/stardew-access/Patches/OtherMenuPatches/ShippingMenuPatch.cs @@ -0,0 +1,46 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class ShippingMenuPatch + { + internal static int prevSlotIndex = -999; + + internal static void DrawPatch(ShippingMenu __instance, List ___categoryTotals) + { + try + { + + if (__instance.currentPage == -1) + { + int total = ___categoryTotals[5]; + string toSpeak; + if (__instance.okButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) + { + // Perform Left Click + if (MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) + { + Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); + } + toSpeak = $"{total}g in total. Press left mouse button to save."; + MainClass.ScreenReader.SayWithChecker(toSpeak, true); + } + + for (int i = 0; i < __instance.categories.Count; i++) + { + if (__instance.categories[i].containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) + { + toSpeak = $"Money recieved from {__instance.getCategoryName(i)}: {___categoryTotals[i]}g."; + MainClass.ScreenReader.SayWithChecker(toSpeak, true); + } + } + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + } +} diff --git a/stardew-access/Patches/OtherMenuPatches/TitleTextInputMenuPatch.cs b/stardew-access/Patches/OtherMenuPatches/TitleTextInputMenuPatch.cs new file mode 100644 index 0000000..2ed19ac --- /dev/null +++ b/stardew-access/Patches/OtherMenuPatches/TitleTextInputMenuPatch.cs @@ -0,0 +1,27 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class TitleTextInputMenuPatch + { + internal static void DrawPatch(TitleTextInputMenu __instance) + { + try + { + string toSpeak = ""; + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + if (__instance.pasteButton != null && __instance.pasteButton.containsPoint(x, y)) + toSpeak = $"Paste button"; + + if (toSpeak != "") + MainClass.ScreenReader.SayWithChecker(toSpeak, true); + } + catch (System.Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + } +} diff --git a/stardew-access/Patches/QuestPatches.cs b/stardew-access/Patches/QuestPatches.cs deleted file mode 100644 index 0b38787..0000000 --- a/stardew-access/Patches/QuestPatches.cs +++ /dev/null @@ -1,287 +0,0 @@ -using Microsoft.Xna.Framework.Graphics; -using StardewValley; -using StardewValley.Menus; -using StardewValley.Quests; - -namespace stardew_access.Patches -{ - internal class QuestPatches - { - internal static string currentDailyQuestText = " "; - internal static string questLogQuery = " "; - internal static bool isNarratingQuestInfo = false, firstTimeInIndividualQuest = true; - - #region For Special Orders Board - internal static void SpecialOrdersBoardPatch(SpecialOrdersBoard __instance) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - - if (__instance.acceptLeftQuestButton.visible && __instance.acceptLeftQuestButton.containsPoint(x, y)) - { - string toSpeak = getSpecialOrderDetails(__instance.leftOrder); - - toSpeak = $"Left Quest:\n\t{toSpeak}\n\tPress left click to accept this quest."; - - MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true); - return; - } - - if (__instance.acceptRightQuestButton.visible && __instance.acceptRightQuestButton.containsPoint(x, y)) - { - string toSpeak = getSpecialOrderDetails(__instance.rightOrder); - - toSpeak = $"Right Quest:\n\t{toSpeak}\n\tPress left click to accept this quest."; - - MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true); - return; - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - private static string getSpecialOrderDetails(SpecialOrder order) - { - int daysLeft = order.GetDaysLeft(); - string description = order.GetDescription(); - string objectiveDescription = ""; - string name = order.GetName(); - int moneyReward = order.GetMoneyReward(); - - // Get each objectives - for (int i = 0; i < order.GetObjectiveDescriptions().Count; i++) - { - objectiveDescription += order.GetObjectiveDescriptions()[i] + ", \n"; - } - - string toReturn = $"{name}\n\tDescription:{description}\n\tObjectives: {objectiveDescription}"; - - if (order.IsTimedQuest()) - { - toReturn = $"{toReturn}\n\tTime: {daysLeft} days"; - } - - if (order.HasMoneyReward()) - { - toReturn = $"{toReturn}\n\tReward: {moneyReward}g"; - } - - return toReturn; - } - #endregion - - #region For Normal Billboard in the town - internal static void BillboardPatch(Billboard __instance, bool ___dailyQuestBoard) - { - try - { - if (!___dailyQuestBoard) - { - #region Callender - for (int i = 0; i < __instance.calendarDays.Count; i++) - { - if (__instance.calendarDays[i].containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) - { - string toSpeak = $"Day {i + 1}"; - - if (__instance.calendarDays[i].name.Length > 0) - { - toSpeak += $", {__instance.calendarDays[i].name}"; - } - if (__instance.calendarDays[i].hoverText.Length > 0) - { - toSpeak += $", {__instance.calendarDays[i].hoverText}"; - } - - if (Game1.dayOfMonth == i + 1) - toSpeak += $", Current"; - - MainClass.ScreenReader.SayWithChecker(toSpeak, true); - } - } - #endregion - } - else - { - #region Daily Quest Board - if (Game1.questOfTheDay == null || Game1.questOfTheDay.currentObjective == null || Game1.questOfTheDay.currentObjective.Length == 0) - { - // No quests - string toSpeak = "No quests for today!"; - if (currentDailyQuestText != toSpeak) - { - currentDailyQuestText = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - } - else - { - SpriteFont font = ((LocalizedContentManager.CurrentLanguageCode == LocalizedContentManager.LanguageCode.ko) ? Game1.smallFont : Game1.dialogueFont); - string description = Game1.parseText(Game1.questOfTheDay.questDescription, font, 640); - string toSpeak = description; - - if (currentDailyQuestText != toSpeak) - { - currentDailyQuestText = toSpeak; - - // Snap to accept quest button - if (__instance.acceptQuestButton.visible) - { - toSpeak += "\t\n Left click to accept quest."; - __instance.acceptQuestButton.snapMouseCursorToCenter(); - } - - MainClass.ScreenReader.Say(toSpeak, true); - } - } - #endregion - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - #endregion - - #region Journal Menu - internal static void QuestLogPatch(QuestLog __instance, int ___questPage, List> ___pages, int ___currentPage, IQuest ____shownQuest, List ____objectiveText) - { - try - { - bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - string toSpeak = " ", extra = ""; - - if (___questPage == -1) - { - #region Quest Lists - if (!firstTimeInIndividualQuest) - firstTimeInIndividualQuest = true; - - for (int i = 0; i < __instance.questLogButtons.Count; i++) - { - if (___pages.Count() > 0 && ___pages[___currentPage].Count() > i) - { - if (!__instance.questLogButtons[i].containsPoint(x, y)) - continue; - - string name = ___pages[___currentPage][i].GetName(); - int daysLeft = ___pages[___currentPage][i].GetDaysLeft(); - toSpeak = $"{name}"; - - if (daysLeft > 0 && ___pages[___currentPage][i].ShouldDisplayAsComplete()) - toSpeak += $"\t\n {daysLeft} days left"; - - toSpeak += ___pages[___currentPage][i].ShouldDisplayAsComplete() ? " completed!" : ""; - break; - } - } - - if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y)) - toSpeak = "Previous page button"; - else if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y)) - toSpeak = "Next page button"; - else if (__instance.upperRightCloseButton != null && __instance.upperRightCloseButton.visible && __instance.upperRightCloseButton.containsPoint(x, y)) - toSpeak = "Close menu button"; - - if (questLogQuery != toSpeak) - { - questLogQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - #endregion - } - else - { - #region Individual quest - bool containsReward = __instance.HasReward() || __instance.HasMoneyReward(); - string description = Game1.parseText(____shownQuest.GetDescription(), Game1.dialogueFont, __instance.width - 128); - string title = ____shownQuest.GetName(); - - if (firstTimeInIndividualQuest || (isPrimaryInfoKeyPressed && !isNarratingQuestInfo)) - { - if (firstTimeInIndividualQuest) - toSpeak = "Back button"; - - if (____shownQuest.ShouldDisplayAsComplete()) - { - #region Quest completed menu - - extra = $"Quest: {title} Completed!"; - - if (__instance.HasMoneyReward()) - extra += $"you recieved {____shownQuest.GetMoneyReward()}g"; - - #endregion - } - else - { - #region Quest in-complete menu - extra = $"Title: {title}. \t\n Description: {description}"; - - for (int j = 0; j < ____objectiveText.Count; j++) - { - string parsed_text = Game1.parseText(____objectiveText[j], width: __instance.width - 192, whichFont: Game1.dialogueFont); - if (____shownQuest != null && ____shownQuest is SpecialOrder) - { - OrderObjective order_objective = ((SpecialOrder)____shownQuest).objectives[j]; - if (order_objective.GetMaxCount() > 1 && order_objective.ShouldShowProgress()) - parsed_text += "\n\t" + order_objective.GetCount() + " of " + order_objective.GetMaxCount() + " completed"; - } - - extra += $"\t\nOrder {j + 1}: {parsed_text} \t\n"; - } - - if (____shownQuest != null) - { - int daysLeft = ____shownQuest.GetDaysLeft(); - - if (daysLeft > 0) - extra += $"\t\n{daysLeft} days left."; - } - #endregion - } - - isNarratingQuestInfo = true; - Task.Delay(200).ContinueWith(_ => { isNarratingQuestInfo = false; }); - questLogQuery = " "; - } - - if (!firstTimeInIndividualQuest) - if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y)) - toSpeak = (___currentPage > 0) ? "Previous page button" : "Back button"; - else if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y)) - toSpeak = "Next page button"; - else if (__instance.cancelQuestButton != null && __instance.cancelQuestButton.visible && __instance.cancelQuestButton.containsPoint(x, y)) - toSpeak = "Cancel quest button"; - else if (__instance.upperRightCloseButton != null && __instance.upperRightCloseButton.visible && __instance.upperRightCloseButton.containsPoint(x, y)) - toSpeak = "Close menu button"; - else if (containsReward && __instance.rewardBox.containsPoint(x, y)) - toSpeak = "Left click to collect reward"; - - if (firstTimeInIndividualQuest || (questLogQuery != toSpeak)) - { - questLogQuery = toSpeak; - MainClass.ScreenReader.Say(extra + " \n\t" + toSpeak, true); - - if (firstTimeInIndividualQuest) - firstTimeInIndividualQuest = false; - } - - #endregion - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - #endregion - - } -} diff --git a/stardew-access/Patches/QuestPatches/BillboardPatch.cs b/stardew-access/Patches/QuestPatches/BillboardPatch.cs new file mode 100644 index 0000000..f8e4f47 --- /dev/null +++ b/stardew-access/Patches/QuestPatches/BillboardPatch.cs @@ -0,0 +1,98 @@ +using Microsoft.Xna.Framework.Graphics; +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class BillboardPatch + { + private static string billboardQueryKey = ""; + + internal static void DrawPatch(Billboard __instance, bool ___dailyQuestBoard) + { + try + { + if (___dailyQuestBoard) + { + narrateDailyQuestBoard(__instance); + } + else + { + narrateCallendar(__instance); + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static void narrateCallendar(Billboard __instance) + { + for (int i = 0; i < __instance.calendarDays.Count; i++) + { + if (!__instance.calendarDays[i].containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) + continue; + + string toSpeak = $"Day {i + 1}"; + + if (__instance.calendarDays[i].name.Length > 0) + { + toSpeak += $", {__instance.calendarDays[i].name}"; + } + if (__instance.calendarDays[i].hoverText.Length > 0) + { + toSpeak += $", {__instance.calendarDays[i].hoverText}"; + } + + if (Game1.dayOfMonth == i + 1) + toSpeak += $", Current"; + + if (billboardQueryKey != toSpeak) + { + billboardQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + } + } + + private static void narrateDailyQuestBoard(Billboard __instance) + { + if (Game1.questOfTheDay == null || Game1.questOfTheDay.currentObjective == null || Game1.questOfTheDay.currentObjective.Length == 0) + { + // No quests + string toSpeak = "No quests for today!"; + if (billboardQueryKey != toSpeak) + { + billboardQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + } + else + { + SpriteFont font = ((LocalizedContentManager.CurrentLanguageCode == LocalizedContentManager.LanguageCode.ko) ? Game1.smallFont : Game1.dialogueFont); + string description = Game1.parseText(Game1.questOfTheDay.questDescription, font, 640); + string toSpeak = description; + + if (billboardQueryKey != toSpeak) + { + billboardQueryKey = toSpeak; + + // Snap to accept quest button + if (__instance.acceptQuestButton.visible) + { + toSpeak += "\t\n Left click to accept quest."; + __instance.acceptQuestButton.snapMouseCursorToCenter(); + } + + MainClass.ScreenReader.Say(toSpeak, true); + } + } + } + + internal static void Cleanup() + { + billboardQueryKey = ""; + } + } +} diff --git a/stardew-access/Patches/QuestPatches/QuestLogPatch.cs b/stardew-access/Patches/QuestPatches/QuestLogPatch.cs new file mode 100644 index 0000000..c93ddc1 --- /dev/null +++ b/stardew-access/Patches/QuestPatches/QuestLogPatch.cs @@ -0,0 +1,164 @@ +using StardewValley; +using StardewValley.Menus; +using StardewValley.Quests; + +namespace stardew_access.Patches +{ + // a.k.a. Journal Menu + internal class QuestLogPatch + { + internal static string questLogQuery = ""; + internal static bool isNarratingQuestInfo = false; + internal static bool firstTimeInIndividualQuest = true; + + internal static void DrawPatch(QuestLog __instance, int ___questPage, List> ___pages, int ___currentPage, IQuest ____shownQuest, List ____objectiveText) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + if (___questPage == -1) + { + narrateQuestList(__instance, ___pages, ___currentPage, x, y); + } + else + { + narrateIndividualQuest(__instance, ___currentPage, ____shownQuest, ____objectiveText, x, y); + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static void narrateQuestList(QuestLog __instance, List> ___pages, int ___currentPage, int x, int y) + { + string toSpeak = ""; + + if (!firstTimeInIndividualQuest) firstTimeInIndividualQuest = true; + + if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y)) + toSpeak = "Previous page button"; + else if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y)) + toSpeak = "Next page button"; + else if (__instance.upperRightCloseButton != null && __instance.upperRightCloseButton.visible && __instance.upperRightCloseButton.containsPoint(x, y)) + toSpeak = "Close menu button"; + else + { + for (int i = 0; i < __instance.questLogButtons.Count; i++) + { + if (___pages.Count() <= 0 || ___pages[___currentPage].Count() <= i) + continue; + + if (!__instance.questLogButtons[i].containsPoint(x, y)) + continue; + + string name = ___pages[___currentPage][i].GetName(); + int daysLeft = ___pages[___currentPage][i].GetDaysLeft(); + toSpeak = $"{name}"; + + if (daysLeft > 0 && ___pages[___currentPage][i].ShouldDisplayAsComplete()) + toSpeak += $"\t\n {daysLeft} days left"; + + toSpeak += ___pages[___currentPage][i].ShouldDisplayAsComplete() ? " completed!" : ""; + break; + } + } + + if (questLogQuery != toSpeak) + { + questLogQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + } + + private static void narrateIndividualQuest(QuestLog __instance, int ___currentPage, IQuest ____shownQuest, List ____objectiveText, int x, int y) + { + bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); + bool containsReward = __instance.HasReward() || __instance.HasMoneyReward(); + string description = Game1.parseText(____shownQuest.GetDescription(), Game1.dialogueFont, __instance.width - 128); + string title = ____shownQuest.GetName(); + string toSpeak = ""; + string extra = ""; + + if (firstTimeInIndividualQuest || (isPrimaryInfoKeyPressed && !isNarratingQuestInfo)) + { + if (firstTimeInIndividualQuest) + toSpeak = "Back button"; + + if (____shownQuest.ShouldDisplayAsComplete()) + { + #region Quest completed menu + + extra = $"Quest: {title} Completed!"; + + if (__instance.HasMoneyReward()) + extra += $"you recieved {____shownQuest.GetMoneyReward()}g"; + + #endregion + } + else + { + #region Quest in-complete menu + extra = $"Title: {title}. \t\n Description: {description}"; + + for (int j = 0; j < ____objectiveText.Count; j++) + { + string parsed_text = Game1.parseText(____objectiveText[j], width: __instance.width - 192, whichFont: Game1.dialogueFont); + if (____shownQuest != null && ____shownQuest is SpecialOrder) + { + OrderObjective order_objective = ((SpecialOrder)____shownQuest).objectives[j]; + if (order_objective.GetMaxCount() > 1 && order_objective.ShouldShowProgress()) + parsed_text += "\n\t" + order_objective.GetCount() + " of " + order_objective.GetMaxCount() + " completed"; + } + + extra += $"\t\nOrder {j + 1}: {parsed_text} \t\n"; + } + + if (____shownQuest != null) + { + int daysLeft = ____shownQuest.GetDaysLeft(); + + if (daysLeft > 0) + extra += $"\t\n{daysLeft} days left."; + } + #endregion + } + + isNarratingQuestInfo = true; + Task.Delay(200).ContinueWith(_ => { isNarratingQuestInfo = false; }); + questLogQuery = ""; + } + + if (!firstTimeInIndividualQuest) + { + if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y)) + toSpeak = (___currentPage > 0) ? "Previous page button" : "Back button"; + else if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y)) + toSpeak = "Next page button"; + else if (__instance.cancelQuestButton != null && __instance.cancelQuestButton.visible && __instance.cancelQuestButton.containsPoint(x, y)) + toSpeak = "Cancel quest button"; + else if (__instance.upperRightCloseButton != null && __instance.upperRightCloseButton.visible && __instance.upperRightCloseButton.containsPoint(x, y)) + toSpeak = "Close menu button"; + else if (containsReward && __instance.rewardBox.containsPoint(x, y)) + toSpeak = "Left click to collect reward"; + } + + if (firstTimeInIndividualQuest || (questLogQuery != toSpeak)) + { + questLogQuery = toSpeak; + MainClass.ScreenReader.Say(extra + " \n\t" + toSpeak, true); + + if (firstTimeInIndividualQuest) firstTimeInIndividualQuest = false; + } + } + + internal static void Cleaup() + { + questLogQuery = ""; + isNarratingQuestInfo = false; + firstTimeInIndividualQuest = true; + } + } +} diff --git a/stardew-access/Patches/QuestPatches/SpecialOrdersBoardPatch.cs b/stardew-access/Patches/QuestPatches/SpecialOrdersBoardPatch.cs new file mode 100644 index 0000000..eb27f5a --- /dev/null +++ b/stardew-access/Patches/QuestPatches/SpecialOrdersBoardPatch.cs @@ -0,0 +1,84 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class SpecialOrdersBoardPatch + { + private static string specialOrdersBoardQueryKey = ""; + + internal static void DrawPatch(SpecialOrdersBoard __instance) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + if (__instance.acceptLeftQuestButton.visible && __instance.acceptLeftQuestButton.containsPoint(x, y)) + { + string toSpeak = getSpecialOrderDetails(__instance.leftOrder); + + toSpeak = $"Left Quest:\n\t{toSpeak}\n\tPress left click to accept this quest."; + + Speak(toSpeak); + return; + } + + if (__instance.acceptRightQuestButton.visible && __instance.acceptRightQuestButton.containsPoint(x, y)) + { + string toSpeak = getSpecialOrderDetails(__instance.rightOrder); + + toSpeak = $"Right Quest:\n\t{toSpeak}\n\tPress left click to accept this quest."; + + Speak(toSpeak); + return; + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static string getSpecialOrderDetails(SpecialOrder order) + { + int daysLeft = order.GetDaysLeft(); + string description = order.GetDescription(); + string objectiveDescription = ""; + string name = order.GetName(); + int moneyReward = order.GetMoneyReward(); + + // Get each objectives + for (int i = 0; i < order.GetObjectiveDescriptions().Count; i++) + { + objectiveDescription += order.GetObjectiveDescriptions()[i] + ", \n"; + } + + string toReturn = $"{name}\n\tDescription:{description}\n\tObjectives: {objectiveDescription}"; + + if (order.IsTimedQuest()) + { + toReturn = $"{toReturn}\n\tTime: {daysLeft} days"; + } + + if (order.HasMoneyReward()) + { + toReturn = $"{toReturn}\n\tReward: {moneyReward}g"; + } + + return toReturn; + } + + private static void Speak(string toSpeak) + { + if (specialOrdersBoardQueryKey == toSpeak) return; + + specialOrdersBoardQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + + internal static void Cleanup() + { + specialOrdersBoardQueryKey = ""; + } + } +} diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs deleted file mode 100644 index af8cba3..0000000 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ /dev/null @@ -1,238 +0,0 @@ -using StardewValley; -using StardewValley.Menus; -using static StardewValley.Menus.LoadGameMenu; - -namespace stardew_access.Patches -{ - internal class TitleMenuPatches - { - public static string advancedGameOptionsQueryKey = " "; - - internal static void AdvancedGameOptionsPatch(AdvancedGameOptions __instance) - { - try - { - int currentItemIndex = Math.Max(0, Math.Min(__instance.options.Count - 7, __instance.currentItemIndex)); - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); - - if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - { - string toSpeak = "OK Button"; - if (advancedGameOptionsQueryKey != toSpeak) - { - advancedGameOptionsQueryKey = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - - for (int i = 0; i < __instance.optionSlots.Count; i++) - { - if (__instance.optionSlots[i].bounds.Contains(x, y) && currentItemIndex + i < __instance.options.Count && __instance.options[currentItemIndex + i].bounds.Contains(x - __instance.optionSlots[i].bounds.X, y - __instance.optionSlots[i].bounds.Y)) - { - OptionsElement optionsElement = __instance.options[currentItemIndex + i]; - string toSpeak = optionsElement.label; - - if (optionsElement is OptionsButton) - toSpeak = $" {toSpeak} Button"; - else if (optionsElement is OptionsCheckbox) - toSpeak = (((OptionsCheckbox)optionsElement).isChecked ? "Enabled" : "Disabled") + $" {toSpeak} Checkbox"; - else if (optionsElement is OptionsDropDown) - toSpeak = $"{toSpeak} Dropdown, option {((OptionsDropDown)optionsElement).dropDownDisplayOptions[((OptionsDropDown)optionsElement).selectedOption]} selected"; - else if (optionsElement is OptionsSlider) - toSpeak = $"{((OptionsSlider)optionsElement).value}% {toSpeak} Slider"; - else if (optionsElement is OptionsPlusMinus) - toSpeak = $"{((OptionsPlusMinus)optionsElement).displayOptions[((OptionsPlusMinus)optionsElement).selected]} selected of {toSpeak}"; - else if (optionsElement is OptionsInputListener) - { - string buttons = ""; - ((OptionsInputListener)optionsElement).buttonNames.ForEach(name => { buttons += $", {name}"; }); - toSpeak = $"{toSpeak} is bound to {buttons}. Left click to change."; - } - else if (optionsElement is OptionsTextEntry) - { - toSpeak = $"Seed text box"; - } - else - { - if (toSpeak.Contains(":")) - toSpeak = toSpeak.Replace(":", ""); - - toSpeak = $"{toSpeak} Options:"; - } - - if (advancedGameOptionsQueryKey != toSpeak) - { - advancedGameOptionsQueryKey = toSpeak; - MainClass.ScreenReader.Say(toSpeak, true); - } - return; - } - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void CoopMenuPatch(CoopMenu __instance, CoopMenu.Tab ___currentTab) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); - string toSpeak = " "; - - #region Join/Host Button (Important! This should be checked before checking other buttons) - if (__instance.slotButtons[0].containsPoint(x, y)) - { - if (___currentTab == CoopMenu.Tab.JOIN_TAB) - toSpeak = "Join lan game"; - if (___currentTab == CoopMenu.Tab.HOST_TAB) - toSpeak = "Host new farm"; - } - #endregion - - #region Other Buttons - if (__instance.joinTab.containsPoint(x, y)) - { - toSpeak = "Join Tab Button"; - } - else if (__instance.hostTab.containsPoint(x, y)) - { - toSpeak = "Host Tab Button"; - } - else if (__instance.refreshButton.containsPoint(x, y)) - { - toSpeak = "Refresh Button"; - } - #endregion - - if (toSpeak != " ") - MainClass.ScreenReader.SayWithChecker(toSpeak, true); - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void TitleMenuPatch(TitleMenu __instance, bool ___isTransitioningButtons) - { - try - { - if (___isTransitioningButtons) - return; - - string toSpeak = ""; - - __instance.buttons.ForEach(component => - { - if (component.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) - { - string name = component.name; - string label = component.label; - toSpeak = $"{name} {label} Button"; - } - }); - - if (__instance.muteMusicButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) - { - toSpeak = "Mute Music Button"; - } - - if (__instance.aboutButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) - { - toSpeak = "About Button"; - } - - if (__instance.languageButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) - { - toSpeak = "Language Button"; - } - - if (__instance.windowedButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) - { - toSpeak = "Fullscreen: " + ((Game1.isFullscreen) ? "enabled" : "disabled"); - } - - if (TitleMenu.subMenu != null && __instance.backButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) - { - string text = "Back Button"; - MainClass.ScreenReader.SayWithChecker(text, true); - } - - // Fix for back button not working using keyboard - if (TitleMenu.subMenu is CharacterCustomization && ((CharacterCustomization)TitleMenu.subMenu).backButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) - { - // Perform Left Click - if (MainClass.Config.LeftClickMainKey.JustPressed()) - { - __instance.backButtonPressed(); - } - } - - if (TitleMenu.subMenu == null && toSpeak != "") - MainClass.ScreenReader.SayWithChecker(toSpeak, true); - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - - internal static void LoadGameMenuPatch(SaveFileSlot __instance, LoadGameMenu ___menu, int i) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); - if (___menu.slotButtons[i].containsPoint(x, y)) - { - if (__instance.Farmer != null) - { - #region Farms - if (___menu.deleteButtons.Count > 0 && ___menu.deleteButtons[i].containsPoint(x, y)) - { - MainClass.ScreenReader.SayWithChecker($"Delete {__instance.Farmer.farmName.Value} Farm", true); - return; - } - - if (___menu.deleteConfirmationScreen) - { - // Used diff. functions to narrate to prevent it from speaking the message again on selecting another button. - string message = "Really delete farm?"; - - MainClass.ScreenReader.SayWithChecker(message, true); - if (___menu.okDeleteButton.containsPoint(x, y)) - { - MainClass.ScreenReader.SayWithMenuChecker("Ok Button", false); - } - else if (___menu.cancelDeleteButton.containsPoint(x, y)) - { - MainClass.ScreenReader.SayWithMenuChecker("Cancel Button", false); - } - return; - } - - String farmerName = __instance.Farmer.displayName; - String farmName = __instance.Farmer.farmName.Value; - String money = __instance.Farmer.Money.ToString(); - String hoursPlayed = Utility.getHoursMinutesStringFromMilliseconds(__instance.Farmer.millisecondsPlayed); - string dateStringForSaveGame = ((!__instance.Farmer.dayOfMonthForSaveGame.HasValue || - !__instance.Farmer.seasonForSaveGame.HasValue || - !__instance.Farmer.yearForSaveGame.HasValue) ? __instance.Farmer.dateStringForSaveGame : Utility.getDateStringFor(__instance.Farmer.dayOfMonthForSaveGame.Value, __instance.Farmer.seasonForSaveGame.Value, __instance.Farmer.yearForSaveGame.Value)); - - string toSpeak = $"{farmName} Farm Selected, \t\n Farmer: {farmerName}, \t\nMoney: {money}, \t\nHours Played: {hoursPlayed}, \t\nDate: {dateStringForSaveGame}"; - - MainClass.ScreenReader.SayWithChecker(toSpeak, true); - #endregion - } - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - } -} diff --git a/stardew-access/Patches/TitleMenuPatches/AdvancedGameOptionsPatch.cs b/stardew-access/Patches/TitleMenuPatches/AdvancedGameOptionsPatch.cs new file mode 100644 index 0000000..606ab1c --- /dev/null +++ b/stardew-access/Patches/TitleMenuPatches/AdvancedGameOptionsPatch.cs @@ -0,0 +1,85 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class AdvancedGameOptionsPatch + { + public static string advancedGameOptionsQueryKey = " "; + + internal static void DrawPatch(AdvancedGameOptions __instance) + { + try + { + int currentItemIndex = Math.Max(0, Math.Min(__instance.options.Count - 7, __instance.currentItemIndex)); + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); + + if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + { + string toSpeak = "OK Button"; + if (advancedGameOptionsQueryKey != toSpeak) + { + advancedGameOptionsQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + return; + } + + for (int i = 0; i < __instance.optionSlots.Count; i++) + { + if (!__instance.optionSlots[i].bounds.Contains(x, y) + || currentItemIndex + i >= __instance.options.Count + || !__instance.options[currentItemIndex + i].bounds.Contains(x - __instance.optionSlots[i].bounds.X, y - __instance.optionSlots[i].bounds.Y)) + continue; + + OptionsElement optionsElement = __instance.options[currentItemIndex + i]; + string toSpeak = optionsElement.label; + + if (optionsElement is OptionsButton) + toSpeak = $" {toSpeak} Button"; + else if (optionsElement is OptionsCheckbox) + toSpeak = (((OptionsCheckbox)optionsElement).isChecked ? "Enabled" : "Disabled") + $" {toSpeak} Checkbox"; + else if (optionsElement is OptionsDropDown) + toSpeak = $"{toSpeak} Dropdown, option {((OptionsDropDown)optionsElement).dropDownDisplayOptions[((OptionsDropDown)optionsElement).selectedOption]} selected"; + else if (optionsElement is OptionsSlider) + toSpeak = $"{((OptionsSlider)optionsElement).value}% {toSpeak} Slider"; + else if (optionsElement is OptionsPlusMinus) + toSpeak = $"{((OptionsPlusMinus)optionsElement).displayOptions[((OptionsPlusMinus)optionsElement).selected]} selected of {toSpeak}"; + else if (optionsElement is OptionsInputListener) + { + string buttons = ""; + ((OptionsInputListener)optionsElement).buttonNames.ForEach(name => { buttons += $", {name}"; }); + toSpeak = $"{toSpeak} is bound to {buttons}. Left click to change."; + } + else if (optionsElement is OptionsTextEntry) + { + toSpeak = $"Seed text box"; + } + else + { + if (toSpeak.Contains(":")) + toSpeak = toSpeak.Replace(":", ""); + + toSpeak = $"{toSpeak} Options:"; + } + + if (advancedGameOptionsQueryKey != toSpeak) + { + advancedGameOptionsQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + return; + } + } + catch (Exception e) + { + MainClass.ErrorLog($"An error occured in advanced game menu patch:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static void Cleanup() + { + advancedGameOptionsQueryKey = ""; + } + } +} diff --git a/stardew-access/Patches/CharacterCustomizationMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches/CharacterCustomizationMenuPatches.cs similarity index 99% rename from stardew-access/Patches/CharacterCustomizationMenuPatches.cs rename to stardew-access/Patches/TitleMenuPatches/CharacterCustomizationMenuPatches.cs index fa3e13f..005456d 100644 --- a/stardew-access/Patches/CharacterCustomizationMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches/CharacterCustomizationMenuPatches.cs @@ -119,7 +119,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + MainClass.ErrorLog($"An error occured in character customization menu patch:\n{e.Message}\n{e.StackTrace}"); } } diff --git a/stardew-access/Patches/TitleMenuPatches/CoopMenuPatch.cs b/stardew-access/Patches/TitleMenuPatches/CoopMenuPatch.cs new file mode 100644 index 0000000..3f4eb6a --- /dev/null +++ b/stardew-access/Patches/TitleMenuPatches/CoopMenuPatch.cs @@ -0,0 +1,59 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class CoopMenuPatch + { + private static string coopMenuQueryKey = ""; + + internal static void DrawPatch(CoopMenu __instance, CoopMenu.Tab ___currentTab) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); + string toSpeak = ""; + + #region Join/Host Button (Important! This should be checked before checking other buttons) + if (__instance.slotButtons[0].containsPoint(x, y)) + { + if (___currentTab == CoopMenu.Tab.JOIN_TAB) + toSpeak = "Join lan game"; + if (___currentTab == CoopMenu.Tab.HOST_TAB) + toSpeak = "Host new farm"; + } + #endregion + + #region Other Buttons + if (__instance.joinTab.containsPoint(x, y)) + { + toSpeak = "Join Tab Button"; + } + else if (__instance.hostTab.containsPoint(x, y)) + { + toSpeak = "Host Tab Button"; + } + else if (__instance.refreshButton.containsPoint(x, y)) + { + toSpeak = "Refresh Button"; + } + #endregion + + if (coopMenuQueryKey != toSpeak) + { + coopMenuQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + } + catch (Exception e) + { + MainClass.ErrorLog($"An error occured in co-op menu patch:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static void Cleanup() + { + coopMenuQueryKey = ""; + } + } +} diff --git a/stardew-access/Patches/TitleMenuPatches/LoadGameMenuPatch.cs b/stardew-access/Patches/TitleMenuPatches/LoadGameMenuPatch.cs new file mode 100644 index 0000000..df16a22 --- /dev/null +++ b/stardew-access/Patches/TitleMenuPatches/LoadGameMenuPatch.cs @@ -0,0 +1,86 @@ +using StardewValley; +using StardewValley.Menus; +using static StardewValley.Menus.LoadGameMenu; + +namespace stardew_access.Patches +{ + internal class LoadGameMenuPatch + { + private static string loadGameMenuQueryKey = ""; + private static bool firstTimeInMenu = true; + + internal static void DrawPatch(SaveFileSlot __instance, LoadGameMenu ___menu, int i) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); + string toSpeak = ""; + + if (!___menu.slotButtons[i].containsPoint(x, y)) return; + if (__instance.Farmer == null) return; + + if (___menu.deleteButtons.Count > 0 && ___menu.deleteButtons[i].containsPoint(x, y)) + { + toSpeak = $"Delete {__instance.Farmer.farmName.Value} Farm"; + if (loadGameMenuQueryKey != toSpeak) + { + loadGameMenuQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + return; + } + + if (___menu.deleteConfirmationScreen) + { + if (firstTimeInMenu) + { + firstTimeInMenu = false; + toSpeak = "Really delete farm?"; + } + + if (___menu.okDeleteButton.containsPoint(x, y)) + { + toSpeak = $"{toSpeak} Ok button"; + } + else if (___menu.cancelDeleteButton.containsPoint(x, y)) + { + toSpeak = $"{toSpeak} Cancel button"; + } + + if (loadGameMenuQueryKey != toSpeak) + { + loadGameMenuQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + return; + } + + String farmerName = __instance.Farmer.displayName; + String farmName = __instance.Farmer.farmName.Value; + String money = __instance.Farmer.Money.ToString(); + String hoursPlayed = Utility.getHoursMinutesStringFromMilliseconds(__instance.Farmer.millisecondsPlayed); + string dateStringForSaveGame = ((!__instance.Farmer.dayOfMonthForSaveGame.HasValue || + !__instance.Farmer.seasonForSaveGame.HasValue || + !__instance.Farmer.yearForSaveGame.HasValue) ? __instance.Farmer.dateStringForSaveGame : Utility.getDateStringFor(__instance.Farmer.dayOfMonthForSaveGame.Value, __instance.Farmer.seasonForSaveGame.Value, __instance.Farmer.yearForSaveGame.Value)); + + toSpeak = $"{farmName} Farm Selected, \t\n Farmer: {farmerName}, \t\nMoney: {money}, \t\nHours Played: {hoursPlayed}, \t\nDate: {dateStringForSaveGame}"; + + if (loadGameMenuQueryKey != toSpeak) + { + loadGameMenuQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + } + catch (Exception e) + { + MainClass.ErrorLog($"An error occured in load game menu patch:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static void Cleanup() + { + loadGameMenuQueryKey = ""; + firstTimeInMenu = true; + } + } +} diff --git a/stardew-access/Patches/TitleMenuPatches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches/TitleMenuPatches.cs new file mode 100644 index 0000000..7dbf06f --- /dev/null +++ b/stardew-access/Patches/TitleMenuPatches/TitleMenuPatches.cs @@ -0,0 +1,81 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class TitleMenuPatch + { + private static string titleMenuQueryKey = ""; + + internal static void DrawPatch(TitleMenu __instance, bool ___isTransitioningButtons) + { + try + { + if (___isTransitioningButtons) + return; + + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + string toSpeak = ""; + + if (__instance.muteMusicButton.containsPoint(x, y)) + { + toSpeak = "Mute Music Button"; + } + else if (__instance.aboutButton.containsPoint(x, y)) + { + toSpeak = "About Button"; + } + else if (__instance.languageButton.containsPoint(x, y)) + { + toSpeak = "Language Button"; + } + else if (__instance.windowedButton.containsPoint(x, y)) + { + toSpeak = "Fullscreen: " + ((Game1.isFullscreen) ? "enabled" : "disabled"); + } + else if (TitleMenu.subMenu != null && __instance.backButton.containsPoint(x, y)) + { + string text = "Back Button"; + MainClass.ScreenReader.SayWithChecker(text, true); + } + else + { + __instance.buttons.ForEach(component => + { + if (!component.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) + return; + + string name = component.name; + string label = component.label; + toSpeak = $"{name} {label} Button"; + }); + } + + // Fix for back button not working using keyboard + if (TitleMenu.subMenu is CharacterCustomization && ((CharacterCustomization)TitleMenu.subMenu).backButton.containsPoint(x, y)) + { + // Perform Left Click + if (MainClass.Config.LeftClickMainKey.JustPressed()) + { + __instance.backButtonPressed(); + } + } + + if (TitleMenu.subMenu == null && titleMenuQueryKey!=toSpeak) + { + titleMenuQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + } + catch (Exception e) + { + MainClass.ErrorLog($"An error occured in title menu patch:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static void Cleanup() + { + titleMenuQueryKey = ""; + } + } +}