From 0de1cd419ba7512990c039d5667219e6b9ae5e4c Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 17 Mar 2022 13:28:46 +0530 Subject: [PATCH 001/232] Removed [ ] from charachter customization menu --- stardew-access/ModEntry.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 415dad3..cd89f7e 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -6,6 +6,7 @@ using HarmonyLib; using stardew_access.Patches; using stardew_access.ScreenReader; using Microsoft.Xna.Framework; +using StardewValley.Menus; namespace stardew_access { @@ -142,9 +143,10 @@ namespace stardew_access { bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift); bool isLeftControlPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl); + bool isCustomizingChrachter = Game1.activeClickableMenu is CharacterCustomization || (TitleMenu.subMenu != null && TitleMenu.subMenu is CharacterCustomization); // Perform Left Click - if (Equals(e.Button, SButton.OemOpenBrackets)) + if (!isCustomizingChrachter && Equals(e.Button, SButton.OemOpenBrackets)) // Excluding the character creation menu { Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); } @@ -154,7 +156,7 @@ namespace stardew_access } // Perform Right CLick - if (Equals(e.Button, SButton.OemCloseBrackets)) + if (!isCustomizingChrachter && Equals(e.Button, SButton.OemCloseBrackets)) // Excluding the character creation menu { Game1.activeClickableMenu.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true)); } From fcd2243cc544b6341e24d647193745e532ddae08 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 17 Mar 2022 20:04:20 +0530 Subject: [PATCH 002/232] Made reading floorings toggleable | Added names to tuples making it more readable --- stardew-access/CustomCommands.cs | 7 ++++ stardew-access/Features/Radar.cs | 26 +++++++-------- stardew-access/Features/ReadTile.cs | 52 ++++++++++++++--------------- stardew-access/ModEntry.cs | 1 + 4 files changed, 47 insertions(+), 39 deletions(-) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index e26cc67..d589ea2 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -27,6 +27,13 @@ namespace stardew_access MainClass.GetMonitor().Log("Snap Mouse is " + (MainClass.snapMouse ? "on" : "off"), LogLevel.Info); }); + helper.ConsoleCommands.Add("flooring", "Toggle flooring in read tile.", (string commmand, string[] args) => + { + MainClass.readFlooring = !MainClass.readFlooring; + + MainClass.GetMonitor().Log("Flooring is " + (MainClass.readFlooring ? "on" : "off"), LogLevel.Info); + }); + helper.ConsoleCommands.Add("radar", "Toggle radar feature.", (string commmand, string[] args) => { MainClass.radar = !MainClass.radar; diff --git a/stardew-access/Features/Radar.cs b/stardew-access/Features/Radar.cs index 3419927..ba9ebde 100644 --- a/stardew-access/Features/Radar.cs +++ b/stardew-access/Features/Radar.cs @@ -191,14 +191,14 @@ namespace stardew_access.Features public (bool, string?, string) CheckTile(Vector2 position) { - (string?, CATEGORY?) tileDetail = ReadTile.getNameWithCategoryAtTile(position); - if (tileDetail.Item1 == null) + (string? name, CATEGORY? category) tileDetail = ReadTile.getNameWithCategoryAtTile(position); + if (tileDetail.name == null) return (false, null, CATEGORY.Others.ToString()); - if (tileDetail.Item2 == null) - tileDetail.Item2 = CATEGORY.Others; + if (tileDetail.category == null) + tileDetail.category = CATEGORY.Others; - return (true, tileDetail.Item1, tileDetail.Item2.ToString()); + return (true, tileDetail.name, tileDetail.category.ToString()); } @@ -208,9 +208,9 @@ namespace stardew_access.Features { if (Game1.currentLocation.isObjectAtTile((int)position.X, (int)position.Y)) { - (string?, CATEGORY) objDetails = ReadTile.getObjectAtTile((int)position.X, (int)position.Y); - string? objectName = objDetails.Item1; - CATEGORY category = objDetails.Item2; + (string? name, CATEGORY category) objDetails = ReadTile.getObjectAtTile((int)position.X, (int)position.Y); + string? objectName = objDetails.name; + CATEGORY category = objDetails.category; StardewValley.Object obj = Game1.currentLocation.getObjectAtTile((int)position.X, (int)position.Y); if (objectName != null) @@ -232,13 +232,13 @@ namespace stardew_access.Features } else { - (string?, CATEGORY?) tileDetail = ReadTile.getNameWithCategoryAtTile(position); - if (tileDetail.Item1 != null) + (string? name, CATEGORY? category) tileDetail = ReadTile.getNameWithCategoryAtTile(position); + if (tileDetail.name != null) { - if (tileDetail.Item2 == null) - tileDetail.Item2 = CATEGORY.Others; + if (tileDetail.category == null) + tileDetail.category = CATEGORY.Others; - PlaySoundAt(position, tileDetail.Item1, tileDetail.Item2); + PlaySoundAt(position, tileDetail.name, tileDetail.category); } } } diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 0ab8422..7118d8c 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -72,18 +72,18 @@ namespace stardew_access.Features } ///Returns the name of the object at tile alongwith it's category's name - public static (string?, string?) getNameWithCategoryNameAtTile(Vector2 tile) + public static (string? name, string? categoryName) getNameWithCategoryNameAtTile(Vector2 tile) { - (string?, CATEGORY?) tileDetail = getNameWithCategoryAtTile(tile); + (string? name, CATEGORY? category) tileDetail = getNameWithCategoryAtTile(tile); - if (tileDetail.Item2 == null) - tileDetail.Item2 = CATEGORY.Others; + if (tileDetail.category == null) + tileDetail.category = CATEGORY.Others; - return (tileDetail.Item1, tileDetail.Item2.ToString()); + return (tileDetail.name, tileDetail.category.ToString()); } ///Returns the name of the object at tile alongwith it's category - public static (string?, CATEGORY?) getNameWithCategoryAtTile(Vector2 tile) + public static (string? name, CATEGORY? category) getNameWithCategoryAtTile(Vector2 tile) { int x = (int)tile.X; int y = (int)tile.Y; @@ -93,7 +93,7 @@ namespace stardew_access.Features bool isColliding = isCollidingAtTile(x, y); Dictionary> terrainFeature = Game1.currentLocation.terrainFeatures.FieldDict; string? door = getDoorAtTile(x, y); - (CATEGORY?, string?) tileInfo = getTileInfo(x, y); + (CATEGORY? category, string? name) tileInfo = getTileInfo(x, y); string? junimoBundle = getJunimoBundleAt(x, y); string? resourceClump = getResourceClumpAtTile(x, y); string? farmAnimal = getFarmAnimalAt(Game1.currentLocation, x, y); @@ -119,18 +119,18 @@ namespace stardew_access.Features } else if (Game1.currentLocation.isObjectAtTile(x, y)) { - (string?, CATEGORY?) obj = getObjectAtTile(x, y); - toReturn = obj.Item1; - category = obj.Item2; + (string? name, CATEGORY? category) obj = getObjectAtTile(x, y); + toReturn = obj.name; + category = obj.category; } else if (terrainFeature.ContainsKey(tile)) { - (string?, CATEGORY) tf = getTerrainFeatureAtTile(terrainFeature[tile]); - string? terrain = tf.Item1; + (string? name, CATEGORY category) tf = getTerrainFeatureAtTile(terrainFeature[tile]); + string? terrain = tf.name; if (terrain != null) { toReturn = terrain; - category = tf.Item2; + category = tf.category; } } @@ -164,10 +164,10 @@ namespace stardew_access.Features toReturn = "Elevator"; category = CATEGORY.Doors; } - else if (tileInfo.Item2 != null) + else if (tileInfo.name != null) { - toReturn = tileInfo.Item2; - category = tileInfo.Item1; + toReturn = tileInfo.name; + category = tileInfo.category; } else if (junimoBundle != null) { @@ -191,7 +191,7 @@ namespace stardew_access.Features bool isColliding = isCollidingAtTile(x, y); Dictionary> terrainFeature = Game1.currentLocation.terrainFeatures.FieldDict; string? door = getDoorAtTile(x, y); - (CATEGORY?, string?) tileInfo = getTileInfo(x, y); + (CATEGORY? category, string? name) tileInfo = getTileInfo(x, y); string? junimoBundle = getJunimoBundleAt(x, y); string? resourceClump = getResourceClumpAtTile(x, y); string? farmAnimal = getFarmAnimalAt(Game1.currentLocation, x, y); @@ -211,7 +211,7 @@ namespace stardew_access.Features } else if (Game1.currentLocation.isObjectAtTile(x, y)) { - toReturn = getObjectAtTile(x, y).Item1; + toReturn = getObjectAtTile(x, y).name; } else if (terrainFeature.ContainsKey(tile)) { @@ -243,9 +243,9 @@ namespace stardew_access.Features { toReturn = "Elevator"; } - else if (tileInfo.Item2 != null) + else if (tileInfo.name != null) { - toReturn = tileInfo.Item2; + toReturn = tileInfo.name; } else if (junimoBundle != null) { @@ -391,9 +391,9 @@ namespace stardew_access.Features /// /// /// - /// Item1: This is the category of the tile. Default to Furnitures. - ///
Item2: This is the name of the tile. Default to null if the tile tile has nothing on it.
- public static (CATEGORY?, string?) getTileInfo(int x, int y) + /// category: This is the category of the tile. Default to Furnitures. + ///
name: This is the name of the tile. Default to null if the tile tile has nothing on it.
+ public static (CATEGORY? category, string? name) getTileInfo(int x, int y) { int? index = null; @@ -449,7 +449,7 @@ namespace stardew_access.Features return (null, null); } - public static (string?, CATEGORY) getTerrainFeatureAtTile(Netcode.NetRef terrain) + public static (string? name, CATEGORY category) getTerrainFeatureAtTile(Netcode.NetRef terrain) { string? toReturn = null; CATEGORY category = CATEGORY.Others; @@ -518,7 +518,7 @@ namespace stardew_access.Features if (toReturn.Contains("feature")) toReturn.Replace("feature", ""); } - else if (terrain.Get() is Flooring) + else if (terrain.Get() is Flooring && MainClass.readFlooring) { category = CATEGORY.Flooring; Flooring flooring = (Flooring)terrain.Get(); @@ -639,7 +639,7 @@ namespace stardew_access.Features return seedName; } - public static (string?, CATEGORY) getObjectAtTile(int x, int y) + public static (string? name, CATEGORY category) getObjectAtTile(int x, int y) { string? toReturn = null; diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index cd89f7e..dde56a1 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -19,6 +19,7 @@ namespace stardew_access public static bool radar = false; public static bool radarDebug = false; public static bool radarStereoSound = true; + public static bool readFlooring = false; private static IMonitor monitor; public static string hudMessageQueryKey = ""; private static Radar radarFeature; From 674cd803986b3ca402e976fb2257daee570fff54 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 17 Mar 2022 20:52:50 +0530 Subject: [PATCH 003/232] Added machine status --- stardew-access/Features/ReadTile.cs | 62 ++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 7118d8c..cf77810 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -8,6 +8,10 @@ using StardewValley.TerrainFeatures; namespace stardew_access.Features { + public enum MachineState + { + Ready, Busy, Waiting + } public class ReadTile { public static bool isReadingTile = false; @@ -641,13 +645,55 @@ namespace stardew_access.Features public static (string? name, CATEGORY category) getObjectAtTile(int x, int y) { - string? toReturn = null; + (string? name, CATEGORY category) toReturn = (null, CATEGORY.Others); StardewValley.Object obj = Game1.currentLocation.getObjectAtTile(x, y); int index = obj.ParentSheetIndex; - toReturn = obj.DisplayName; + toReturn.name = obj.DisplayName; // Get object names based on index + (string? name, CATEGORY category) correctNameAndCategory = getCorrectNameAndCategoryFromIndex(index); + if (correctNameAndCategory.name != null) + toReturn = correctNameAndCategory; + + if (obj is Chest) + { + Chest chest = (Chest)obj; + toReturn = (chest.DisplayName, CATEGORY.Chests); + } + + if (obj is Furniture) + toReturn.category = CATEGORY.Furnitures; + + MachineState machineState = GetMachineState(obj); + if (machineState == MachineState.Ready) + toReturn.name = $"Harvestable {toReturn.name}"; + else if (machineState == MachineState.Busy) + toReturn.name = $"Busy {toReturn.name}"; + + return toReturn; + } + + private static MachineState GetMachineState(StardewValley.Object machine) + { + if (machine is CrabPot crabPot) + if (crabPot.bait.Value is not null && crabPot.heldObject.Value is null) + return MachineState.Busy; + return GetMachineState(machine.readyForHarvest.Value, machine.MinutesUntilReady, machine.heldObject); + } + + private static MachineState GetMachineState(bool readyForHarvest, int minutesUntilReady, StardewValley.Object? heldObject) + { + if (readyForHarvest || (heldObject is not null && minutesUntilReady <= 0)) + return MachineState.Ready; + else if (minutesUntilReady > 0) + return MachineState.Busy; + else + return MachineState.Waiting; + } + + private static (string? name, CATEGORY category) getCorrectNameAndCategoryFromIndex(int index) + { switch (index) { case 313: @@ -767,18 +813,6 @@ namespace stardew_access.Features } } - if (obj is Chest) - { - Chest chest = (Chest)obj; - return (chest.DisplayName, CATEGORY.Chests); - } - - if (obj is Furniture) - return (toReturn, CATEGORY.Furnitures); - - if (toReturn != null) - return (toReturn, CATEGORY.Others); - return (null, CATEGORY.Others); } From 2d3d79be0404bddb529f38b0291087c9adf2ae22 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 19 Mar 2022 12:54:53 +0530 Subject: [PATCH 004/232] Added alt + j keybind --- stardew-access/CustomCommands.cs | 108 +++++++++--------- stardew-access/CustomSoundEffects.cs | 2 +- stardew-access/Features/CurrentPlayer.cs | 14 ++- stardew-access/Features/Other.cs | 2 +- stardew-access/Features/Radar.cs | 8 +- stardew-access/Features/ReadTile.cs | 26 +++-- stardew-access/ModEntry.cs | 31 +++-- .../Patches/BuildingNAnimalMenuPatches.cs | 4 +- stardew-access/Patches/ChatManuPatches.cs | 2 +- stardew-access/Patches/DialoguePatches.cs | 6 +- stardew-access/Patches/GameMenuPatches.cs | 22 ++-- stardew-access/Patches/MenuPatches.cs | 20 ++-- stardew-access/Patches/QuestPatches.cs | 6 +- stardew-access/Patches/TitleMenuPatches.cs | 8 +- stardew-access/manifest.json | 2 +- 15 files changed, 148 insertions(+), 113 deletions(-) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index d589ea2..db08520 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -17,28 +17,28 @@ namespace stardew_access { MainClass.readTile = !MainClass.readTile; - MainClass.GetMonitor().Log("Read Tile is " + (MainClass.readTile ? "on" : "off"), LogLevel.Info); + MainClass.DebugLog("Read Tile is " + (MainClass.readTile ? "on" : "off")); }); helper.ConsoleCommands.Add("snapmouse", "Toggle snap mouse feature.", (string commmand, string[] args) => { MainClass.snapMouse = !MainClass.snapMouse; - MainClass.GetMonitor().Log("Snap Mouse is " + (MainClass.snapMouse ? "on" : "off"), LogLevel.Info); + MainClass.DebugLog("Snap Mouse is " + (MainClass.snapMouse ? "on" : "off")); }); helper.ConsoleCommands.Add("flooring", "Toggle flooring in read tile.", (string commmand, string[] args) => { MainClass.readFlooring = !MainClass.readFlooring; - MainClass.GetMonitor().Log("Flooring is " + (MainClass.readFlooring ? "on" : "off"), LogLevel.Info); + MainClass.DebugLog("Flooring is " + (MainClass.readFlooring ? "on" : "off")); }); helper.ConsoleCommands.Add("radar", "Toggle radar feature.", (string commmand, string[] args) => { MainClass.radar = !MainClass.radar; - MainClass.GetMonitor().Log("Radar " + (MainClass.radar ? "on" : "off"), LogLevel.Info); + MainClass.DebugLog("Radar " + (MainClass.radar ? "on" : "off")); }); #region Radar Feature @@ -46,21 +46,21 @@ namespace stardew_access { MainClass.radarDebug = !MainClass.radarDebug; - MainClass.GetMonitor().Log("Radar debugging " + (MainClass.radarDebug ? "on" : "off"), LogLevel.Info); + MainClass.DebugLog("Radar debugging " + (MainClass.radarDebug ? "on" : "off")); }); helper.ConsoleCommands.Add("rstereo", "Toggle stereo sound in radar feature.", (string commmand, string[] args) => { MainClass.radarStereoSound = !MainClass.radarStereoSound; - MainClass.GetMonitor().Log("Stereo sound is " + (MainClass.radarStereoSound ? "on" : "off"), LogLevel.Info); + MainClass.DebugLog("Stereo sound is " + (MainClass.radarStereoSound ? "on" : "off")); }); helper.ConsoleCommands.Add("rfocus", "Toggle focus mode in radar feature.", (string commmand, string[] args) => { bool focus = MainClass.RadarFeature.ToggleFocus(); - MainClass.GetMonitor().Log("Focus mode is " + (focus ? "on" : "off"), LogLevel.Info); + MainClass.DebugLog("Focus mode is " + (focus ? "on" : "off")); }); helper.ConsoleCommands.Add("rdelay", "Set the delay of radar feature in milliseconds.", (string commmand, string[] args) => @@ -79,19 +79,19 @@ namespace stardew_access { MainClass.RadarFeature.delay = delay; if (delay >= 1000) - MainClass.GetMonitor().Log($"Delay set to {MainClass.RadarFeature.delay} milliseconds.", LogLevel.Info); + MainClass.DebugLog($"Delay set to {MainClass.RadarFeature.delay} milliseconds."); else - MainClass.GetMonitor().Log($"Delay should be atleast 1 second or 1000 millisecond long.", LogLevel.Info); + MainClass.DebugLog($"Delay should be atleast 1 second or 1000 millisecond long."); } else { - MainClass.GetMonitor().Log("Invalid delay amount, it can only be in numeric form.", LogLevel.Info); + MainClass.DebugLog("Invalid delay amount, it can only be in numeric form."); } } else { - MainClass.GetMonitor().Log("Enter the delay amount (in milliseconds)!", LogLevel.Info); + MainClass.DebugLog("Enter the delay amount (in milliseconds)!"); } }); @@ -112,19 +112,19 @@ namespace stardew_access { MainClass.RadarFeature.range = range; if (range >= 2 && range <= 10) - MainClass.GetMonitor().Log($"Range set to {MainClass.RadarFeature.range}.", LogLevel.Info); + MainClass.DebugLog($"Range set to {MainClass.RadarFeature.range}."); else - MainClass.GetMonitor().Log($"Range should be atleast 2 and maximum 10.", LogLevel.Info); + MainClass.DebugLog($"Range should be atleast 2 and maximum 10."); } else { - MainClass.GetMonitor().Log("Invalid range amount, it can only be in numeric form.", LogLevel.Info); + MainClass.DebugLog("Invalid range amount, it can only be in numeric form."); } } else { - MainClass.GetMonitor().Log("Enter the range amount!", LogLevel.Info); + MainClass.DebugLog("Enter the range amount!"); } }); @@ -143,16 +143,16 @@ namespace stardew_access if (!MainClass.RadarFeature.exclusions.Contains(keyToAdd)) { MainClass.RadarFeature.exclusions.Add(keyToAdd); - MainClass.GetMonitor().Log($"Added {keyToAdd} key to exclusions list.", LogLevel.Info); + MainClass.DebugLog($"Added {keyToAdd} key to exclusions list."); } else { - MainClass.GetMonitor().Log($"{keyToAdd} key already present in the list.", LogLevel.Info); + MainClass.DebugLog($"{keyToAdd} key already present in the list."); } } else { - MainClass.GetMonitor().Log("Unable to add the key to exclusions list.", LogLevel.Info); + MainClass.DebugLog("Unable to add the key to exclusions list."); } }); @@ -168,16 +168,16 @@ namespace stardew_access if (MainClass.RadarFeature.exclusions.Contains(keyToAdd)) { MainClass.RadarFeature.exclusions.Remove(keyToAdd); - MainClass.GetMonitor().Log($"Removed {keyToAdd} key from exclusions list.", LogLevel.Info); + MainClass.DebugLog($"Removed {keyToAdd} key from exclusions list."); } else { - MainClass.GetMonitor().Log($"Cannot find {keyToAdd} key in exclusions list.", LogLevel.Info); + MainClass.DebugLog($"Cannot find {keyToAdd} key in exclusions list."); } } else { - MainClass.GetMonitor().Log("Unable to remove the key from exclusions list.", LogLevel.Info); + MainClass.DebugLog("Unable to remove the key from exclusions list."); } }); @@ -190,23 +190,23 @@ namespace stardew_access { toPrint = $"{toPrint}\t{i + 1}: {MainClass.RadarFeature.exclusions[i]}"; } - MainClass.GetMonitor().Log(toPrint, LogLevel.Info); + MainClass.DebugLog(toPrint); } else { - MainClass.GetMonitor().Log("No exclusions found.", LogLevel.Info); + MainClass.DebugLog("No exclusions found."); } }); helper.ConsoleCommands.Add("reclear", "Clear the focus exclusions in the radar featrure.", (string commmand, string[] args) => { MainClass.RadarFeature.exclusions.Clear(); - MainClass.GetMonitor().Log($"Cleared the focus list in the exclusions feature.", LogLevel.Info); + MainClass.DebugLog($"Cleared the focus list in the exclusions feature."); }); helper.ConsoleCommands.Add("recount", "Number of exclusions in the radar feature.", (string commmand, string[] args) => { - MainClass.GetMonitor().Log($"There are {MainClass.RadarFeature.exclusions.Count} exclusiond in the radar feature.", LogLevel.Info); + MainClass.DebugLog($"There are {MainClass.RadarFeature.exclusions.Count} exclusiond in the radar feature."); }); #endregion @@ -223,16 +223,16 @@ namespace stardew_access if (!MainClass.RadarFeature.focus.Contains(keyToAdd)) { MainClass.RadarFeature.focus.Add(keyToAdd); - MainClass.GetMonitor().Log($"Added {keyToAdd} key to focus list.", LogLevel.Info); + MainClass.DebugLog($"Added {keyToAdd} key to focus list."); } else { - MainClass.GetMonitor().Log($"{keyToAdd} key already present in the list.", LogLevel.Info); + MainClass.DebugLog($"{keyToAdd} key already present in the list."); } } else { - MainClass.GetMonitor().Log("Unable to add the key to focus list.", LogLevel.Info); + MainClass.DebugLog("Unable to add the key to focus list."); } }); @@ -248,16 +248,16 @@ namespace stardew_access if (MainClass.RadarFeature.focus.Contains(keyToAdd)) { MainClass.RadarFeature.focus.Remove(keyToAdd); - MainClass.GetMonitor().Log($"Removed {keyToAdd} key from focus list.", LogLevel.Info); + MainClass.DebugLog($"Removed {keyToAdd} key from focus list."); } else { - MainClass.GetMonitor().Log($"Cannot find {keyToAdd} key in focus list.", LogLevel.Info); + MainClass.DebugLog($"Cannot find {keyToAdd} key in focus list."); } } else { - MainClass.GetMonitor().Log("Unable to remove the key from focus list.", LogLevel.Info); + MainClass.DebugLog("Unable to remove the key from focus list."); } }); @@ -270,23 +270,23 @@ namespace stardew_access { toPrint = $"{toPrint}\t{i + 1}): {MainClass.RadarFeature.focus[i]}"; } - MainClass.GetMonitor().Log(toPrint, LogLevel.Info); + MainClass.DebugLog(toPrint); } else { - MainClass.GetMonitor().Log("No objects found in the focus list.", LogLevel.Info); + MainClass.DebugLog("No objects found in the focus list."); } }); helper.ConsoleCommands.Add("rfclear", "Clear the focus list in the radar featrure.", (string commmand, string[] args) => { MainClass.RadarFeature.focus.Clear(); - MainClass.GetMonitor().Log($"Cleared the focus list in the radar feature.", LogLevel.Info); + MainClass.DebugLog($"Cleared the focus list in the radar feature."); }); helper.ConsoleCommands.Add("rfcount", "Number of list in the radar feature.", (string commmand, string[] args) => { - MainClass.GetMonitor().Log($"There are {MainClass.RadarFeature.focus.Count} objects in the focus list in the radar feature.", LogLevel.Info); + MainClass.DebugLog($"There are {MainClass.RadarFeature.focus.Count} objects in the focus list in the radar feature."); }); #endregion @@ -297,14 +297,14 @@ namespace stardew_access { if (Game1.currentLocation is not Farm) { - MainClass.GetMonitor().Log("Can only use this command in the farm", LogLevel.Info); + MainClass.DebugLog("Can only use this command in the farm"); return; } string? indexInString = args.ElementAtOrDefault(0); if (indexInString == null) { - MainClass.GetMonitor().Log("Enter the index too!", LogLevel.Info); + MainClass.DebugLog("Enter the index too!"); return; } @@ -313,12 +313,12 @@ namespace stardew_access if (!isParsable || !(index >= 0 && index <= 9)) { - MainClass.GetMonitor().Log("Index can only be a number and from 0 to 9 only", LogLevel.Info); + MainClass.DebugLog("Index can only be a number and from 0 to 9 only"); return; } BuildingNAnimalMenuPatches.marked[index] = new Vector2((int)Game1.player.getTileX(), (int)Game1.player.getTileY()); - MainClass.GetMonitor().Log($"Location {(int)Game1.player.getTileX()}x {(int)Game1.player.getTileY()}y added at {index} index.", LogLevel.Info); + MainClass.DebugLog($"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) => @@ -333,16 +333,16 @@ namespace stardew_access } if (toPrint == "") - MainClass.GetMonitor().Log("No positions marked!", LogLevel.Info); + MainClass.DebugLog("No positions marked!"); else - MainClass.GetMonitor().Log($"Marked positions:{toPrint}\nOpen command menu and use pageup and pagedown to check the list", LogLevel.Info); + MainClass.DebugLog($"Marked positions:{toPrint}\nOpen command menu and use pageup and pagedown to check the list"); }); helper.ConsoleCommands.Add("buildlist", "List all buildings for selection for upgrading/demolishing/painting", (string commmand, string[] args) => { if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu) || !BuildingNAnimalMenuPatches.isOnFarm) { - MainClass.GetMonitor().Log($"Cannot list buildings.", LogLevel.Info); + MainClass.DebugLog($"Cannot list buildings."); return; } @@ -363,11 +363,11 @@ namespace stardew_access if (toPrint == "") { - MainClass.GetMonitor().Log("No appropriate buildings to list", LogLevel.Info); + MainClass.DebugLog("No appropriate buildings to list"); } else { - MainClass.GetMonitor().Log($"Available buildings:{toPrint}\nOpen command menu and use pageup and pagedown to check the list", LogLevel.Info); + MainClass.DebugLog($"Available buildings:{toPrint}\nOpen command menu and use pageup and pagedown to check the list"); } }); @@ -375,14 +375,14 @@ namespace stardew_access { if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu) || !BuildingNAnimalMenuPatches.isOnFarm) { - MainClass.GetMonitor().Log($"Cannot select building.", LogLevel.Info); + MainClass.DebugLog($"Cannot select building."); return; } string? indexInString = args.ElementAtOrDefault(0); if (indexInString == null) { - MainClass.GetMonitor().Log("Enter the index of the building too! Use buildlist", LogLevel.Info); + MainClass.DebugLog("Enter the index of the building too! Use buildlist"); return; } @@ -391,7 +391,7 @@ namespace stardew_access if (!isParsable) { - MainClass.GetMonitor().Log("Index can only be a number.", LogLevel.Info); + MainClass.DebugLog("Index can only be a number."); return; } @@ -405,13 +405,13 @@ namespace stardew_access { if (BuildingNAnimalMenuPatches.availableBuildings[index] == null) { - MainClass.GetMonitor().Log($"No building found with index {index}. Use buildlist.", LogLevel.Info); + MainClass.DebugLog($"No building found with index {index}. Use buildlist."); return; } if (positionIndexInString == null) { - MainClass.GetMonitor().Log("Enter the index of marked place too! Use marklist.", LogLevel.Info); + MainClass.DebugLog("Enter the index of marked place too! Use marklist."); return; } @@ -419,7 +419,7 @@ namespace stardew_access if (!isParsable) { - MainClass.GetMonitor().Log("Index can only be a number.", LogLevel.Info); + MainClass.DebugLog("Index can only be a number."); return; } } @@ -428,7 +428,7 @@ namespace stardew_access { if (BuildingNAnimalMenuPatches.marked[index] == Vector2.Zero) { - MainClass.GetMonitor().Log($"No marked position found at {index} index.", LogLevel.Info); + MainClass.DebugLog($"No marked position found at {index} index."); return; } } @@ -436,7 +436,7 @@ namespace stardew_access { if (BuildingNAnimalMenuPatches.availableBuildings[index] == null) { - MainClass.GetMonitor().Log($"No building found with index {index}. Use buildlist.", LogLevel.Info); + MainClass.DebugLog($"No building found with index {index}. Use buildlist."); return; } } @@ -455,7 +455,7 @@ namespace stardew_access if (response != null) { - MainClass.GetMonitor().Log(response, LogLevel.Info); + MainClass.DebugLog(response); } }); #endregion @@ -464,7 +464,7 @@ namespace stardew_access { MainClass.GetScreenReader().InitializeScreenReader(); - MainClass.GetMonitor().Log("Screen Reader refreshed!", LogLevel.Info); + MainClass.DebugLog("Screen Reader refreshed!"); }); } } diff --git a/stardew-access/CustomSoundEffects.cs b/stardew-access/CustomSoundEffects.cs index 8ad60e9..de23067 100644 --- a/stardew-access/CustomSoundEffects.cs +++ b/stardew-access/CustomSoundEffects.cs @@ -71,7 +71,7 @@ namespace stardew_access } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to initialize custom sounds:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to initialize custom sounds:\n{e.Message}\n{e.StackTrace}"); } } } diff --git a/stardew-access/Features/CurrentPlayer.cs b/stardew-access/Features/CurrentPlayer.cs index 1bc15cd..7ea330e 100644 --- a/stardew-access/Features/CurrentPlayer.cs +++ b/stardew-access/Features/CurrentPlayer.cs @@ -31,13 +31,20 @@ namespace stardew_access.Features return staminaPercentage; } + public static Vector2 getPosition() + { + if (Game1.player == null) + return Vector2.Zero; + + return Game1.player.getTileLocation(); + } + public static int getPositionX() { if (Game1.player == null) return 0; - int x = (int)Game1.player.getTileLocation().X; - return x; + return (int)getPosition().X; } public static int getPositionY() @@ -45,8 +52,7 @@ namespace stardew_access.Features if (Game1.player == null) return 0; - int y = (int)Game1.player.getTileLocation().Y; - return y; + return (int)getPosition().Y; } public static string getTimeOfDay() diff --git a/stardew-access/Features/Other.cs b/stardew-access/Features/Other.cs index a9d078e..a518787 100644 --- a/stardew-access/Features/Other.cs +++ b/stardew-access/Features/Other.cs @@ -96,7 +96,7 @@ namespace stardew_access.Features } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate hud messages:\n{e.Message}\n{e.StackTrace}", StardewModdingAPI.LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate hud messages:\n{e.Message}\n{e.StackTrace}"); } await Task.Delay(300); diff --git a/stardew-access/Features/Radar.cs b/stardew-access/Features/Radar.cs index ba9ebde..4fcba88 100644 --- a/stardew-access/Features/Radar.cs +++ b/stardew-access/Features/Radar.cs @@ -99,7 +99,7 @@ namespace stardew_access.Features public async void Run() { if (MainClass.radarDebug) - MainClass.GetMonitor().Log($"\n\nRead Tile started", StardewModdingAPI.LogLevel.Debug); + MainClass.DebugLog($"\n\nRead Tile started"); isRunning = true; Vector2 currPosition = Game1.player.getTileLocation(); @@ -111,7 +111,7 @@ namespace stardew_access.Features SearchNearbyTiles(currPosition, range); if (MainClass.radarDebug) - MainClass.GetMonitor().Log($"\nRead Tile stopped\n\n", StardewModdingAPI.LogLevel.Debug); + MainClass.DebugLog($"\nRead Tile stopped\n\n"); await Task.Delay(delay); isRunning = false; @@ -244,7 +244,7 @@ namespace stardew_access.Features } catch (Exception e) { - MainClass.GetMonitor().Log($"{e.Message}\n{e.StackTrace}\n{e.Source}", StardewModdingAPI.LogLevel.Error); + MainClass.ErrorLog($"{e.Message}\n{e.StackTrace}\n{e.Source}"); } } @@ -296,7 +296,7 @@ namespace stardew_access.Features #endregion if (MainClass.radarDebug) - MainClass.GetMonitor().Log($"{radarFocus}\tObject:{searchQuery.ToLower().Trim()}\tPosition: X={position.X} Y={position.Y}", StardewModdingAPI.LogLevel.Debug); + MainClass.ErrorLog($"{radarFocus}\tObject:{searchQuery.ToLower().Trim()}\tPosition: X={position.X} Y={position.Y}"); int px = (int)Game1.player.getTileX(); // Player's X postion int py = (int)Game1.player.getTileY(); // Player's Y postion diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index cf77810..85b4498 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -22,17 +22,28 @@ namespace stardew_access.Features isReadingTile = false; } - public static async void run(bool manuallyTriggered = false) + public static async void run(bool manuallyTriggered = false, bool playersPosition = false) { isReadingTile = true; try { - #region Get Next Grab Tile - Vector2 tile = CurrentPlayer.getNextTile(); - int x = (int)tile.X; - int y = (int)tile.Y; + Vector2 tile; + int x, y; + #region Get Tile + if (!playersPosition) + { + // Grab tile + tile = CurrentPlayer.getNextTile(); + } + else + { + // Player's standing tile + tile = CurrentPlayer.getPosition(); + } #endregion + x = (int)tile.X; + y = (int)tile.Y; if (Context.IsPlayerFree) { @@ -62,13 +73,14 @@ namespace stardew_access.Features } #endregion - prevTile = tile; + if (!manuallyTriggered) + prevTile = tile; } } catch (Exception e) { - MainClass.GetMonitor().Log($"Error in Read Tile:\n{e.Message}\n{e.StackTrace}", LogLevel.Debug); + MainClass.ErrorLog($"Error in Read Tile:\n{e.Message}\n{e.StackTrace}"); } await Task.Delay(100); diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index dde56a1..407b417 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -41,11 +41,6 @@ namespace stardew_access screenReader = value; } - public static IMonitor GetMonitor() - { - return monitor; - } - public static void SetMonitor(IMonitor value) { monitor = value; @@ -140,6 +135,8 @@ namespace stardew_access if (e == null) return; + bool isLeftAltPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftAlt); + if (Game1.activeClickableMenu != null) { bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift); @@ -198,10 +195,20 @@ namespace stardew_access MainClass.GetScreenReader().Say(toSpeak, true); } - // Manual read tile - if (Equals(e.Button, SButton.J)) + // Manual read tile at looking tile + if (Equals(e.Button, SButton.J) && !isLeftAltPressed) { + readTile = false; ReadTile.run(manuallyTriggered: true); + Task.Delay(1000).ContinueWith(t => { readTile = true; }); + } + + // Manual read tile at player's position + if (Equals(e.Button, SButton.J) && isLeftAltPressed) + { + readTile = false; + ReadTile.run(manuallyTriggered: true, playersPosition: true); + Task.Delay(1000).ContinueWith(t => { readTile = true; }); } /*if (Equals(e.Button, SButton.B)) @@ -210,5 +217,15 @@ namespace stardew_access monitor.Log($"{Game1.player.controller.pathToEndPoint==null}", LogLevel.Debug); // true if path not found }*/ } + + public static void ErrorLog(string message) + { + monitor.Log(message, LogLevel.Error); + } + + public static void DebugLog(string message) + { + monitor.Log(message, LogLevel.Debug); + } } } \ No newline at end of file diff --git a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs index 7d346a4..6aaef02 100644 --- a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs +++ b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs @@ -98,7 +98,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -269,7 +269,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } diff --git a/stardew-access/Patches/ChatManuPatches.cs b/stardew-access/Patches/ChatManuPatches.cs index 66b7b0a..e30427e 100644 --- a/stardew-access/Patches/ChatManuPatches.cs +++ b/stardew-access/Patches/ChatManuPatches.cs @@ -48,7 +48,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index 669171c..8eaf766 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -105,7 +105,7 @@ namespace stardew_access.Patches else toSpeak = response; - MainClass.GetMonitor().Log(toSpeak, LogLevel.Debug); + MainClass.ErrorLog(toSpeak); MainClass.GetScreenReader().Say(toSpeak, true); } } @@ -130,7 +130,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate dialog:\n{e.StackTrace}\n{e.Message}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate dialog:\n{e.StackTrace}\n{e.Message}"); } } @@ -315,7 +315,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate dialog:\n{e.StackTrace}\n{e.Message}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate dialog:\n{e.StackTrace}\n{e.Message}"); } } } diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index 08ca10a..f815451 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -129,7 +129,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -289,7 +289,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } await Task.Delay(200); @@ -440,7 +440,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -548,7 +548,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -578,7 +578,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -662,7 +662,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -853,7 +853,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -1099,7 +1099,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -1317,7 +1317,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -1370,7 +1370,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -1405,7 +1405,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 0a009d8..94ad707 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -38,7 +38,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } return true; @@ -73,7 +73,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -93,7 +93,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -109,7 +109,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -131,7 +131,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -232,7 +232,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -270,7 +270,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -331,7 +331,7 @@ namespace stardew_access.Patches catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -373,7 +373,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -424,7 +424,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + 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 index 9d958fc..3acb6a4 100644 --- a/stardew-access/Patches/QuestPatches.cs +++ b/stardew-access/Patches/QuestPatches.cs @@ -39,7 +39,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -142,7 +142,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } #endregion @@ -238,7 +238,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } #endregion diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index f9d5b4a..4d46d16 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -49,7 +49,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -103,7 +103,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -157,7 +157,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -179,7 +179,7 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error); + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index 95dc2b5..4196f38 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.1.0", + "Version": "1.1.1", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From 5e65ca8dbde4d9164cf3b8d23a95f6a826500091 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 19 Mar 2022 13:12:45 +0530 Subject: [PATCH 005/232] Added the Missing Bundle --- stardew-access/Features/ReadTile.cs | 38 ++++++++++++++++++----------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 85b4498..caee167 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -326,23 +326,31 @@ namespace stardew_access.Features public static string? getJunimoBundleAt(int x, int y) { - if (Game1.currentLocation is not CommunityCenter) + if (Game1.currentLocation is not CommunityCenter communityCenter || Game1.currentLocation is not AbandonedJojaMart abandonedJojaMart) return null; - CommunityCenter communityCenter = ((CommunityCenter)Game1.currentLocation); + string? name = null; + if (communityCenter != null) + name = (x, y) switch + { + (14, 5) => "Pantry", + (14, 23) => "Crafts Room", + (40, 10) => "Fish Tank", + (63, 14) => "Boiler Room", + (55, 6) => "Vault", + (46, 11) => "Bulletin Board", + _ => null, + }; + else if (abandonedJojaMart != null) + name = (x, y) switch + { + (8, 8) => "Missing", + _ => null, + }; - string? name = (x, y) switch - { - (14, 5) => "Pantry", - (14, 23) => "Crafts Room", - (40, 10) => "Fish Tank", - (63, 14) => "Boiler Room", - (55, 6) => "Vault", - (46, 11) => "Bulletin Board", - _ => null, - }; - - if (name != null && communityCenter.shouldNoteAppearInArea(CommunityCenter.getAreaNumberFromName(name))) + if (name != null && communityCenter != null && communityCenter.shouldNoteAppearInArea(CommunityCenter.getAreaNumberFromName(name))) + return $"{name} bundle"; + else if (name != null && abandonedJojaMart != null) return $"{name} bundle"; else return null; @@ -655,6 +663,7 @@ namespace stardew_access.Features return seedName; } + #region Objects public static (string? name, CATEGORY category) getObjectAtTile(int x, int y) { (string? name, CATEGORY category) toReturn = (null, CATEGORY.Others); @@ -827,6 +836,7 @@ namespace stardew_access.Features return (null, CATEGORY.Others); } + #endregion public static bool isMineDownLadderAtTile(int x, int y) { From 18a671d97ad9a860b754cf2f1112495697f03739 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 19 Mar 2022 13:57:55 +0530 Subject: [PATCH 006/232] Added the Missing Bundle --- stardew-access/Features/ReadTile.cs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index caee167..742a70e 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -326,11 +326,9 @@ namespace stardew_access.Features public static string? getJunimoBundleAt(int x, int y) { - if (Game1.currentLocation is not CommunityCenter communityCenter || Game1.currentLocation is not AbandonedJojaMart abandonedJojaMart) - return null; - string? name = null; - if (communityCenter != null) + if (Game1.currentLocation is CommunityCenter communityCenter) + { name = (x, y) switch { (14, 5) => "Pantry", @@ -341,19 +339,22 @@ namespace stardew_access.Features (46, 11) => "Bulletin Board", _ => null, }; - else if (abandonedJojaMart != null) + if (name != null && communityCenter.shouldNoteAppearInArea(CommunityCenter.getAreaNumberFromName(name))) + return $"{name} bundle"; + } + else if (Game1.currentLocation is not AbandonedJojaMart) + { name = (x, y) switch { (8, 8) => "Missing", _ => null, }; - if (name != null && communityCenter != null && communityCenter.shouldNoteAppearInArea(CommunityCenter.getAreaNumberFromName(name))) - return $"{name} bundle"; - else if (name != null && abandonedJojaMart != null) - return $"{name} bundle"; - else - return null; + if (name != null) + return $"{name} bundle"; + } + + return null; } public static bool isCollidingAtTile(int x, int y) From 99961d8e6279b6b5a7a6f91f1420886b99733aa8 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 19 Mar 2022 14:03:21 +0530 Subject: [PATCH 007/232] Added alt + k to narrate current location name --- stardew-access/ModEntry.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 407b417..bda0a42 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -175,12 +175,19 @@ namespace stardew_access } // Narrate Position - if (Equals(e.Button, SButton.K)) + if (Equals(e.Button, SButton.K) && !isLeftAltPressed) { string toSpeak = $"X: {CurrentPlayer.getPositionX()} , Y: {CurrentPlayer.getPositionY()}"; MainClass.GetScreenReader().Say(toSpeak, true); } + // Narrate Current Location + if (Equals(e.Button, SButton.K) && isLeftAltPressed) + { + string toSpeak = $"{Game1.currentLocation.Name}"; + MainClass.GetScreenReader().Say(toSpeak, true); + } + // Narrate money at hand if (Equals(e.Button, SButton.R)) { From 49be1d3e6b1170a2a12267cde2ff588ea652756b Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 19 Mar 2022 14:12:47 +0530 Subject: [PATCH 008/232] Bug fix in bundle menu --- stardew-access/Patches/DialoguePatches.cs | 1 - stardew-access/Patches/GameMenuPatches.cs | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index 8eaf766..437ddaa 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -105,7 +105,6 @@ namespace stardew_access.Patches else toSpeak = response; - MainClass.ErrorLog(toSpeak); MainClass.GetScreenReader().Say(toSpeak, true); } } diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index f815451..6d2931c 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -234,19 +234,19 @@ namespace stardew_access.Patches break; case 2: // For inventory slots { - if (__instance.inventory != null && __instance.inventory.inventory.Count >= 0) + if (__instance.inventory != null && __instance.inventory.actualInventory.Count >= 0) { int prevSlotIndex = currentInventorySlot; currentInventorySlot = currentInventorySlot + (isLeftShiftPressed ? -1 : 1); - if (currentInventorySlot >= __instance.inventory.inventory.Count) + if (currentInventorySlot >= __instance.inventory.actualInventory.Count) if (isLeftShiftPressed) - currentInventorySlot = __instance.inventory.inventory.Count - 1; + currentInventorySlot = __instance.inventory.actualInventory.Count - 1; else currentInventorySlot = 0; if (currentInventorySlot < 0) if (isLeftShiftPressed) - currentInventorySlot = __instance.inventory.inventory.Count - 1; + currentInventorySlot = __instance.inventory.actualInventory.Count - 1; else currentInventorySlot = 0; From 5b0c6fe154e5a4f230524b60b2ffc3905f3b18a3 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 19 Mar 2022 14:30:37 +0530 Subject: [PATCH 009/232] Fixed letter viewer menu patch --- stardew-access/Patches/MenuPatches.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 94ad707..8312e82 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -281,6 +281,7 @@ namespace stardew_access.Patches if (!__instance.IsActive()) return; + int x = Game1.getMousePosition().X, y = Game1.getMousePosition().Y; #region Texts in the letter string message = __instance.mailMessage[__instance.page]; @@ -305,11 +306,14 @@ namespace stardew_access.Patches currentLetterText = toSpeak; // snap mouse to accept quest button - if (__instance.acceptQuestButton != null && __instance.acceptQuestButton.visible) + 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.GetScreenReader().Say(toSpeak, false); } #endregion @@ -322,11 +326,20 @@ namespace stardew_access.Patches string name = c.name; string label = c.label; - if (c.containsPoint(Game1.getMousePosition().X, Game1.getMousePosition().Y)) + if (c.containsPoint(x, y)) MainClass.GetScreenReader().SayWithChecker($"Grab: {name} \t\n {label}", false); } } #endregion + + #region Narrate buttons + if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y)) + MainClass.GetScreenReader().SayWithChecker($"Previous page button", false); + + if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y)) + MainClass.GetScreenReader().SayWithChecker($"Next page button", false); + + #endregion } catch (Exception e) { From 32d354f23c8e617b99ad21576572c376adfc1da8 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 19 Mar 2022 15:23:18 +0530 Subject: [PATCH 010/232] Fixing museum menu --- stardew-access/HarmonyPatches.cs | 11 +++ stardew-access/ModEntry.cs | 4 - stardew-access/Patches/MenuPatches.cs | 114 ++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 4 deletions(-) diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index 2a4f5c0..7a9440a 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -1,6 +1,7 @@ using HarmonyLib; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; using stardew_access.Patches; using StardewValley; using StardewValley.Menus; @@ -137,6 +138,16 @@ namespace stardew_access 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(MenuPatches), nameof(MenuPatches.MuseumMenuKeyPressPatch)) + ); + + harmony.Patch( + original: AccessTools.Method(typeof(MuseumMenu), nameof(MuseumMenu.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.MuseumMenuPatch)) + ); #endregion #region Quest Patches diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index bda0a42..5558305 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -205,17 +205,13 @@ namespace stardew_access // Manual read tile at looking tile if (Equals(e.Button, SButton.J) && !isLeftAltPressed) { - readTile = false; ReadTile.run(manuallyTriggered: true); - Task.Delay(1000).ContinueWith(t => { readTile = true; }); } // Manual read tile at player's position if (Equals(e.Button, SButton.J) && isLeftAltPressed) { - readTile = false; ReadTile.run(manuallyTriggered: true, playersPosition: true); - Task.Delay(1000).ContinueWith(t => { readTile = true; }); } /*if (Equals(e.Button, SButton.B)) diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 8312e82..fb3d267 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -1,4 +1,5 @@ using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; using stardew_access.Features; using StardewModdingAPI; using StardewValley; @@ -9,8 +10,121 @@ namespace stardew_access.Patches internal class MenuPatches { private static string currentLetterText = " "; + private static string hoveredItemQueryKey = " "; private static string currentLevelUpTitle = " "; public static Vector2? prevTile = null; + private static bool isMoving = false; + + 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(), y = Game1.getMouseY(); // Mouse x and y position + + if (___holdingMuseumPiece) + { + // Museum Inventory + } + else + { + // Player Inventory + narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y); + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static bool narrateHoveredItemInInventory(InventoryMenu inventoryMenu, List inventory, IList actualInventory, int x, int y) + { + #region Narrate hovered item + for (int i = 0; i < inventory.Count; i++) + { + if (inventory[i].containsPoint(x, y)) + { + string toSpeak = ""; + if ((i + 1) <= actualInventory.Count) + { + if (actualInventory[i] != null) + { + string name = actualInventory[i].DisplayName; + int stack = actualInventory[i].Stack; + string quality = ""; + + #region Add quality of item + if (actualInventory[i] is StardewValley.Object && ((StardewValley.Object)actualInventory[i]).quality > 0) + { + int qualityIndex = ((StardewValley.Object)actualInventory[i]).quality; + if (qualityIndex == 1) + { + quality = "Silver quality"; + } + else if (qualityIndex == 2 || qualityIndex == 3) + { + quality = "Gold quality"; + } + else if (qualityIndex >= 4) + { + quality = "Iridium quality"; + } + } + #endregion + + if (inventoryMenu.highlightMethod(inventoryMenu.actualInventory[i])) + name = $"Donatable {name}"; + + if (stack > 1) + toSpeak = $"{stack} {name} {quality}"; + else + toSpeak = $"{name} {quality}"; + } + else + { + // For empty slot + toSpeak = "Empty Slot"; + } + } + else + { + // For empty slot + toSpeak = "Empty Slot"; + } + + if (hoveredItemQueryKey != $"{toSpeak}:{i}") + { + hoveredItemQueryKey = $"{toSpeak}:{i}"; + MainClass.GetScreenReader().Say(toSpeak, true); + } + return true; + } + } + #endregion + return false; + } internal static bool PlaySoundPatch(string cueName) { From 092960f5711de0d562a5a551ac33ce35c2767479 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 19 Mar 2022 15:44:57 +0530 Subject: [PATCH 011/232] Fixed museum menu --- stardew-access/Patches/MenuPatches.cs | 36 +++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index fb3d267..8ac486f 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -3,6 +3,7 @@ using Microsoft.Xna.Framework.Input; using stardew_access.Features; using StardewModdingAPI; using StardewValley; +using StardewValley.Locations; using StardewValley.Menus; namespace stardew_access.Patches @@ -10,11 +11,12 @@ namespace stardew_access.Patches internal class MenuPatches { private static string currentLetterText = " "; - private static string hoveredItemQueryKey = " "; + private static string museumQueryKey = " "; private static string currentLevelUpTitle = " "; public static Vector2? prevTile = null; private static bool isMoving = false; + #region Museum Menu Patch internal static bool MuseumMenuKeyPressPatch() { try @@ -43,14 +45,37 @@ namespace stardew_access.Patches { int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position - if (___holdingMuseumPiece) + 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 = $"{tileX}x {tileY}y"; + + if (museumQueryKey != toSpeak) + { + museumQueryKey = toSpeak; + MainClass.GetScreenReader().Say(toSpeak, true); + } } else { // Player Inventory - narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y); + if (!narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) + { + if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + { + if (museumQueryKey != $"ok button") + { + museumQueryKey = $"ok button"; + MainClass.GetScreenReader().Say("ok button", true); + } + } + } } } catch (Exception e) @@ -58,6 +83,7 @@ namespace stardew_access.Patches MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } + #endregion internal static bool narrateHoveredItemInInventory(InventoryMenu inventoryMenu, List inventory, IList actualInventory, int x, int y) { @@ -114,9 +140,9 @@ namespace stardew_access.Patches toSpeak = "Empty Slot"; } - if (hoveredItemQueryKey != $"{toSpeak}:{i}") + if (museumQueryKey != $"{toSpeak}:{i}") { - hoveredItemQueryKey = $"{toSpeak}:{i}"; + museumQueryKey = $"{toSpeak}:{i}"; MainClass.GetScreenReader().Say(toSpeak, true); } return true; From eb68e61bf7ad2407ea3a5734b7ef35deb9d19e38 Mon Sep 17 00:00:00 2001 From: TrueBlindGaming Date: Sat, 19 Mar 2022 12:25:07 -0600 Subject: [PATCH 012/232] fix for busy stones..mostly --- stardew-access/Features/ReadTile.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 742a70e..03eaef0 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -687,12 +687,13 @@ namespace stardew_access.Features if (obj is Furniture) toReturn.category = CATEGORY.Furnitures; - MachineState machineState = GetMachineState(obj); - if (machineState == MachineState.Ready) - toReturn.name = $"Harvestable {toReturn.name}"; - else if (machineState == MachineState.Busy) - toReturn.name = $"Busy {toReturn.name}"; - + if(toReturn.category == CATEGORY.Others) { + MachineState machineState = GetMachineState(obj); + if (machineState == MachineState.Ready) + toReturn.name = $"Harvestable {toReturn.name}"; + else if (machineState == MachineState.Busy) + toReturn.name = $"Busy {toReturn.name}"; + } return toReturn; } From b7e01c1dc16fa39412a76cd03f590d5e0c66a2fb Mon Sep 17 00:00:00 2001 From: TrueBlindGaming Date: Sat, 19 Mar 2022 16:59:42 -0600 Subject: [PATCH 013/232] fix for missing bundle --- stardew-access/Features/ReadTile.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 03eaef0..8f95cf2 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -342,7 +342,7 @@ namespace stardew_access.Features if (name != null && communityCenter.shouldNoteAppearInArea(CommunityCenter.getAreaNumberFromName(name))) return $"{name} bundle"; } - else if (Game1.currentLocation is not AbandonedJojaMart) + else if (Game1.currentLocation is AbandonedJojaMart) { name = (x, y) switch { @@ -687,7 +687,7 @@ namespace stardew_access.Features if (obj is Furniture) toReturn.category = CATEGORY.Furnitures; - if(toReturn.category == CATEGORY.Others) { + if(toReturn.category == CATEGORY.Others) { MachineState machineState = GetMachineState(obj); if (machineState == MachineState.Ready) toReturn.name = $"Harvestable {toReturn.name}"; @@ -838,7 +838,7 @@ namespace stardew_access.Features return (null, CATEGORY.Others); } - #endregion + #endregion public static bool isMineDownLadderAtTile(int x, int y) { From 2a003fc6baefd38ed37541049f86c0eb9b0f79c9 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 20 Mar 2022 12:26:01 +0530 Subject: [PATCH 014/232] Fixed busy stone --- stardew-access/Features/ReadTile.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 8f95cf2..8d91051 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -342,7 +342,7 @@ namespace stardew_access.Features if (name != null && communityCenter.shouldNoteAppearInArea(CommunityCenter.getAreaNumberFromName(name))) return $"{name} bundle"; } - else if (Game1.currentLocation is AbandonedJojaMart) + else if (Game1.currentLocation is AbandonedJojaMart) { name = (x, y) switch { @@ -678,6 +678,9 @@ namespace stardew_access.Features if (correctNameAndCategory.name != null) toReturn = correctNameAndCategory; + if (toReturn.name.ToLower().Equals("stone")) + toReturn.category = CATEGORY.Debris; + if (obj is Chest) { Chest chest = (Chest)obj; @@ -687,7 +690,8 @@ namespace stardew_access.Features if (obj is Furniture) toReturn.category = CATEGORY.Furnitures; - if(toReturn.category == CATEGORY.Others) { + if (toReturn.category == CATEGORY.Others) + { MachineState machineState = GetMachineState(obj); if (machineState == MachineState.Ready) toReturn.name = $"Harvestable {toReturn.name}"; From b47e1b5cd63d9088af19ee2fca22aae975094da2 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 20 Mar 2022 12:35:50 +0530 Subject: [PATCH 015/232] Beta 1.1.2 --- stardew-access/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index 4196f38..45f9807 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.1.1", + "Version": "1.1.2", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From b3a9e970165d4dd77c48176f3d432bf2417153a1 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 20 Mar 2022 13:04:24 +0530 Subject: [PATCH 016/232] more readability --- stardew-access/Features/ReadTile.cs | 4 ++-- stardew-access/Patches/MenuPatches.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 8d91051..44537cf 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -678,7 +678,7 @@ namespace stardew_access.Features if (correctNameAndCategory.name != null) toReturn = correctNameAndCategory; - if (toReturn.name.ToLower().Equals("stone")) + if (toReturn.name.ToLower().Equals("stone")) // Fix for `Busy stone` toReturn.category = CATEGORY.Debris; if (obj is Chest) @@ -690,7 +690,7 @@ namespace stardew_access.Features if (obj is Furniture) toReturn.category = CATEGORY.Furnitures; - if (toReturn.category == CATEGORY.Others) + if (toReturn.category == CATEGORY.Others) // Fix for `Harvestable table` and `Busy nodes` { MachineState machineState = GetMachineState(obj); if (machineState == MachineState.Ready) diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 8ac486f..95c3633 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -54,7 +54,7 @@ namespace stardew_access.Patches LibraryMuseum libraryMuseum = (LibraryMuseum)Game1.currentLocation; if (libraryMuseum.isTileSuitableForMuseumPiece(tileX, tileY)) - toSpeak = $"{tileX}x {tileY}y"; + toSpeak = $"slot {tileX}x {tileY}y"; if (museumQueryKey != toSpeak) { From f9b4bc88db6348c0f84a7d226e5d8de502cb8e25 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Sun, 20 Mar 2022 16:26:30 -0400 Subject: [PATCH 017/232] added config and option to toggle coordinate verbosity; fixed coordinate formatting of comma. --- stardew-access/ModConfig.cs | 7 +++++++ stardew-access/ModEntry.cs | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 stardew-access/ModConfig.cs diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs new file mode 100644 index 0000000..e671468 --- /dev/null +++ b/stardew-access/ModConfig.cs @@ -0,0 +1,7 @@ +namespace stardew_access +{ + class ModConfig + { + public Boolean VerboseCoordinates { get; set; } = true; + } +} diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 5558305..117524a 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -12,6 +12,7 @@ namespace stardew_access { public class MainClass : Mod { + private ModConfig config; private Harmony? harmony; public static bool readTile = true; public static bool snapMouse = true; @@ -54,7 +55,7 @@ namespace stardew_access public override void Entry(IModHelper helper) { #region Initializations - + this.config = helper.ReadConfig(); SetMonitor(base.Monitor); // Inititalize monitor modHelper = helper; @@ -177,7 +178,16 @@ namespace stardew_access // Narrate Position if (Equals(e.Button, SButton.K) && !isLeftAltPressed) { - string toSpeak = $"X: {CurrentPlayer.getPositionX()} , Y: {CurrentPlayer.getPositionY()}"; + string toSpeak; + if (this.config.VerboseCoordinates) + { + toSpeak = $"X: {CurrentPlayer.getPositionX()}, Y: {CurrentPlayer.getPositionY()}"; + } + else + { + toSpeak = $"{CurrentPlayer.getPositionX()}, {CurrentPlayer.getPositionY()}"; + } + MainClass.GetScreenReader().Say(toSpeak, true); } From 9607a008e01fad51500bcdecce6d1b19b42ca41d Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 21 Mar 2022 11:13:55 +0530 Subject: [PATCH 018/232] Added mod config --- stardew-access/CustomCommands.cs | 20 +++++++++---------- stardew-access/Features/Radar.cs | 2 +- stardew-access/Features/ReadTile.cs | 2 +- stardew-access/ModConfig.cs | 8 +++++++- stardew-access/ModEntry.cs | 30 ++++++++++++++--------------- 5 files changed, 34 insertions(+), 28 deletions(-) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index db08520..eb00255 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -15,30 +15,30 @@ namespace stardew_access helper.ConsoleCommands.Add("readtile", "Toggle read tile feature.", (string commmand, string[] args) => { - MainClass.readTile = !MainClass.readTile; + MainClass.Config.ReadTile = !MainClass.Config.ReadTile; - MainClass.DebugLog("Read Tile is " + (MainClass.readTile ? "on" : "off")); + MainClass.DebugLog("Read Tile is " + (MainClass.Config.ReadTile ? "on" : "off")); }); helper.ConsoleCommands.Add("snapmouse", "Toggle snap mouse feature.", (string commmand, string[] args) => { - MainClass.snapMouse = !MainClass.snapMouse; + MainClass.Config.SnapMouse = !MainClass.Config.SnapMouse; - MainClass.DebugLog("Snap Mouse is " + (MainClass.snapMouse ? "on" : "off")); + MainClass.DebugLog("Snap Mouse is " + (MainClass.Config.SnapMouse ? "on" : "off")); }); helper.ConsoleCommands.Add("flooring", "Toggle flooring in read tile.", (string commmand, string[] args) => { - MainClass.readFlooring = !MainClass.readFlooring; + MainClass.Config.ReadFlooring = !MainClass.Config.ReadFlooring; - MainClass.DebugLog("Flooring is " + (MainClass.readFlooring ? "on" : "off")); + MainClass.DebugLog("Flooring is " + (MainClass.Config.ReadFlooring ? "on" : "off")); }); helper.ConsoleCommands.Add("radar", "Toggle radar feature.", (string commmand, string[] args) => { - MainClass.radar = !MainClass.radar; + MainClass.Config.Radar = !MainClass.Config.Radar; - MainClass.DebugLog("Radar " + (MainClass.radar ? "on" : "off")); + MainClass.DebugLog("Radar " + (MainClass.Config.Radar ? "on" : "off")); }); #region Radar Feature @@ -51,9 +51,9 @@ namespace stardew_access helper.ConsoleCommands.Add("rstereo", "Toggle stereo sound in radar feature.", (string commmand, string[] args) => { - MainClass.radarStereoSound = !MainClass.radarStereoSound; + MainClass.Config.RadarStereoSound = !MainClass.Config.RadarStereoSound; - MainClass.DebugLog("Stereo sound is " + (MainClass.radarStereoSound ? "on" : "off")); + MainClass.DebugLog("Stereo sound is " + (MainClass.Config.RadarStereoSound ? "on" : "off")); }); helper.ConsoleCommands.Add("rfocus", "Toggle focus mode in radar feature.", (string commmand, string[] args) => diff --git a/stardew-access/Features/Radar.cs b/stardew-access/Features/Radar.cs index 4fcba88..e0a708e 100644 --- a/stardew-access/Features/Radar.cs +++ b/stardew-access/Features/Radar.cs @@ -330,7 +330,7 @@ namespace stardew_access.Features { string soundName = $"_{post}"; - if (!MainClass.radarStereoSound) + if (!MainClass.Config.RadarStereoSound) soundName = $"_mono{soundName}"; if (category == CATEGORY.Farmers) // Villagers and farmers diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 44537cf..56446aa 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -543,7 +543,7 @@ namespace stardew_access.Features if (toReturn.Contains("feature")) toReturn.Replace("feature", ""); } - else if (terrain.Get() is Flooring && MainClass.readFlooring) + else if (terrain.Get() is Flooring && MainClass.Config.ReadFlooring) { category = CATEGORY.Flooring; Flooring flooring = (Flooring)terrain.Get(); diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index e671468..a02a089 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -1,7 +1,13 @@ namespace stardew_access { - class ModConfig + internal class ModConfig { public Boolean VerboseCoordinates { get; set; } = true; + public Boolean ReadTile { get; set; } = true; + public Boolean SnapMouse { get; set; } = true; + public Boolean Radar { get; set; } = false; + public Boolean RadarStereoSound { get; set; } = true; + public Boolean ReadFlooring { get; set; } = false; + } } diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 117524a..2ec6c4d 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -12,24 +12,23 @@ namespace stardew_access { public class MainClass : Mod { - private ModConfig config; + #region Global Vars + private static ModConfig config; private Harmony? harmony; - public static bool readTile = true; - public static bool snapMouse = true; - public static bool isNarratingHudMessage = false; - public static bool radar = false; - public static bool radarDebug = false; - public static bool radarStereoSound = true; - public static bool readFlooring = false; private static IMonitor monitor; - public static string hudMessageQueryKey = ""; private static Radar radarFeature; private static IScreenReader? screenReader; private static IModHelper modHelper; + internal static ModConfig Config { get => config; set => config = value; } public static IModHelper ModHelper { get => modHelper; } public static Radar RadarFeature { get => radarFeature; set => radarFeature = value; } + public static string hudMessageQueryKey = ""; + public static bool isNarratingHudMessage = false; + public static bool radarDebug = false; + #endregion + public static IScreenReader GetScreenReader() { if (screenReader == null) @@ -55,7 +54,8 @@ namespace stardew_access public override void Entry(IModHelper helper) { #region Initializations - this.config = helper.ReadConfig(); + Config = helper.ReadConfig(); + SetMonitor(base.Monitor); // Inititalize monitor modHelper = helper; @@ -116,13 +116,13 @@ namespace stardew_access Other.narrateCurrentLocation(); - if (snapMouse) + if (Config.SnapMouse) Other.SnapMouseToPlayer(); - if (!ReadTile.isReadingTile && readTile) + if (!ReadTile.isReadingTile && Config.ReadTile) ReadTile.run(); - if (!RadarFeature.isRunning && radar) + if (!RadarFeature.isRunning && Config.Radar) RadarFeature.Run(); if (!isNarratingHudMessage) @@ -179,7 +179,7 @@ namespace stardew_access if (Equals(e.Button, SButton.K) && !isLeftAltPressed) { string toSpeak; - if (this.config.VerboseCoordinates) + if (Config.VerboseCoordinates) { toSpeak = $"X: {CurrentPlayer.getPositionX()}, Y: {CurrentPlayer.getPositionY()}"; } @@ -187,7 +187,7 @@ namespace stardew_access { toSpeak = $"{CurrentPlayer.getPositionX()}, {CurrentPlayer.getPositionY()}"; } - + MainClass.GetScreenReader().Say(toSpeak, true); } From fb087476d222a27364b2998884ce30b4af858edc Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 21 Mar 2022 12:12:50 +0530 Subject: [PATCH 019/232] Added mod config with keybindings --- stardew-access/CustomCommands.cs | 12 ++++ stardew-access/HarmonyPatches.cs | 2 +- stardew-access/ModConfig.cs | 20 +++++- stardew-access/ModEntry.cs | 67 ++++++++++--------- .../Patches/BuildingNAnimalMenuPatches.cs | 4 +- stardew-access/Patches/GameMenuPatches.cs | 24 +++---- stardew-access/Patches/MenuPatches.cs | 29 +++----- stardew-access/Patches/QuestPatches.cs | 4 +- stardew-access/Patches/TitleMenuPatches.cs | 65 ++++++++++-------- stardew-access/manifest.json | 2 +- 10 files changed, 131 insertions(+), 98 deletions(-) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index eb00255..d4b2504 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -16,6 +16,7 @@ namespace stardew_access helper.ConsoleCommands.Add("readtile", "Toggle read tile feature.", (string commmand, string[] args) => { MainClass.Config.ReadTile = !MainClass.Config.ReadTile; + MainClass.ModHelper.WriteConfig(MainClass.Config); MainClass.DebugLog("Read Tile is " + (MainClass.Config.ReadTile ? "on" : "off")); }); @@ -23,6 +24,7 @@ namespace stardew_access helper.ConsoleCommands.Add("snapmouse", "Toggle snap mouse feature.", (string commmand, string[] args) => { MainClass.Config.SnapMouse = !MainClass.Config.SnapMouse; + MainClass.ModHelper.WriteConfig(MainClass.Config); MainClass.DebugLog("Snap Mouse is " + (MainClass.Config.SnapMouse ? "on" : "off")); }); @@ -30,6 +32,7 @@ namespace stardew_access helper.ConsoleCommands.Add("flooring", "Toggle flooring in read tile.", (string commmand, string[] args) => { MainClass.Config.ReadFlooring = !MainClass.Config.ReadFlooring; + MainClass.ModHelper.WriteConfig(MainClass.Config); MainClass.DebugLog("Flooring is " + (MainClass.Config.ReadFlooring ? "on" : "off")); }); @@ -37,6 +40,7 @@ namespace stardew_access helper.ConsoleCommands.Add("radar", "Toggle radar feature.", (string commmand, string[] args) => { MainClass.Config.Radar = !MainClass.Config.Radar; + MainClass.ModHelper.WriteConfig(MainClass.Config); MainClass.DebugLog("Radar " + (MainClass.Config.Radar ? "on" : "off")); }); @@ -52,6 +56,7 @@ namespace stardew_access helper.ConsoleCommands.Add("rstereo", "Toggle stereo sound in radar feature.", (string commmand, string[] args) => { MainClass.Config.RadarStereoSound = !MainClass.Config.RadarStereoSound; + MainClass.ModHelper.WriteConfig(MainClass.Config); MainClass.DebugLog("Stereo sound is " + (MainClass.Config.RadarStereoSound ? "on" : "off")); }); @@ -466,6 +471,13 @@ namespace stardew_access MainClass.DebugLog("Screen Reader refreshed!"); }); + + helper.ConsoleCommands.Add("refmc", "Refresh mod config", (string commmand, string[] args) => + { + MainClass.Config = helper.ReadConfig(); + + MainClass.DebugLog("Mod Config refreshed!"); + }); } } } diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index 7a9440a..d060b90 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -42,7 +42,7 @@ namespace stardew_access harmony.Patch( original: AccessTools.Method(typeof(CharacterCustomization), nameof(CharacterCustomization.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(TitleMenuPatches), nameof(TitleMenuPatches.NewGameMenuPatch)) + postfix: new HarmonyMethod(typeof(TitleMenuPatches), nameof(TitleMenuPatches.CharacterCustomizationMenuPatch)) ); harmony.Patch( diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index a02a089..24dd743 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -1,4 +1,6 @@ -namespace stardew_access +using StardewModdingAPI.Utilities; + +namespace stardew_access { internal class ModConfig { @@ -9,5 +11,21 @@ public Boolean RadarStereoSound { get; set; } = true; public Boolean ReadFlooring { get; set; } = false; + #region KeyBinds + + // https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Input#SButton button key codes + public KeybindList LeftClickMainKey { get; set; } = KeybindList.Parse("LeftControl + Enter"); + public KeybindList RightClickMainKey { get; set; } = KeybindList.Parse("LeftShift + Enter"); + public KeybindList LeftClickAlternateKey { get; set; } = KeybindList.Parse("OemOpenBrackets"); + public KeybindList RightClickAlternateKey { get; set; } = KeybindList.Parse("OemCloseBrackets"); + public KeybindList HealthNStaminaKey { get; set; } = KeybindList.Parse("H"); + public KeybindList PositionKey { get; set; } = KeybindList.Parse("K"); + public KeybindList LocationKey { get; set; } = KeybindList.Parse("LeftAlt + K"); + public KeybindList MoneyKey { get; set; } = KeybindList.Parse("R"); + public KeybindList TimeNSeasonKey { get; set; } = KeybindList.Parse("Q"); + public KeybindList ReadTileKey { get; set; } = KeybindList.Parse("J"); + public KeybindList ReadStandingTileKey { get; set; } = KeybindList.Parse("LeftAlt + J"); + + #endregion } } diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 2ec6c4d..8deeb5d 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -144,39 +144,34 @@ namespace stardew_access bool isLeftControlPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl); bool isCustomizingChrachter = Game1.activeClickableMenu is CharacterCustomization || (TitleMenu.subMenu != null && TitleMenu.subMenu is CharacterCustomization); - // Perform Left Click - if (!isCustomizingChrachter && Equals(e.Button, SButton.OemOpenBrackets)) // Excluding the character creation menu + #region Mouse Click Simulation + // Main Keybinds + if (isLeftControlPressed && Config.LeftClickMainKey.JustPressed()) { Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); } - if (isLeftControlPressed && Equals(e.Button, SButton.Enter)) + if (isLeftShiftPressed && Config.RightClickMainKey.JustPressed()) { - Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); + Game1.activeClickableMenu.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true)); } - // Perform Right CLick - if (!isCustomizingChrachter && Equals(e.Button, SButton.OemCloseBrackets)) // Excluding the character creation menu - { - Game1.activeClickableMenu.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true)); - } - if (isLeftShiftPressed && Equals(e.Button, SButton.Enter)) + // Alternate Keybinds + if (!isCustomizingChrachter && Config.LeftClickAlternateKey.JustPressed()) // Excluding the character creation menu + { + Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); + } + if (!isCustomizingChrachter && Config.RightClickAlternateKey.JustPressed()) // Excluding the character creation menu { Game1.activeClickableMenu.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true)); } + #endregion } if (!Context.IsPlayerFree) return; - // Narrate health and stamina - if (Equals(e.Button, SButton.H)) - { - string toSpeak = $"Health is {CurrentPlayer.getHealth()} and Stamina is {CurrentPlayer.getStamina()}"; - MainClass.GetScreenReader().Say(toSpeak, true); - } - // Narrate Position - if (Equals(e.Button, SButton.K) && !isLeftAltPressed) + if (Config.PositionKey.JustPressed()) { string toSpeak; if (Config.VerboseCoordinates) @@ -189,46 +184,54 @@ namespace stardew_access } MainClass.GetScreenReader().Say(toSpeak, true); + return; + } + + // Narrate health and stamina + if (Config.HealthNStaminaKey.JustPressed()) + { + string toSpeak = $"Health is {CurrentPlayer.getHealth()} and Stamina is {CurrentPlayer.getStamina()}"; + MainClass.GetScreenReader().Say(toSpeak, true); + return; } // Narrate Current Location - if (Equals(e.Button, SButton.K) && isLeftAltPressed) + if (Config.LocationKey.JustPressed()) { string toSpeak = $"{Game1.currentLocation.Name}"; MainClass.GetScreenReader().Say(toSpeak, true); + return; } // Narrate money at hand - if (Equals(e.Button, SButton.R)) + if (Config.MoneyKey.JustPressed()) { string toSpeak = $"You have {CurrentPlayer.getMoney()}g"; MainClass.GetScreenReader().Say(toSpeak, true); + return; } // Narrate time and season - if (Equals(e.Button, SButton.Q)) + if (Config.TimeNSeasonKey.JustPressed()) { string toSpeak = $"Time is {CurrentPlayer.getTimeOfDay()} and it is {CurrentPlayer.getDay()} {CurrentPlayer.getDate()} of {CurrentPlayer.getSeason()}"; MainClass.GetScreenReader().Say(toSpeak, true); - } - - // Manual read tile at looking tile - if (Equals(e.Button, SButton.J) && !isLeftAltPressed) - { - ReadTile.run(manuallyTriggered: true); + return; } // Manual read tile at player's position - if (Equals(e.Button, SButton.J) && isLeftAltPressed) + if (Config.ReadStandingTileKey.JustPressed()) { ReadTile.run(manuallyTriggered: true, playersPosition: true); + return; } - /*if (Equals(e.Button, SButton.B)) + // Manual read tile at looking tile + if (Config.ReadTileKey.JustPressed()) { - Game1.player.controller = new PathFindController(Game1.player, Game1.currentLocation, new Point(49,13), 2); - monitor.Log($"{Game1.player.controller.pathToEndPoint==null}", LogLevel.Debug); // true if path not found - }*/ + ReadTile.run(manuallyTriggered: true); + return; + } } public static void ErrorLog(string message) diff --git a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs index 6aaef02..409b705 100644 --- a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs +++ b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs @@ -24,7 +24,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position purchaseAnimalsMenu = __instance; isOnFarm = ___onFarm; @@ -125,7 +125,7 @@ namespace stardew_access.Patches if (currentBluprint == null) return; - int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position bool isBPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.B); string ingredients = ""; string name = currentBluprint.displayName; diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index 6d2931c..e3f17ca 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -29,7 +29,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position if (!___specificBundlePage) { currentIngredientListItem = -1; @@ -300,7 +300,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position + 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) @@ -308,7 +308,7 @@ namespace stardew_access.Patches if (__instance.names[i] is string) { #region For NPCs - if (__instance.characterSlots[i].bounds.Contains(Game1.getMouseX(), Game1.getMouseY())) + 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); @@ -448,7 +448,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position bool isIPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.I); bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift); @@ -560,7 +560,7 @@ namespace stardew_access.Patches if (__instance.currentTab != 0 && __instance.currentTab != 4 && __instance.currentTab != 6 && __instance.currentTab != 7) return; - int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position for (int i = 0; i < __instance.tabs.Count; i++) { @@ -586,7 +586,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position + 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) @@ -670,7 +670,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position bool isIPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.I); bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift); @@ -934,7 +934,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position bool isIPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.I); bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift); @@ -1138,7 +1138,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position + 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)) @@ -1326,7 +1326,7 @@ namespace stardew_access.Patches try { int currentItemIndex = Math.Max(0, Math.Min(__instance.options.Count - 7, __instance.currentItemIndex)); - int x = Game1.getMouseX(), y = Game1.getMouseY(); + 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)) @@ -1379,7 +1379,7 @@ namespace stardew_access.Patches try { if (__instance.exitToTitle.visible && - __instance.exitToTitle.containsPoint(Game1.getMouseX(), Game1.getMouseY())) + __instance.exitToTitle.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { string toSpeak = "Exit to Title Button"; if (exitPageQueryKey != toSpeak) @@ -1391,7 +1391,7 @@ namespace stardew_access.Patches return; } if (__instance.exitToDesktop.visible && - __instance.exitToDesktop.containsPoint(Game1.getMouseX(), Game1.getMouseY())) + __instance.exitToDesktop.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { string toSpeak = "Exit to Desktop Button"; if (exitPageQueryKey != toSpeak) diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 95c3633..06d325b 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -43,7 +43,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position if (__instance.heldItem != null) { @@ -188,7 +188,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position if (__instance.nextPageButton != null && __instance.nextPageButton.containsPoint(x, y)) { @@ -221,7 +221,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position + 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)) @@ -257,7 +257,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); MainClass.GetScreenReader().SayWithMenuChecker(___message, true); if (__instance.okButton.containsPoint(x, y)) @@ -279,13 +279,9 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); string leftProfession = " ", rightProfession = " ", extraInfo = " ", newCraftingRecipe = " ", toSpeak = " "; - bool isOpenBracketPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.OemOpenBrackets); // for left click - bool isLeftCtrlPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl); - bool isEnterPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Enter); - if (!__instance.informationUp) { return; @@ -307,7 +303,7 @@ namespace stardew_access.Patches if (__instance.leftProfession.containsPoint(x, y)) { - if (isOpenBracketPressed || (isLeftCtrlPressed && isEnterPressed && __instance.readyToClose())) + if ((MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) && __instance.readyToClose()) { Game1.player.professions.Add(___professionsToChoose[0]); __instance.getImmediateProfessionPerk(___professionsToChoose[0]); @@ -324,7 +320,7 @@ namespace stardew_access.Patches if (__instance.rightProfession.containsPoint(x, y)) { - if (isOpenBracketPressed || (isLeftCtrlPressed && isEnterPressed && __instance.readyToClose())) + if ((MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) && __instance.readyToClose()) { Game1.player.professions.Add(___professionsToChoose[1]); __instance.getImmediateProfessionPerk(___professionsToChoose[1]); @@ -356,7 +352,7 @@ namespace stardew_access.Patches if (__instance.okButton.containsPoint(x, y)) { - if (isOpenBracketPressed || (isLeftCtrlPressed && isEnterPressed)) + if (MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) __instance.okButtonClicked(); toSpeak = $"{___title} {extraInfo} {newCraftingRecipe}. Left click to close."; @@ -380,18 +376,15 @@ namespace stardew_access.Patches { try { - bool isLeftControlPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl); - bool isOpenBracketPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.OemOpenBrackets); // for left click - bool isEnterPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Enter); if (__instance.currentPage == -1) { int total = ___categoryTotals[5]; string toSpeak; - if (__instance.okButton.containsPoint(Game1.getMouseX(), Game1.getMouseY())) + if (__instance.okButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { // Perform Left Click - if (isOpenBracketPressed || (isLeftControlPressed && isEnterPressed)) + if (MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) { Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); } @@ -400,7 +393,7 @@ namespace stardew_access.Patches } for (int i = 0; i < __instance.categories.Count; i++) { - if (__instance.categories[i].containsPoint(Game1.getMouseX(), Game1.getMouseY())) + if (__instance.categories[i].containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { toSpeak = $"Money recieved from {__instance.getCategoryName(i)}: {___categoryTotals[i]}g."; MainClass.GetScreenReader().SayWithChecker(toSpeak, true); diff --git a/stardew-access/Patches/QuestPatches.cs b/stardew-access/Patches/QuestPatches.cs index 3acb6a4..4d1b48c 100644 --- a/stardew-access/Patches/QuestPatches.cs +++ b/stardew-access/Patches/QuestPatches.cs @@ -15,7 +15,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position if (__instance.acceptLeftQuestButton.visible && __instance.acceptLeftQuestButton.containsPoint(x, y)) { @@ -83,7 +83,7 @@ namespace stardew_access.Patches #region Callender for (int i = 0; i < __instance.calendarDays.Count; i++) { - if (__instance.calendarDays[i].containsPoint(Game1.getMouseX(), Game1.getMouseY())) + if (__instance.calendarDays[i].containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { string toSpeak = $"Day {i + 1}"; diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index 4d46d16..b66a841 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -1,6 +1,4 @@ -using Microsoft.Xna.Framework; -using StardewModdingAPI; -using StardewValley; +using StardewValley; using StardewValley.Menus; using static StardewValley.Menus.LoadGameMenu; @@ -16,7 +14,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); string toSpeak = " "; #region Join/Host Button (Important! This should be checked before checking other buttons) @@ -64,7 +62,7 @@ namespace stardew_access.Patches __instance.buttons.ForEach(component => { - if (component.containsPoint(Game1.getMouseX(), Game1.getMouseY())) + if (component.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { string name = component.name; string label = component.label; @@ -72,27 +70,27 @@ namespace stardew_access.Patches } }); - if (__instance.muteMusicButton.containsPoint(Game1.getMouseX(), Game1.getMouseY())) + if (__instance.muteMusicButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { toSpeak = "Mute Music Button"; } - if (__instance.aboutButton.containsPoint(Game1.getMouseX(), Game1.getMouseY())) + if (__instance.aboutButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { toSpeak = "About Button"; } - if (__instance.languageButton.containsPoint(Game1.getMouseX(), Game1.getMouseY())) + if (__instance.languageButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { toSpeak = "Language Button"; } - if (__instance.windowedButton.containsPoint(Game1.getMouseX(), Game1.getMouseY())) + if (__instance.windowedButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { toSpeak = "Fullscreen toggle Button"; } - if (TitleMenu.subMenu != null && __instance.backButton.containsPoint(Game1.getMouseX(), Game1.getMouseY())) + if (TitleMenu.subMenu != null && __instance.backButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { string text = "Back Button"; MainClass.GetScreenReader().SayWithChecker(text, true); @@ -111,7 +109,7 @@ namespace stardew_access.Patches { try { - int x = Game1.getMouseX(), y = Game1.getMouseY(); + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); if (___menu.slotButtons[i].containsPoint(x, y)) { if (__instance.Farmer != null) @@ -161,13 +159,22 @@ namespace stardew_access.Patches } } - internal static void NewGameMenuPatch(CharacterCustomization __instance, bool ___skipIntro) + internal static void CharacterCustomizationMenuPatch(CharacterCustomization __instance, bool ___skipIntro) { try { bool isNextArrowPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Right); bool isPrevArrowPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Left); + if (__instance.backButton.containsPoint != null && __instance.backButton.visible && __instance.backButton.containsPoint((int)Game1.getMouseX(true), (int)Game1.getMouseY(true))) + { + // Perform Left Click + if (MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) + { + Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); + } + } + if (isNextArrowPressed && !isRunning) { _ = CycleThroughItems(true, __instance, ___skipIntro); @@ -289,7 +296,7 @@ namespace stardew_access.Patches } #endregion - __instance.skipIntroButton.snapMouseCursor(); + __instance.skipIntroButton.snapMouseCursorToCenter(); toSpeak = (___skipIntro ? "Enabled" : "Disabled") + " Skip Intro Button"; } break; @@ -311,7 +318,7 @@ namespace stardew_access.Patches } #endregion - __instance.randomButton.snapMouseCursor(); + __instance.randomButton.snapMouseCursorToCenter(); toSpeak = "Random Skin Button"; break; } @@ -333,7 +340,7 @@ namespace stardew_access.Patches } #endregion - __instance.genderButtons[0].snapMouseCursor(); + __instance.genderButtons[0].snapMouseCursorToCenter(); toSpeak = "Gender Male Button"; break; } @@ -355,7 +362,7 @@ namespace stardew_access.Patches } #endregion - __instance.genderButtons[1].snapMouseCursor(); + __instance.genderButtons[1].snapMouseCursorToCenter(); toSpeak = "Gender Female Button"; break; } @@ -377,7 +384,7 @@ namespace stardew_access.Patches } #endregion - __instance.farmTypeButtons[0].snapMouseCursor(); + __instance.farmTypeButtons[0].snapMouseCursorToCenter(); toSpeak = getFarmHoverText(__instance.farmTypeButtons[0]); break; } @@ -399,7 +406,7 @@ namespace stardew_access.Patches } #endregion - __instance.farmTypeButtons[1].snapMouseCursor(); + __instance.farmTypeButtons[1].snapMouseCursorToCenter(); toSpeak = getFarmHoverText(__instance.farmTypeButtons[1]); break; } @@ -421,7 +428,7 @@ namespace stardew_access.Patches } #endregion - __instance.farmTypeButtons[2].snapMouseCursor(); + __instance.farmTypeButtons[2].snapMouseCursorToCenter(); toSpeak = getFarmHoverText(__instance.farmTypeButtons[2]); break; } @@ -443,7 +450,7 @@ namespace stardew_access.Patches } #endregion - __instance.farmTypeButtons[3].snapMouseCursor(); + __instance.farmTypeButtons[3].snapMouseCursorToCenter(); toSpeak = getFarmHoverText(__instance.farmTypeButtons[3]); break; } @@ -465,7 +472,7 @@ namespace stardew_access.Patches } #endregion - __instance.farmTypeButtons[4].snapMouseCursor(); + __instance.farmTypeButtons[4].snapMouseCursorToCenter(); toSpeak = getFarmHoverText(__instance.farmTypeButtons[4]); break; } @@ -487,7 +494,7 @@ namespace stardew_access.Patches } #endregion - __instance.farmTypeButtons[5].snapMouseCursor(); + __instance.farmTypeButtons[5].snapMouseCursorToCenter(); toSpeak = getFarmHoverText(__instance.farmTypeButtons[5]); break; } @@ -509,7 +516,7 @@ namespace stardew_access.Patches } #endregion - __instance.farmTypeButtons[6].snapMouseCursor(); + __instance.farmTypeButtons[6].snapMouseCursorToCenter(); toSpeak = getFarmHoverText(__instance.farmTypeButtons[6]); break; } @@ -531,7 +538,7 @@ namespace stardew_access.Patches } #endregion - __instance.farmTypeNextPageButton.snapMouseCursor(); + __instance.farmTypeNextPageButton.snapMouseCursorToCenter(); toSpeak = "Next Farm Type Page Button"; break; } @@ -553,7 +560,7 @@ namespace stardew_access.Patches } #endregion - __instance.farmTypePreviousPageButton.snapMouseCursor(); + __instance.farmTypePreviousPageButton.snapMouseCursorToCenter(); toSpeak = "Previous Farm Type Page Button"; break; } @@ -575,7 +582,7 @@ namespace stardew_access.Patches } #endregion - __instance.cabinLayoutButtons[0].snapMouseCursor(); + __instance.cabinLayoutButtons[0].snapMouseCursorToCenter(); toSpeak = "Cabin layout nearby"; break; } @@ -597,7 +604,7 @@ namespace stardew_access.Patches } #endregion - __instance.cabinLayoutButtons[1].snapMouseCursor(); + __instance.cabinLayoutButtons[1].snapMouseCursorToCenter(); toSpeak = "Cabin layout separate"; break; } @@ -619,7 +626,7 @@ namespace stardew_access.Patches } #endregion - __instance.okButton.snapMouseCursor(); + __instance.okButton.snapMouseCursorToCenter(); toSpeak = "Ok Button"; } break; @@ -632,7 +639,7 @@ namespace stardew_access.Patches } #endregion - __instance.backButton.snapMouseCursor(); + __instance.backButton.snapMouseCursorToCenter(); toSpeak = "Back Button"; } break; diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index 45f9807..27e0f9d 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.1.2", + "Version": "1.1.3", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From 101d0f496caf7bdb6ba6e0cbc706b7cae44bc77d Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 21 Mar 2022 12:33:44 +0530 Subject: [PATCH 020/232] Removed duplicate code --- stardew-access/Features/ReadTile.cs | 83 +++-------------------------- 1 file changed, 6 insertions(+), 77 deletions(-) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 56446aa..acf3117 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -98,6 +98,12 @@ namespace stardew_access.Features return (tileDetail.name, tileDetail.category.ToString()); } + ///Returns the name of the object at tile + public static string? getNameAtTile(Vector2 tile) + { + return getNameWithCategoryAtTile(tile).name; + } + ///Returns the name of the object at tile alongwith it's category public static (string? name, CATEGORY? category) getNameWithCategoryAtTile(Vector2 tile) { @@ -197,83 +203,6 @@ namespace stardew_access.Features return (toReturn, category); } - ///Returns the name of the object at tile - public static string? getNameAtTile(Vector2 tile) - { - int x = (int)tile.X; - int y = (int)tile.Y; - string? toReturn = ""; - - bool isColliding = isCollidingAtTile(x, y); - Dictionary> terrainFeature = Game1.currentLocation.terrainFeatures.FieldDict; - string? door = getDoorAtTile(x, y); - (CATEGORY? category, string? name) tileInfo = getTileInfo(x, y); - string? junimoBundle = getJunimoBundleAt(x, y); - string? resourceClump = getResourceClumpAtTile(x, y); - string? farmAnimal = getFarmAnimalAt(Game1.currentLocation, x, y); - - if (Game1.currentLocation.isCharacterAtTile(tile) != null) - { - NPC npc = Game1.currentLocation.isCharacterAtTile(tile); - toReturn = npc.displayName; - } - else if (farmAnimal != null) - { - toReturn = farmAnimal; - } - else if (Game1.currentLocation.isWaterTile(x, y) && isColliding) - { - toReturn = "Water"; - } - else if (Game1.currentLocation.isObjectAtTile(x, y)) - { - toReturn = getObjectAtTile(x, y).name; - } - else if (terrainFeature.ContainsKey(tile)) - { - string? terrain = getTerrainFeatureAtTile(terrainFeature[tile]).Item1; - if (terrain != null) - toReturn = terrain; - } - else if (Game1.currentLocation.getLargeTerrainFeatureAt(x, y) != null) - { - toReturn = getBushAtTile(x, y); - } - else if (resourceClump != null) - { - toReturn = resourceClump; - } - else if (door != null) - { - toReturn = door; - } - else if (isMineDownLadderAtTile(x, y)) - { - toReturn = "Ladder"; - } - else if (isMineUpLadderAtTile(x, y)) - { - toReturn = "Up Ladder"; - } - else if (isElevatorAtTile(x, y)) - { - toReturn = "Elevator"; - } - else if (tileInfo.name != null) - { - toReturn = tileInfo.name; - } - else if (junimoBundle != null) - { - toReturn = junimoBundle; - } - - if (toReturn == "") - return null; - - return toReturn; - } - public static string? getBushAtTile(int x, int y) { string? toReturn = null; From 5d8912ae7cfb9428a94890ccd3a120294ee67ea0 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 29 Mar 2022 19:25:53 +0530 Subject: [PATCH 021/232] Added fullscreen indication --- stardew-access/ModConfig.cs | 3 +++ stardew-access/Patches/TitleMenuPatches.cs | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index 24dd743..98c19df 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -27,5 +27,8 @@ namespace stardew_access public KeybindList ReadStandingTileKey { get; set; } = KeybindList.Parse("LeftAlt + J"); #endregion + + // TODO Add the exclusion and focus list too + // public String ExclusionList { get; set; } = "test"; } } diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index b66a841..6324855 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -87,7 +87,8 @@ namespace stardew_access.Patches if (__instance.windowedButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { - toSpeak = "Fullscreen toggle Button"; + bool isFullscreen = Game1.options.isCurrentlyFullscreen(); + toSpeak = "Fullscreen: " + ((isFullscreen) ? "on" : "off"); } if (TitleMenu.subMenu != null && __instance.backButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) From d24c224d1ddcc2ed96d2e40463e820ab9ee8c0b4 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 29 Mar 2022 19:57:38 +0530 Subject: [PATCH 022/232] Code cleanup --- stardew-access/Features/Other.cs | 5 +- stardew-access/Features/Radar.cs | 6 +- stardew-access/Features/ReadTile.cs | 7 +- stardew-access/HarmonyPatches.cs | 2 +- stardew-access/ModEntry.cs | 10 ++ ...{ChatManuPatches.cs => ChatMenuPatches.cs} | 14 +- stardew-access/Patches/GameMenuPatches.cs | 26 ++-- stardew-access/Patches/MenuPatches.cs | 139 +++++++++--------- stardew-access/Patches/TitleMenuPatches.cs | 14 +- .../ScreenReader/ScreenReaderLinux.cs | 5 + 10 files changed, 113 insertions(+), 115 deletions(-) rename stardew-access/Patches/{ChatManuPatches.cs => ChatMenuPatches.cs} (86%) diff --git a/stardew-access/Features/Other.cs b/stardew-access/Features/Other.cs index a518787..6876480 100644 --- a/stardew-access/Features/Other.cs +++ b/stardew-access/Features/Other.cs @@ -67,9 +67,8 @@ namespace stardew_access.Features Game1.setMousePosition(x, y); } - public static async void narrateHudMessages() + public static void narrateHudMessages() { - MainClass.isNarratingHudMessage = true; try { if (Game1.hudMessages.Count > 0) @@ -99,8 +98,6 @@ namespace stardew_access.Features MainClass.ErrorLog($"Unable to narrate hud messages:\n{e.Message}\n{e.StackTrace}"); } - await Task.Delay(300); - MainClass.isNarratingHudMessage = false; } } } diff --git a/stardew-access/Features/Radar.cs b/stardew-access/Features/Radar.cs index e0a708e..62b4c27 100644 --- a/stardew-access/Features/Radar.cs +++ b/stardew-access/Features/Radar.cs @@ -96,12 +96,11 @@ namespace stardew_access.Features */ } - public async void Run() + public void Run() { if (MainClass.radarDebug) MainClass.DebugLog($"\n\nRead Tile started"); - isRunning = true; Vector2 currPosition = Game1.player.getTileLocation(); closed.Clear(); @@ -112,9 +111,6 @@ namespace stardew_access.Features if (MainClass.radarDebug) MainClass.DebugLog($"\nRead Tile stopped\n\n"); - - await Task.Delay(delay); - isRunning = false; } /// diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index acf3117..c02fcb1 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -22,10 +22,8 @@ namespace stardew_access.Features isReadingTile = false; } - public static async void run(bool manuallyTriggered = false, bool playersPosition = false) + public static void run(bool manuallyTriggered = false, bool playersPosition = false) { - isReadingTile = true; - try { Vector2 tile; @@ -82,9 +80,6 @@ namespace stardew_access.Features { MainClass.ErrorLog($"Error in Read Tile:\n{e.Message}\n{e.StackTrace}"); } - - await Task.Delay(100); - isReadingTile = false; } ///Returns the name of the object at tile alongwith it's category's name diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index d060b90..a145139 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -170,7 +170,7 @@ namespace stardew_access #region Chat Menu Patches harmony.Patch( original: AccessTools.Method(typeof(ChatBox), nameof(ChatBox.update), new Type[] { typeof(GameTime) }), - postfix: new HarmonyMethod(typeof(ChatManuPatches), nameof(ChatManuPatches.ChatBoxPatch)) + postfix: new HarmonyMethod(typeof(ChatMenuPatches), nameof(ChatMenuPatches.ChatBoxPatch)) ); #endregion diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 8deeb5d..c982f3c 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -120,14 +120,24 @@ namespace stardew_access Other.SnapMouseToPlayer(); if (!ReadTile.isReadingTile && Config.ReadTile) + { + ReadTile.isReadingTile = true; ReadTile.run(); + Task.Delay(100).ContinueWith(_ => { ReadTile.isReadingTile = false; }); + } if (!RadarFeature.isRunning && Config.Radar) + { + RadarFeature.isRunning = true; RadarFeature.Run(); + Task.Delay(RadarFeature.delay).ContinueWith(_ => { RadarFeature.isRunning = false; }); + } if (!isNarratingHudMessage) { + isNarratingHudMessage = true; Other.narrateHudMessages(); + Task.Delay(300).ContinueWith(_ => { isNarratingHudMessage = false; }); } } diff --git a/stardew-access/Patches/ChatManuPatches.cs b/stardew-access/Patches/ChatMenuPatches.cs similarity index 86% rename from stardew-access/Patches/ChatManuPatches.cs rename to stardew-access/Patches/ChatMenuPatches.cs index e30427e..6b3a07a 100644 --- a/stardew-access/Patches/ChatManuPatches.cs +++ b/stardew-access/Patches/ChatMenuPatches.cs @@ -1,10 +1,9 @@ -using StardewModdingAPI; -using StardewValley; +using StardewValley; using StardewValley.Menus; namespace stardew_access.Patches { - internal class ChatManuPatches + internal class ChatMenuPatches { private static int currentChatMessageIndex = 0; private static bool isChatRunning = false; @@ -25,11 +24,15 @@ namespace stardew_access.Patches #region To narrate previous and next chat messages if (isNextArrowPressed && !isChatRunning) { + isChatRunning = true; CycleThroughChatMessages(true, ___messages); + Task.Delay(200).ContinueWith(_ => { isChatRunning = false; }); } else if (isPrevArrowPressed && !isChatRunning) { + isChatRunning = true; CycleThroughChatMessages(false, ___messages); + Task.Delay(200).ContinueWith(_ => { isChatRunning = false; }); } #endregion } @@ -52,9 +55,8 @@ namespace stardew_access.Patches } } - private static async void CycleThroughChatMessages(bool increase, List ___messages) + private static void CycleThroughChatMessages(bool increase, List ___messages) { - isChatRunning = true; string toSpeak = " "; if (increase) { @@ -78,8 +80,6 @@ namespace stardew_access.Patches }); MainClass.GetScreenReader().Say(toSpeak, true); - await Task.Delay(200); - isChatRunning = false; } } } diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index e3f17ca..90481a7 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -1,5 +1,4 @@ -using StardewModdingAPI; -using StardewValley; +using StardewValley; using StardewValley.Locations; using StardewValley.Menus; using StardewValley.Objects; @@ -104,15 +103,21 @@ namespace stardew_access.Patches 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)) { @@ -133,9 +138,8 @@ namespace stardew_access.Patches } } - private static async void JunimoNoteCustomButtons(JunimoNoteMenu __instance, Bundle ___currentPageBundle, int signal, bool isLeftShiftPressed = false) + private static void JunimoNoteCustomButtons(JunimoNoteMenu __instance, Bundle ___currentPageBundle, int signal, bool isLeftShiftPressed = false) { - isUsingCustomButtons = true; try { @@ -291,9 +295,6 @@ namespace stardew_access.Patches { MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } - - await Task.Delay(200); - isUsingCustomButtons = false; } internal static void SocialPagePatch(SocialPage __instance, List ___sprites, int ___slotPosition, List ___kidsNames) @@ -955,7 +956,9 @@ namespace stardew_access.Patches } else if (isCPressed && !isSelectingRecipe) { - _ = CycleThroughRecipies(__instance.pagesOfCraftingRecipes, ___currentCraftingPage, __instance); + isSelectingRecipe = true; + CycleThroughRecipies(__instance.pagesOfCraftingRecipes, ___currentCraftingPage, __instance); + Task.Delay(200).ContinueWith(_ => { isSelectingRecipe = false; }); } #region Narrate buttons in the menu @@ -1103,19 +1106,14 @@ namespace stardew_access.Patches } } - private static async Task CycleThroughRecipies(List> pagesOfCraftingRecipes, int ___currentCraftingPage, CraftingPage __instance) + private static void CycleThroughRecipies(List> pagesOfCraftingRecipes, int ___currentCraftingPage, CraftingPage __instance) { - isSelectingRecipe = true; - currentSelectedCraftingRecipe++; if (currentSelectedCraftingRecipe < 0 || currentSelectedCraftingRecipe >= pagesOfCraftingRecipes[0].Count) currentSelectedCraftingRecipe = 0; __instance.setCurrentlySnappedComponentTo(pagesOfCraftingRecipes[___currentCraftingPage].ElementAt(currentSelectedCraftingRecipe).Key.myID); pagesOfCraftingRecipes[___currentCraftingPage].ElementAt(currentSelectedCraftingRecipe).Key.snapMouseCursorToCenter(); - - await Task.Delay(200); - isSelectingRecipe = false; } // This method is used to get the inventory items to check if the player has enough ingredients for a recipe diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 06d325b..40180dc 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -481,41 +481,12 @@ namespace stardew_access.Patches } } + #region Cleanup on exitting a menu internal static void Game1ExitActiveMenuPatch() { try { - if (Game1.activeClickableMenu is GameMenu) - { - GameMenuPatches.gameMenuQueryKey = ""; - GameMenuPatches.craftingPageQueryKey = ""; - GameMenuPatches.inventoryPageQueryKey = ""; - GameMenuPatches.exitPageQueryKey = ""; - GameMenuPatches.optionsPageQueryKey = ""; - GameMenuPatches.socialPageQuery = ""; - GameMenuPatches.currentSelectedCraftingRecipe = -1; - GameMenuPatches.isSelectingRecipe = false; - } - - if (Game1.activeClickableMenu is JunimoNoteMenu) - { - GameMenuPatches.currentIngredientListItem = -1; - GameMenuPatches.currentIngredientInputSlot = -1; - GameMenuPatches.currentInventorySlot = -1; - GameMenuPatches.junimoNoteMenuQuery = ""; - } - - if (Game1.activeClickableMenu is ShopMenu) - { - GameMenuPatches.shopMenuQueryKey = ""; - } - - if (Game1.activeClickableMenu is ItemGrabMenu) - { - GameMenuPatches.itemGrabMenuQueryKey = ""; - } - - GameMenuPatches.hoveredItemQueryKey = ""; + Cleanup(Game1.activeClickableMenu); } catch (Exception e) { @@ -527,46 +498,7 @@ namespace stardew_access.Patches { try { - if (__instance is GeodeMenu) - { - GameMenuPatches.geodeMenuQueryKey = ""; - } - - if (__instance is ItemGrabMenu) - { - GameMenuPatches.itemGrabMenuQueryKey = ""; - } - - if (__instance is ShopMenu) - { - GameMenuPatches.shopMenuQueryKey = ""; - } - - if (__instance is CarpenterMenu) - { - BuildingNAnimalMenuPatches.carpenterMenuQuery = ""; - BuildingNAnimalMenuPatches.isUpgrading = false; - BuildingNAnimalMenuPatches.isDemolishing = false; - BuildingNAnimalMenuPatches.isPainting = false; - BuildingNAnimalMenuPatches.isMoving = false; - BuildingNAnimalMenuPatches.isConstructing = false; - BuildingNAnimalMenuPatches.carpenterMenu = null; - } - - if (__instance is PurchaseAnimalsMenu) - { - BuildingNAnimalMenuPatches.purchaseAnimalMenuQuery = ""; - BuildingNAnimalMenuPatches.firstTimeInNamingMenu = true; - BuildingNAnimalMenuPatches.purchaseAnimalsMenu = null; - } - - if (__instance is DialogueBox) - { - DialoguePatches.isDialogueAppearingFirstTime = true; - DialoguePatches.currentDialogue = " "; - } - - GameMenuPatches.hoveredItemQueryKey = ""; + Cleanup(__instance); } catch (Exception e) { @@ -574,6 +506,71 @@ namespace stardew_access.Patches } } + private static void Cleanup(IClickableMenu menu) + { + if (menu is GameMenu) + { + GameMenuPatches.gameMenuQueryKey = ""; + GameMenuPatches.craftingPageQueryKey = ""; + GameMenuPatches.inventoryPageQueryKey = ""; + GameMenuPatches.exitPageQueryKey = ""; + GameMenuPatches.optionsPageQueryKey = ""; + GameMenuPatches.socialPageQuery = ""; + GameMenuPatches.currentSelectedCraftingRecipe = -1; + GameMenuPatches.isSelectingRecipe = false; + } + + if (menu is JunimoNoteMenu) + { + GameMenuPatches.currentIngredientListItem = -1; + GameMenuPatches.currentIngredientInputSlot = -1; + GameMenuPatches.currentInventorySlot = -1; + GameMenuPatches.junimoNoteMenuQuery = ""; + } + + if (menu is ShopMenu) + { + GameMenuPatches.shopMenuQueryKey = ""; + } + + if (menu is ItemGrabMenu) + { + GameMenuPatches.itemGrabMenuQueryKey = ""; + } + + if (menu is GeodeMenu) + { + GameMenuPatches.geodeMenuQueryKey = ""; + } + + if (menu is CarpenterMenu) + { + BuildingNAnimalMenuPatches.carpenterMenuQuery = ""; + BuildingNAnimalMenuPatches.isUpgrading = false; + BuildingNAnimalMenuPatches.isDemolishing = false; + BuildingNAnimalMenuPatches.isPainting = false; + BuildingNAnimalMenuPatches.isMoving = false; + BuildingNAnimalMenuPatches.isConstructing = false; + BuildingNAnimalMenuPatches.carpenterMenu = null; + } + + if (menu is PurchaseAnimalsMenu) + { + BuildingNAnimalMenuPatches.purchaseAnimalMenuQuery = ""; + BuildingNAnimalMenuPatches.firstTimeInNamingMenu = true; + BuildingNAnimalMenuPatches.purchaseAnimalsMenu = null; + } + + if (menu is DialogueBox) + { + DialoguePatches.isDialogueAppearingFirstTime = true; + DialoguePatches.currentDialogue = " "; + } + + GameMenuPatches.hoveredItemQueryKey = ""; + } + #endregion + internal static void ExitEventPatch() { if (MainClass.GetScreenReader() != null) diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index 6324855..066014e 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -178,11 +178,15 @@ namespace stardew_access.Patches if (isNextArrowPressed && !isRunning) { - _ = CycleThroughItems(true, __instance, ___skipIntro); + isRunning = true; + CycleThroughItems(true, __instance, ___skipIntro); + Task.Delay(200).ContinueWith(_ => { isRunning = false; }); } else if (isPrevArrowPressed && !isRunning) { - _ = CycleThroughItems(false, __instance, ___skipIntro); + isRunning = true; + CycleThroughItems(false, __instance, ___skipIntro); + Task.Delay(200).ContinueWith(_ => { isRunning = false; }); } } catch (Exception e) @@ -191,9 +195,8 @@ namespace stardew_access.Patches } } - private static async Task CycleThroughItems(bool increase, CharacterCustomization __instance, bool ___skipIntro) + private static void CycleThroughItems(bool increase, CharacterCustomization __instance, bool ___skipIntro) { - isRunning = true; string toSpeak = " "; if (increase) @@ -650,9 +653,6 @@ namespace stardew_access.Patches { MainClass.GetScreenReader().Say(toSpeak, true); } - - await Task.Delay(200); - isRunning = false; } private static string getFarmHoverText(ClickableTextureComponent farm) diff --git a/stardew-access/ScreenReader/ScreenReaderLinux.cs b/stardew-access/ScreenReader/ScreenReaderLinux.cs index 8b85f68..d058cd6 100644 --- a/stardew-access/ScreenReader/ScreenReaderLinux.cs +++ b/stardew-access/ScreenReader/ScreenReaderLinux.cs @@ -1,3 +1,8 @@ +/* + Linux speech dispatcher library used: + https://github.com/shoaib11120/libspeechdwrapper +*/ + using System.Runtime.InteropServices; namespace stardew_access.ScreenReader From 706330551007541a3279872968cc405ae9a661c4 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 29 Mar 2022 19:59:47 +0530 Subject: [PATCH 023/232] Fixed fullscreen indication --- stardew-access/Patches/TitleMenuPatches.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index 066014e..fed2f1e 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -87,8 +87,7 @@ namespace stardew_access.Patches if (__instance.windowedButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { - bool isFullscreen = Game1.options.isCurrentlyFullscreen(); - toSpeak = "Fullscreen: " + ((isFullscreen) ? "on" : "off"); + toSpeak = "Fullscreen: " + ((Game1.isFullscreen) ? "on" : "off"); } if (TitleMenu.subMenu != null && __instance.backButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) From bfad0676bd0379436a4cc8318fa68f75ffa3910e Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 31 Mar 2022 14:31:09 +0530 Subject: [PATCH 024/232] Code Cleanup --- stardew-access/Patches/TitleMenuPatches.cs | 504 +++------------------ 1 file changed, 62 insertions(+), 442 deletions(-) diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index fed2f1e..eaae7c9 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -8,7 +8,6 @@ namespace stardew_access.Patches { private static int saveGameIndex = -1; private static bool isRunning = false; - private const int MAX_COMPONENTS = 20; internal static void CoopMenuPatch(CoopMenu __instance, CoopMenu.Tab ___currentTab) { @@ -87,7 +86,7 @@ namespace stardew_access.Patches if (__instance.windowedButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { - toSpeak = "Fullscreen: " + ((Game1.isFullscreen) ? "on" : "off"); + toSpeak = "Fullscreen: " + ((Game1.isFullscreen) ? "enabled" : "disabled"); } if (TitleMenu.subMenu != null && __instance.backButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) @@ -197,456 +196,77 @@ namespace stardew_access.Patches private static void CycleThroughItems(bool increase, CharacterCustomization __instance, bool ___skipIntro) { string toSpeak = " "; + Dictionary buttons = new(); + + #region Add buttons with their names IF they are available + if (__instance.nameBoxCC != null && __instance.nameBoxCC.visible) + buttons.Add(__instance.nameBoxCC, "Enter Farmer's Name"); + + if (__instance.farmnameBoxCC != null && __instance.farmnameBoxCC.visible) + buttons.Add(__instance.farmnameBoxCC, "Enter Farm's Name"); + + if (__instance.favThingBoxCC != null && __instance.favThingBoxCC.visible) + buttons.Add(__instance.favThingBoxCC, "Enter Favourite Thing"); + + if (__instance.skipIntroButton != null && __instance.skipIntroButton.visible) + buttons.Add(__instance.skipIntroButton, (___skipIntro ? "Enabled" : "Disabled") + " Skip Intro Button"); + + if (__instance.randomButton != null && __instance.randomButton.visible) + buttons.Add(__instance.randomButton, "Random Skin Button"); + + if (__instance.genderButtons.Count > 0) + { + buttons.Add(__instance.genderButtons[0], "Gender: Male Button"); + buttons.Add(__instance.genderButtons[1], "Gender: Female Button"); + } + + if (__instance.farmTypeButtons.Count > 0) + { + buttons.Add(__instance.farmTypeButtons[0], getFarmHoverText(__instance.farmTypeButtons[0])); + buttons.Add(__instance.farmTypeButtons[1], getFarmHoverText(__instance.farmTypeButtons[1])); + buttons.Add(__instance.farmTypeButtons[2], getFarmHoverText(__instance.farmTypeButtons[2])); + buttons.Add(__instance.farmTypeButtons[3], getFarmHoverText(__instance.farmTypeButtons[3])); + buttons.Add(__instance.farmTypeButtons[4], getFarmHoverText(__instance.farmTypeButtons[4])); + buttons.Add(__instance.farmTypeButtons[5], getFarmHoverText(__instance.farmTypeButtons[5])); + buttons.Add(__instance.farmTypeButtons[6], getFarmHoverText(__instance.farmTypeButtons[6])); + } + + if (__instance.farmTypeNextPageButton != null && __instance.farmTypeNextPageButton.visible) + buttons.Add(__instance.farmTypeNextPageButton, "Next Farm Type Page Button"); + + if (__instance.farmTypePreviousPageButton != null && __instance.farmTypePreviousPageButton.visible) + buttons.Add(__instance.farmTypePreviousPageButton, "Previous Farm Type Page Button"); + + if (__instance.cabinLayoutButtons.Count > 0) + { + buttons.Add(__instance.cabinLayoutButtons[0], "Cabin layout: nearby Button"); + buttons.Add(__instance.cabinLayoutButtons[1], "Cabin layout: separate Button"); + } + + if (__instance.okButton != null && __instance.okButton.visible) + buttons.Add(__instance.okButton, "OK Button"); + + if (__instance.backButton != null && __instance.backButton.visible) + buttons.Add(__instance.backButton, "Back Button"); + #endregion + + int size = buttons.Count - 1; if (increase) { saveGameIndex++; - if (saveGameIndex > MAX_COMPONENTS) - saveGameIndex = 1; + if (saveGameIndex > size) + saveGameIndex = 0; } else { saveGameIndex--; - if (saveGameIndex < 1) - saveGameIndex = MAX_COMPONENTS; + if (saveGameIndex < 0) + saveGameIndex = size; } - - switch (saveGameIndex) - { - case 1: - { - #region Skip if button is not available - if (!__instance.nameBoxCC.visible) - { - if (increase) - { - ++saveGameIndex; - goto case 2; - } - else - { - --saveGameIndex; - goto case MAX_COMPONENTS; - } - } - #endregion - - __instance.nameBoxCC.snapMouseCursorToCenter(); - toSpeak = "Enter Farmer's Name"; - } - break; - - case 2: - { - #region Skip if button is not available - if (!__instance.farmnameBoxCC.visible) - { - if (increase) - { - ++saveGameIndex; - goto case 3; - } - else - { - --saveGameIndex; - goto case 1; - } - } - #endregion - - __instance.farmnameBoxCC.snapMouseCursorToCenter(); - toSpeak = "Enter Farm's Name"; - } - break; - case 3: - { - #region Skip if button is not available - if (!__instance.favThingBoxCC.visible) - { - if (increase) - { - ++saveGameIndex; - goto case 4; - } - else - { - --saveGameIndex; - goto case 2; - } - } - #endregion - - __instance.favThingBoxCC.snapMouseCursorToCenter(); - toSpeak = "Enter Favourite Thing"; - } - break; - case 4: - { - #region Skip if button is not available - if (!__instance.skipIntroButton.visible) - { - if (increase) - { - ++saveGameIndex; - goto case 5; - } - else - { - --saveGameIndex; - goto case 3; - } - } - #endregion - - __instance.skipIntroButton.snapMouseCursorToCenter(); - toSpeak = (___skipIntro ? "Enabled" : "Disabled") + " Skip Intro Button"; - } - break; - case 5: - { - #region Skip if button is not available - if (!__instance.randomButton.visible) - { - if (increase) - { - ++saveGameIndex; - goto case 6; - } - else - { - --saveGameIndex; - goto case 5; - } - } - #endregion - - __instance.randomButton.snapMouseCursorToCenter(); - toSpeak = "Random Skin Button"; - break; - } - case 6: - { - #region Skip if button is not available - if (__instance.genderButtons.Count <= 0) - { - if (increase) - { - ++saveGameIndex; - goto case 8; - } - else - { - --saveGameIndex; - goto case 6; - } - } - #endregion - - __instance.genderButtons[0].snapMouseCursorToCenter(); - toSpeak = "Gender Male Button"; - break; - } - case 7: - { - #region Skip if button is not available - if (__instance.genderButtons.Count <= 0) - { - if (increase) - { - ++saveGameIndex; - goto case 8; - } - else - { - --saveGameIndex; - goto case 6; - } - } - #endregion - - __instance.genderButtons[1].snapMouseCursorToCenter(); - toSpeak = "Gender Female Button"; - break; - } - case 8: - { - #region Skip if button is not available - if (__instance.farmTypeButtons.Count <= 0) - { - if (increase) - { - ++saveGameIndex; - goto case 9; - } - else - { - --saveGameIndex; - goto case 7; - } - } - #endregion - - __instance.farmTypeButtons[0].snapMouseCursorToCenter(); - toSpeak = getFarmHoverText(__instance.farmTypeButtons[0]); - break; - } - case 9: - { - #region Skip if button is not available - if (__instance.farmTypeButtons.Count <= 0) - { - if (increase) - { - ++saveGameIndex; - goto case 10; - } - else - { - --saveGameIndex; - goto case 8; - } - } - #endregion - - __instance.farmTypeButtons[1].snapMouseCursorToCenter(); - toSpeak = getFarmHoverText(__instance.farmTypeButtons[1]); - break; - } - case 10: - { - #region Skip if button is not available - if (__instance.farmTypeButtons.Count <= 0) - { - if (increase) - { - ++saveGameIndex; - goto case 11; - } - else - { - --saveGameIndex; - goto case 9; - } - } - #endregion - - __instance.farmTypeButtons[2].snapMouseCursorToCenter(); - toSpeak = getFarmHoverText(__instance.farmTypeButtons[2]); - break; - } - case 11: - { - #region Skip if button is not available - if (__instance.farmTypeButtons.Count <= 0) - { - if (increase) - { - ++saveGameIndex; - goto case 12; - } - else - { - --saveGameIndex; - goto case 10; - } - } - #endregion - - __instance.farmTypeButtons[3].snapMouseCursorToCenter(); - toSpeak = getFarmHoverText(__instance.farmTypeButtons[3]); - break; - } - case 12: - { - #region Skip if button is not available - if (__instance.farmTypeButtons.Count <= 0) - { - if (increase) - { - ++saveGameIndex; - goto case 13; - } - else - { - --saveGameIndex; - goto case 11; - } - } - #endregion - - __instance.farmTypeButtons[4].snapMouseCursorToCenter(); - toSpeak = getFarmHoverText(__instance.farmTypeButtons[4]); - break; - } - case 13: - { - #region Skip if button is not available - if (__instance.farmTypeButtons.Count <= 0) - { - if (increase) - { - ++saveGameIndex; - goto case 14; - } - else - { - --saveGameIndex; - goto case 12; - } - } - #endregion - - __instance.farmTypeButtons[5].snapMouseCursorToCenter(); - toSpeak = getFarmHoverText(__instance.farmTypeButtons[5]); - break; - } - case 14: - { - #region Skip if button is not available - if (__instance.farmTypeButtons.Count <= 0) - { - if (increase) - { - ++saveGameIndex; - goto case 15; - } - else - { - --saveGameIndex; - goto case 13; - } - } - #endregion - - __instance.farmTypeButtons[6].snapMouseCursorToCenter(); - toSpeak = getFarmHoverText(__instance.farmTypeButtons[6]); - break; - } - case 15: - { - #region Skip if button is not available - if (__instance.farmTypeNextPageButton == null) - { - if (increase) - { - ++saveGameIndex; - goto case 16; - } - else - { - --saveGameIndex; - goto case 14; - } - } - #endregion - - __instance.farmTypeNextPageButton.snapMouseCursorToCenter(); - toSpeak = "Next Farm Type Page Button"; - break; - } - case 16: - { - #region Skip if button is not available - if (__instance.farmTypePreviousPageButton == null) - { - if (increase) - { - ++saveGameIndex; - goto case 17; - } - else - { - --saveGameIndex; - goto case 15; - } - } - #endregion - - __instance.farmTypePreviousPageButton.snapMouseCursorToCenter(); - toSpeak = "Previous Farm Type Page Button"; - break; - } - case 17: - { - #region Skip if button is not available - if (__instance.cabinLayoutButtons.Count <= 0) - { - if (increase) - { - ++saveGameIndex; - goto case 18; - } - else - { - --saveGameIndex; - goto case 16; - } - } - #endregion - - __instance.cabinLayoutButtons[0].snapMouseCursorToCenter(); - toSpeak = "Cabin layout nearby"; - break; - } - case 18: - { - #region Skip if button is not available - if (__instance.cabinLayoutButtons.Count <= 0) - { - if (increase) - { - ++saveGameIndex; - goto case 19; - } - else - { - --saveGameIndex; - goto case 17; - } - } - #endregion - - __instance.cabinLayoutButtons[1].snapMouseCursorToCenter(); - toSpeak = "Cabin layout separate"; - break; - } - case 19: - { - #region Skip if button is not available - if (!__instance.okButton.visible) - { - if (increase) - { - ++saveGameIndex; - goto case 18; - } - else - { - --saveGameIndex; - goto case 20; - } - } - #endregion - - __instance.okButton.snapMouseCursorToCenter(); - toSpeak = "Ok Button"; - } - break; - case 20: - { - #region Exit if button is not available - if (!__instance.backButton.visible) - { - break; - } - #endregion - - __instance.backButton.snapMouseCursorToCenter(); - toSpeak = "Back Button"; - } - break; - } + buttons.ElementAt(saveGameIndex).Key.snapMouseCursor(); + toSpeak = buttons.ElementAt(saveGameIndex).Value; if (toSpeak != " ") { From 7ecf7fb30c4ee5cf5c25e7e0cad792c36daf05eb Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 2 Apr 2022 15:49:18 +0530 Subject: [PATCH 025/232] Added pet to character customization menu --- stardew-access/Patches/TitleMenuPatches.cs | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index eaae7c9..fb6344d 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -1,5 +1,7 @@ using StardewValley; +using StardewValley.Characters; using StardewValley.Menus; +using static StardewValley.Menus.CharacterCustomization; using static StardewValley.Menus.LoadGameMenu; namespace stardew_access.Patches @@ -208,6 +210,15 @@ namespace stardew_access.Patches if (__instance.favThingBoxCC != null && __instance.favThingBoxCC.visible) buttons.Add(__instance.favThingBoxCC, "Enter Favourite Thing"); + if (__instance.petPortraitBox.HasValue) // Cannot get petButtons like with others + { + ClickableComponent petPrev = __instance.getComponentWithID(511); + buttons.Add(petPrev, "Previous pet: " + getPetName(-1, __instance.isModifyingExistingPet)); + + ClickableComponent petNext = __instance.getComponentWithID(510); + buttons.Add(petNext, "Next pet: " + getPetName(+1, __instance.isModifyingExistingPet)); + } + if (__instance.skipIntroButton != null && __instance.skipIntroButton.visible) buttons.Add(__instance.skipIntroButton, (___skipIntro ? "Enabled" : "Disabled") + " Skip Intro Button"); @@ -274,6 +285,29 @@ namespace stardew_access.Patches } } + private static string getPetName(int change, bool isModifyingExistingPet) + { + Game1.player.whichPetBreed += change; + if (Game1.player.whichPetBreed >= 3) + { + Game1.player.whichPetBreed = 0; + if (!isModifyingExistingPet) + { + Game1.player.catPerson = !Game1.player.catPerson; + } + } + else if (Game1.player.whichPetBreed < 0) + { + Game1.player.whichPetBreed = 2; + if (!isModifyingExistingPet) + { + Game1.player.catPerson = !Game1.player.catPerson; + } + } + + return ((Game1.player.catPerson) ? "Cat" : "Dog") + " Breed: " + Game1.player.whichPetBreed; + } + private static string getFarmHoverText(ClickableTextureComponent farm) { string hoverTitle = " ", hoverText = " "; From dc853a1d7cf1aa3c9c1823f2c68b85a577bf1996 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 2 Apr 2022 16:33:53 +0530 Subject: [PATCH 026/232] Added co-op related options to character customization menu --- stardew-access/Patches/TitleMenuPatches.cs | 51 +++++++++++++++++----- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index fb6344d..17da21d 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -160,7 +160,8 @@ namespace stardew_access.Patches } } - internal static void CharacterCustomizationMenuPatch(CharacterCustomization __instance, bool ___skipIntro) + internal static void CharacterCustomizationMenuPatch(CharacterCustomization __instance, bool ___skipIntro, + ClickableComponent ___startingCabinsLabel, ClickableComponent ___difficultyModifierLabel) { try { @@ -179,13 +180,13 @@ namespace stardew_access.Patches if (isNextArrowPressed && !isRunning) { isRunning = true; - CycleThroughItems(true, __instance, ___skipIntro); + CycleThroughItems(true, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel); Task.Delay(200).ContinueWith(_ => { isRunning = false; }); } else if (isPrevArrowPressed && !isRunning) { isRunning = true; - CycleThroughItems(false, __instance, ___skipIntro); + CycleThroughItems(false, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel); Task.Delay(200).ContinueWith(_ => { isRunning = false; }); } } @@ -195,12 +196,15 @@ namespace stardew_access.Patches } } - private static void CycleThroughItems(bool increase, CharacterCustomization __instance, bool ___skipIntro) + private static void CycleThroughItems(bool increase, CharacterCustomization __instance, bool ___skipIntro, + ClickableComponent ___startingCabinsLabel, ClickableComponent ___difficultyModifierLabel) { string toSpeak = " "; Dictionary buttons = new(); #region Add buttons with their names IF they are available + + #region Character related if (__instance.nameBoxCC != null && __instance.nameBoxCC.visible) buttons.Add(__instance.nameBoxCC, "Enter Farmer's Name"); @@ -219,9 +223,6 @@ namespace stardew_access.Patches buttons.Add(petNext, "Next pet: " + getPetName(+1, __instance.isModifyingExistingPet)); } - if (__instance.skipIntroButton != null && __instance.skipIntroButton.visible) - buttons.Add(__instance.skipIntroButton, (___skipIntro ? "Enabled" : "Disabled") + " Skip Intro Button"); - if (__instance.randomButton != null && __instance.randomButton.visible) buttons.Add(__instance.randomButton, "Random Skin Button"); @@ -230,7 +231,9 @@ namespace stardew_access.Patches buttons.Add(__instance.genderButtons[0], "Gender: Male Button"); buttons.Add(__instance.genderButtons[1], "Gender: Female Button"); } + #endregion + #region Farm layout related if (__instance.farmTypeButtons.Count > 0) { buttons.Add(__instance.farmTypeButtons[0], getFarmHoverText(__instance.farmTypeButtons[0])); @@ -247,12 +250,40 @@ namespace stardew_access.Patches if (__instance.farmTypePreviousPageButton != null && __instance.farmTypePreviousPageButton.visible) buttons.Add(__instance.farmTypePreviousPageButton, "Previous Farm Type Page Button"); + #endregion - if (__instance.cabinLayoutButtons.Count > 0) + #region Co-op related + if (__instance.source == Source.HostNewFarm) { - buttons.Add(__instance.cabinLayoutButtons[0], "Cabin layout: nearby Button"); - buttons.Add(__instance.cabinLayoutButtons[1], "Cabin layout: separate Button"); + ClickableComponent cabinLeft = __instance.getComponentWithID(621); + if (Game1.startingCabins > 0) + buttons.Add(cabinLeft, "Decrease starting cabins button"); + + buttons.Add(___startingCabinsLabel, $"Starting cabins: {Game1.startingCabins}"); + + ClickableComponent cabinRight = __instance.getComponentWithID(622); + if (Game1.startingCabins < 3) + buttons.Add(cabinRight, "Increase starting cabins button"); + + if (Game1.startingCabins > 0) + { + buttons.Add(__instance.cabinLayoutButtons[0], "Cabin layout to nearby Button"); + buttons.Add(__instance.cabinLayoutButtons[1], "Cabin layout to separate Button"); + } + + ClickableComponent difficultyLeft = __instance.getComponentWithID(627); + buttons.Add(difficultyLeft, "Increase profit margin button"); + buttons.Add(___difficultyModifierLabel, "Profit Margin: " + (((Game1.player.difficultyModifier * 100) == 100f) ? "normal" : Game1.player.difficultyModifier.ToString())); + ClickableComponent difficultyRight = __instance.getComponentWithID(628); + buttons.Add(difficultyRight, "Decrease profit margin button"); + + ClickableComponent walletLeft = __instance.getComponentWithID(631); + buttons.Add(walletLeft, "Money style to " + ((!Game1.player.team.useSeparateWallets.Value) ? "separate wallets" : "shared wallets") + " button"); } + #endregion + + if (__instance.skipIntroButton != null && __instance.skipIntroButton.visible) + buttons.Add(__instance.skipIntroButton, (___skipIntro ? "Enabled" : "Disabled") + " Skip Intro Button"); if (__instance.okButton != null && __instance.okButton.visible) buttons.Add(__instance.okButton, "OK Button"); From 7d18bbce44bc1e9976177253199db409a4820a05 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 2 Apr 2022 16:50:40 +0530 Subject: [PATCH 027/232] Fixed back button not working in character customization --- stardew-access/Patches/TitleMenuPatches.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index 17da21d..38d8f8f 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -97,6 +97,16 @@ namespace stardew_access.Patches MainClass.GetScreenReader().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.GetScreenReader().SayWithChecker(toSpeak, true); } @@ -170,11 +180,7 @@ namespace stardew_access.Patches if (__instance.backButton.containsPoint != null && __instance.backButton.visible && __instance.backButton.containsPoint((int)Game1.getMouseX(true), (int)Game1.getMouseY(true))) { - // Perform Left Click - if (MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) - { - Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); - } + } if (isNextArrowPressed && !isRunning) From 9fe91faeeef53b72111d1e748126e708684384ec Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 5 Apr 2022 12:17:28 +0530 Subject: [PATCH 028/232] Organized code --- stardew-access/HarmonyPatches.cs | 6 +- stardew-access/ModEntry.cs | 28 ++- stardew-access/Patches/DialoguePatches.cs | 111 ++++++++-- stardew-access/Patches/MenuPatches.cs | 233 ++------------------ stardew-access/Patches/MuseumMenuPatches.cs | 146 ++++++++++++ stardew-access/Patches/QuestPatches.cs | 7 +- 6 files changed, 279 insertions(+), 252 deletions(-) create mode 100644 stardew-access/Patches/MuseumMenuPatches.cs diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index a145139..1194110 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -106,7 +106,7 @@ namespace stardew_access #region Menu Patches harmony.Patch( original: AccessTools.Method(typeof(LetterViewerMenu), nameof(LetterViewerMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.LetterViewerMenuPatch)) + postfix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.LetterViewerMenuPatch)) ); harmony.Patch( @@ -141,12 +141,12 @@ namespace stardew_access harmony.Patch( original: AccessTools.Method(typeof(MuseumMenu), nameof(MuseumMenu.receiveKeyPress), new Type[] { typeof(Keys) }), - prefix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.MuseumMenuKeyPressPatch)) + prefix: new HarmonyMethod(typeof(MuseumMenuPatches), nameof(MuseumMenuPatches.MuseumMenuKeyPressPatch)) ); harmony.Patch( original: AccessTools.Method(typeof(MuseumMenu), nameof(MuseumMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.MuseumMenuPatch)) + postfix: new HarmonyMethod(typeof(MuseumMenuPatches), nameof(MuseumMenuPatches.MuseumMenuPatch)) ); #endregion diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index c982f3c..2e3c61a 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -15,14 +15,22 @@ namespace stardew_access #region Global Vars private static ModConfig config; private Harmony? harmony; - private static IMonitor monitor; - private static Radar radarFeature; + private static IMonitor? monitor; + private static Radar? radarFeature; private static IScreenReader? screenReader; private static IModHelper modHelper; internal static ModConfig Config { get => config; set => config = value; } public static IModHelper ModHelper { get => modHelper; } - public static Radar RadarFeature { get => radarFeature; set => radarFeature = value; } + public static Radar RadarFeature + { + get + { + if (radarFeature == null) { radarFeature = new Radar(); } + return radarFeature; + } + set => radarFeature = value; + } public static string hudMessageQueryKey = ""; public static bool isNarratingHudMessage = false; @@ -67,8 +75,6 @@ namespace stardew_access CustomCommands.Initialize(); - RadarFeature = new Radar(); - harmony = new Harmony(ModManifest.UniqueID); HarmonyPatches.Initialize(harmony); @@ -108,12 +114,10 @@ namespace stardew_access if (!Context.IsPlayerFree) return; - // Reset variables - MenuPatches.resetGlobalVars(); - QuestPatches.resetGlobalVars(); - + // Narrates currently selected inventory slot Other.narrateCurrentSlot(); + // Narrate current location's name Other.narrateCurrentLocation(); if (Config.SnapMouse) @@ -246,11 +250,17 @@ namespace stardew_access public static void ErrorLog(string message) { + if (monitor == null) + return; + monitor.Log(message, LogLevel.Error); } public static void DebugLog(string message) { + if (monitor == null) + return; + monitor.Log(message, LogLevel.Debug); } } diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index 437ddaa..99d8450 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -8,6 +8,7 @@ namespace stardew_access.Patches { internal class DialoguePatches { + internal static string currentLetterText = " "; internal static string currentDialogue = " "; internal static bool isDialogueAppearingFirstTime = true; @@ -192,20 +193,20 @@ namespace stardew_access.Patches return; #endregion - StringBuilder toSpeak = new StringBuilder(" "); + string toSpeak = " "; #region Add item count before title if (hoveredItem != null && hoveredItem.HasBeenInInventory) { int count = hoveredItem.Stack; if (count > 1) - toSpeak.Append($"{count} "); + toSpeak = $"{toSpeak} {count} "; } #endregion #region Add title if any if (boldTitleText != null) - toSpeak.Append($"{boldTitleText}\n"); + toSpeak = $"{toSpeak} {boldTitleText}\n"; #endregion #region Add quality of item @@ -214,15 +215,15 @@ namespace stardew_access.Patches int quality = ((StardewValley.Object)hoveredItem).quality; if (quality == 1) { - toSpeak.Append("Silver quality"); + toSpeak = $"{toSpeak} Silver quality"; } else if (quality == 2 || quality == 3) { - toSpeak.Append("Gold quality"); + toSpeak = $"{toSpeak} Gold quality"; } else if (quality >= 4) { - toSpeak.Append("Iridium quality"); + toSpeak = $"{toSpeak} Iridium quality"; } } #endregion @@ -233,26 +234,29 @@ namespace stardew_access.Patches string itemName = Game1.objectInformation[extraItemToShowIndex].Split('/')[0]; if (extraItemToShowAmount != -1) - toSpeak.Append($"Required: {extraItemToShowAmount} {itemName}"); + toSpeak = $"{toSpeak} Required: {extraItemToShowAmount} {itemName}"; else - toSpeak.Append($"Required: {itemName}"); + toSpeak = $"{toSpeak} Required: {itemName}"; } #endregion #region Add money if (moneyAmountToDisplayAtBottom != -1) - toSpeak.Append($"\nCost: {moneyAmountToDisplayAtBottom}g\n"); + toSpeak = $"{toSpeak} \nCost: {moneyAmountToDisplayAtBottom}g\n"; #endregion #region Add the base text - toSpeak.Append(text); + if (text == "???") + toSpeak = "unknown"; + else + toSpeak = $"{toSpeak} {text}"; #endregion #region Add crafting ingredients if (craftingIngredients != null) { - toSpeak.Append($"\n{craftingIngredients.description}"); - toSpeak.Append("\nIngredients\n"); + toSpeak = $"{toSpeak} \n{craftingIngredients.description}"; + toSpeak = $"{toSpeak} \nIngredients\n"; craftingIngredients.recipeList.ToList().ForEach(recipe => { @@ -260,7 +264,7 @@ namespace stardew_access.Patches int item = recipe.Key; string name = craftingIngredients.getNameFromIndex(item); - toSpeak.Append($" ,{count} {name}"); + toSpeak = $"{toSpeak} ,{count} {name}"; }); } #endregion @@ -269,11 +273,11 @@ namespace stardew_access.Patches if (hoveredItem is StardewValley.Object && ((StardewValley.Object)hoveredItem).Edibility != -300) { int stamina_recovery = ((StardewValley.Object)hoveredItem).staminaRecoveredOnConsumption(); - toSpeak.Append($"{stamina_recovery} Energy\n"); + toSpeak = $"{toSpeak} {stamina_recovery} Energy\n"; if (stamina_recovery >= 0) { int health_recovery = ((StardewValley.Object)hoveredItem).healthRecoveredOnConsumption(); - toSpeak.Append($"{health_recovery} Health"); + toSpeak = $"{toSpeak} {health_recovery} Health"; } } #endregion @@ -292,7 +296,7 @@ namespace stardew_access.Patches { int count = int.Parse(buffName.Substring(0, buffName.IndexOf(' '))); if (count != 0) - toSpeak.Append($"{buffName}\n"); + toSpeak = $"{toSpeak} {buffName}\n"; } catch (Exception) { } } @@ -317,5 +321,80 @@ namespace stardew_access.Patches MainClass.ErrorLog($"Unable to narrate dialog:\n{e.StackTrace}\n{e.Message}"); } } + + + internal static void LetterViewerMenuPatch(LetterViewerMenu __instance) + { + try + { + if (!__instance.IsActive()) + return; + + 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.GetScreenReader().Say(toSpeak, false); + } + #endregion + + #region Narrate items given in the mail + if (__instance.ShouldShowInteractable()) + { + foreach (ClickableComponent c in __instance.itemsToGrab) + { + string name = c.name; + string label = c.label; + + if (c.containsPoint(x, y)) + MainClass.GetScreenReader().SayWithChecker($"Grab: {name} \t\n {label}", false); + } + } + #endregion + + #region Narrate buttons + if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y)) + MainClass.GetScreenReader().SayWithChecker($"Previous page button", false); + + if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y)) + MainClass.GetScreenReader().SayWithChecker($"Next page button", false); + + #endregion + } + catch (Exception e) + { + + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } } } diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 40180dc..27c559c 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -10,147 +10,8 @@ namespace stardew_access.Patches { internal class MenuPatches { - private static string currentLetterText = " "; - private static string museumQueryKey = " "; private static string currentLevelUpTitle = " "; public static Vector2? prevTile = null; - private static bool isMoving = false; - - #region Museum Menu Patch - 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.GetScreenReader().Say(toSpeak, true); - } - } - else - { - // Player Inventory - if (!narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) - { - if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - { - if (museumQueryKey != $"ok button") - { - museumQueryKey = $"ok button"; - MainClass.GetScreenReader().Say("ok button", true); - } - } - } - } - } - catch (Exception e) - { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - #endregion - - internal static bool narrateHoveredItemInInventory(InventoryMenu inventoryMenu, List inventory, IList actualInventory, int x, int y) - { - #region Narrate hovered item - for (int i = 0; i < inventory.Count; i++) - { - if (inventory[i].containsPoint(x, y)) - { - string toSpeak = ""; - if ((i + 1) <= actualInventory.Count) - { - if (actualInventory[i] != null) - { - string name = actualInventory[i].DisplayName; - int stack = actualInventory[i].Stack; - string quality = ""; - - #region Add quality of item - if (actualInventory[i] is StardewValley.Object && ((StardewValley.Object)actualInventory[i]).quality > 0) - { - int qualityIndex = ((StardewValley.Object)actualInventory[i]).quality; - if (qualityIndex == 1) - { - quality = "Silver quality"; - } - else if (qualityIndex == 2 || qualityIndex == 3) - { - quality = "Gold quality"; - } - else if (qualityIndex >= 4) - { - quality = "Iridium quality"; - } - } - #endregion - - if (inventoryMenu.highlightMethod(inventoryMenu.actualInventory[i])) - name = $"Donatable {name}"; - - if (stack > 1) - toSpeak = $"{stack} {name} {quality}"; - else - toSpeak = $"{name} {quality}"; - } - else - { - // For empty slot - toSpeak = "Empty Slot"; - } - } - else - { - // For empty slot - toSpeak = "Empty Slot"; - } - - if (museumQueryKey != $"{toSpeak}:{i}") - { - museumQueryKey = $"{toSpeak}:{i}"; - MainClass.GetScreenReader().Say(toSpeak, true); - } - return true; - } - } - #endregion - return false; - } internal static bool PlaySoundPatch(string cueName) { @@ -407,80 +268,6 @@ namespace stardew_access.Patches } } - internal static void LetterViewerMenuPatch(LetterViewerMenu __instance) - { - try - { - if (!__instance.IsActive()) - return; - - 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.GetScreenReader().Say(toSpeak, false); - } - #endregion - - #region Narrate items given in the mail - if (__instance.ShouldShowInteractable()) - { - foreach (ClickableComponent c in __instance.itemsToGrab) - { - string name = c.name; - string label = c.label; - - if (c.containsPoint(x, y)) - MainClass.GetScreenReader().SayWithChecker($"Grab: {name} \t\n {label}", false); - } - } - #endregion - - #region Narrate buttons - if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y)) - MainClass.GetScreenReader().SayWithChecker($"Previous page button", false); - - if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y)) - MainClass.GetScreenReader().SayWithChecker($"Next page button", false); - - #endregion - } - catch (Exception e) - { - - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); - } - } - #region Cleanup on exitting a menu internal static void Game1ExitActiveMenuPatch() { @@ -508,6 +295,21 @@ namespace stardew_access.Patches private static void Cleanup(IClickableMenu menu) { + if (menu is LetterViewerMenu) + { + DialoguePatches.currentLetterText = " "; + } + + if (menu is LevelUpMenu) + { + currentLevelUpTitle = " "; + } + + if (menu is Billboard) + { + QuestPatches.currentDailyQuestText = " "; + } + if (menu is GameMenu) { GameMenuPatches.gameMenuQueryKey = ""; @@ -576,10 +378,5 @@ namespace stardew_access.Patches if (MainClass.GetScreenReader() != null) MainClass.GetScreenReader().CloseScreenReader(); } - internal static void resetGlobalVars() - { - currentLetterText = " "; - currentLevelUpTitle = " "; - } } } diff --git a/stardew-access/Patches/MuseumMenuPatches.cs b/stardew-access/Patches/MuseumMenuPatches.cs new file mode 100644 index 0000000..3949de2 --- /dev/null +++ b/stardew-access/Patches/MuseumMenuPatches.cs @@ -0,0 +1,146 @@ +using StardewValley; +using StardewValley.Locations; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class MuseumMenuPatches + { + private static string museumQueryKey = " "; + private static bool isMoving = false; + + 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.GetScreenReader().Say(toSpeak, true); + } + } + else + { + // Player Inventory + if (!narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) + { + if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + { + if (museumQueryKey != $"ok button") + { + museumQueryKey = $"ok button"; + MainClass.GetScreenReader().Say("ok button", true); + } + } + } + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + internal static bool narrateHoveredItemInInventory(InventoryMenu inventoryMenu, List inventory, IList actualInventory, int x, int y) + { + #region Narrate hovered item + for (int i = 0; i < inventory.Count; i++) + { + if (inventory[i].containsPoint(x, y)) + { + string toSpeak = ""; + if ((i + 1) <= actualInventory.Count) + { + if (actualInventory[i] != null) + { + string name = actualInventory[i].DisplayName; + int stack = actualInventory[i].Stack; + string quality = ""; + + #region Add quality of item + if (actualInventory[i] is StardewValley.Object && ((StardewValley.Object)actualInventory[i]).quality > 0) + { + int qualityIndex = ((StardewValley.Object)actualInventory[i]).quality; + if (qualityIndex == 1) + { + quality = "Silver quality"; + } + else if (qualityIndex == 2 || qualityIndex == 3) + { + quality = "Gold quality"; + } + else if (qualityIndex >= 4) + { + quality = "Iridium quality"; + } + } + #endregion + + if (inventoryMenu.highlightMethod(inventoryMenu.actualInventory[i])) + name = $"Donatable {name}"; + + if (stack > 1) + toSpeak = $"{stack} {name} {quality}"; + else + toSpeak = $"{name} {quality}"; + } + else + { + // For empty slot + toSpeak = "Empty Slot"; + } + } + else + { + // For empty slot + toSpeak = "Empty Slot"; + } + + if (museumQueryKey != $"{toSpeak}:{i}") + { + museumQueryKey = $"{toSpeak}:{i}"; + MainClass.GetScreenReader().Say(toSpeak, true); + } + return true; + } + } + #endregion + return false; + } + } +} \ No newline at end of file diff --git a/stardew-access/Patches/QuestPatches.cs b/stardew-access/Patches/QuestPatches.cs index 4d1b48c..1bd8303 100644 --- a/stardew-access/Patches/QuestPatches.cs +++ b/stardew-access/Patches/QuestPatches.cs @@ -8,7 +8,7 @@ namespace stardew_access.Patches { internal class QuestPatches { - private static string currentDailyQuestText = " "; + internal static string currentDailyQuestText = " "; #region For Special Orders Board internal static void SpecialOrdersBoardPatch(SpecialOrdersBoard __instance) @@ -243,10 +243,5 @@ namespace stardew_access.Patches } #endregion - - internal static void resetGlobalVars() - { - currentDailyQuestText = " "; - } } } From 53f7e3ceb044d2fa8991d7930c7d4aa3e0624498 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 5 Apr 2022 12:44:32 +0530 Subject: [PATCH 029/232] Patched collections page's letter viewer menu --- stardew-access/HarmonyPatches.cs | 5 + stardew-access/Patches/DialoguePatches.cs | 123 +++++++++++----------- stardew-access/Patches/GameMenuPatches.cs | 18 ++++ 3 files changed, 87 insertions(+), 59 deletions(-) diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index 1194110..83c1223 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -101,6 +101,11 @@ namespace stardew_access original: AccessTools.Method(typeof(JunimoNoteMenu), nameof(JunimoNoteMenu.draw), new Type[] { typeof(SpriteBatch) }), postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.JunimoNoteMenuPatch)) ); + + harmony.Patch( + original: AccessTools.Method(typeof(CollectionsPage), nameof(CollectionsPage.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.CollectionsPagePatch)) + ); #endregion #region Menu Patches diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index 99d8450..21ad188 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -330,65 +330,7 @@ namespace stardew_access.Patches if (!__instance.IsActive()) return; - 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.GetScreenReader().Say(toSpeak, false); - } - #endregion - - #region Narrate items given in the mail - if (__instance.ShouldShowInteractable()) - { - foreach (ClickableComponent c in __instance.itemsToGrab) - { - string name = c.name; - string label = c.label; - - if (c.containsPoint(x, y)) - MainClass.GetScreenReader().SayWithChecker($"Grab: {name} \t\n {label}", false); - } - } - #endregion - - #region Narrate buttons - if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y)) - MainClass.GetScreenReader().SayWithChecker($"Previous page button", false); - - if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y)) - MainClass.GetScreenReader().SayWithChecker($"Next page button", false); - - #endregion + NarrateLetterContent(__instance); } catch (Exception e) { @@ -396,5 +338,68 @@ namespace stardew_access.Patches 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.GetScreenReader().Say(toSpeak, true); + } + #endregion + + #region Narrate items given in the mail + if (__instance.ShouldShowInteractable()) + { + foreach (ClickableComponent c in __instance.itemsToGrab) + { + string name = c.name; + string label = c.label; + + if (c.containsPoint(x, y)) + MainClass.GetScreenReader().SayWithChecker($"Grab: {name} \t\n {label}", false); + } + } + #endregion + + #region Narrate buttons + if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y)) + MainClass.GetScreenReader().SayWithChecker($"Previous page button", false); + + if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y)) + MainClass.GetScreenReader().SayWithChecker($"Next page button", false); + + #endregion + } } } diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index 90481a7..062e241 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -5,6 +5,7 @@ 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 = ""; @@ -19,11 +20,28 @@ namespace stardew_access.Patches internal static string socialPageQuery = ""; internal static string profilePageQuery = ""; internal static string junimoNoteMenuQuery = ""; + internal static string collectionsPageQuery = ""; internal static int currentSelectedCraftingRecipe = -1; internal static bool isSelectingRecipe = false; internal static bool isUsingCustomButtons = false; internal static int currentIngredientListItem = -1, currentIngredientInputSlot = -1, currentInventorySlot = -1; + 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 JunimoNoteMenuPatch(JunimoNoteMenu __instance, bool ___specificBundlePage, int ___whichArea, Bundle ___currentPageBundle) { try From 041afa68a0aa8efef8353f6b70622783ebf1f6f0 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 5 Apr 2022 13:13:11 +0530 Subject: [PATCH 030/232] Added check for unknown recipe in crafting/cooking menu --- stardew-access/Patches/DialoguePatches.cs | 3 +++ stardew-access/Patches/GameMenuPatches.cs | 14 +++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index 21ad188..37ecb6d 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -191,6 +191,9 @@ namespace stardew_access.Patches if (Game1.activeClickableMenu is PurchaseAnimalsMenu) return; + + if (Game1.activeClickableMenu is CraftingPage) + return; #endregion string toSpeak = " "; diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index 062e241..742171e 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -1031,7 +1031,19 @@ namespace stardew_access.Patches #endregion #region Narrate hovered recipe - if (___hoverRecipe != null) + if (___hoverRecipe == null) + { + string query = $"unknown recipe:{__instance.getCurrentlySnappedComponent().myID}"; + + if (craftingPageQueryKey != query) + { + craftingPageQueryKey = query; + gameMenuQueryKey = ""; + hoveredItemQueryKey = ""; + MainClass.GetScreenReader().Say("unknown recipe", true); + } + } + else { string name = ___hoverRecipe.DisplayName; int numberOfProduce = ___hoverRecipe.numberProducedPerCraft; From afc54a0eeff16254c7e79e24d4f1d8bdd6a2ad1e Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 5 Apr 2022 17:21:35 +0530 Subject: [PATCH 031/232] Patched Animal Query Menu --- stardew-access/Features/ReadTile.cs | 2 +- stardew-access/HarmonyPatches.cs | 5 ++ stardew-access/ModEntry.cs | 4 +- stardew-access/Patches/DialoguePatches.cs | 3 + stardew-access/Patches/GameMenuPatches.cs | 6 +- stardew-access/Patches/MenuPatches.cs | 69 +++++++++++++++++++++++ stardew-access/manifest.json | 2 +- stardew-access/stardew-access.csproj | 3 +- 8 files changed, 86 insertions(+), 8 deletions(-) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index c02fcb1..3771d85 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -859,7 +859,7 @@ namespace stardew_access.Features { if (Game1.currentLocation.resourceClumps[i].occupiesTile(x, y)) { - int index = Game1.currentLocation.resourceClumps[i].parentSheetIndex; + int index = Game1.currentLocation.resourceClumps[i].parentSheetIndex.Value; switch (index) { diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index 83c1223..b78dd19 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -153,6 +153,11 @@ namespace stardew_access original: AccessTools.Method(typeof(MuseumMenu), nameof(MuseumMenu.draw), new Type[] { typeof(SpriteBatch) }), postfix: new HarmonyMethod(typeof(MuseumMenuPatches), nameof(MuseumMenuPatches.MuseumMenuPatch)) ); + + harmony.Patch( + original: AccessTools.Method(typeof(AnimalQueryMenu), nameof(AnimalQueryMenu.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.AnimalQueryMenuPatch)) + ); #endregion #region Quest Patches diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 2e3c61a..8cbb62e 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -170,11 +170,11 @@ namespace stardew_access } // Alternate Keybinds - if (!isCustomizingChrachter && Config.LeftClickAlternateKey.JustPressed()) // Excluding the character creation menu + if (!isCustomizingChrachter && Game1.activeClickableMenu is not AnimalQueryMenu && Config.LeftClickAlternateKey.JustPressed()) // Excluding the character creation menu { Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); } - if (!isCustomizingChrachter && Config.RightClickAlternateKey.JustPressed()) // Excluding the character creation menu + if (!isCustomizingChrachter && Game1.activeClickableMenu is not AnimalQueryMenu && Config.RightClickAlternateKey.JustPressed()) // Excluding the character creation menu { Game1.activeClickableMenu.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true)); } diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index 37ecb6d..a253d84 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -194,6 +194,9 @@ namespace stardew_access.Patches if (Game1.activeClickableMenu is CraftingPage) return; + + if (Game1.activeClickableMenu is AnimalQueryMenu) + return; #endregion string toSpeak = " "; diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index 742171e..34edd8a 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -1081,8 +1081,8 @@ namespace stardew_access.Patches } // These variables are taken from the game's code itself (IClickableMenu.cs -> 1016 line) bool edibleItem = producesItem != null && producesItem is StardewValley.Object && (int)((StardewValley.Object)producesItem).edibility != -300; - string[]? buffIconsToDisplay = (edibleItem && Game1.objectInformation[((StardewValley.Object)producesItem).parentSheetIndex].Split('/').Length > 7) - ? producesItem.ModifyItemBuffs(Game1.objectInformation[((StardewValley.Object)producesItem).parentSheetIndex].Split('/')[7].Split(' ')) + string[]? buffIconsToDisplay = (edibleItem && Game1.objectInformation[((StardewValley.Object)producesItem).ParentSheetIndex].Split('/').Length > 7) + ? producesItem.ModifyItemBuffs(Game1.objectInformation[((StardewValley.Object)producesItem).ParentSheetIndex].Split('/')[7].Split(' ')) : null; if (buffIconsToDisplay != null) @@ -1496,7 +1496,7 @@ namespace stardew_access.Patches #region Add buff items (effects like +1 walking speed) // These variables are taken from the game's code itself (IClickableMenu.cs -> 1016 line) bool edibleItem = actualInventory[i] != null && actualInventory[i] is StardewValley.Object && (int)((StardewValley.Object)actualInventory[i]).edibility != -300; - string[]? buffIconsToDisplay = (edibleItem && Game1.objectInformation[((StardewValley.Object)actualInventory[i]).parentSheetIndex].Split('/').Length > 7) ? actualInventory[i].ModifyItemBuffs(Game1.objectInformation[((StardewValley.Object)actualInventory[i]).parentSheetIndex].Split('/')[7].Split(' ')) : null; + string[]? buffIconsToDisplay = (edibleItem && Game1.objectInformation[((StardewValley.Object)actualInventory[i]).ParentSheetIndex].Split('/').Length > 7) ? actualInventory[i].ModifyItemBuffs(Game1.objectInformation[((StardewValley.Object)actualInventory[i]).ParentSheetIndex].Split('/')[7].Split(' ')) : null; if (buffIconsToDisplay != null) { for (int j = 0; j < buffIconsToDisplay.Length; j++) diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 27c559c..6c4c1c0 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -11,8 +11,77 @@ namespace stardew_access.Patches internal class MenuPatches { private static string currentLevelUpTitle = " "; + private static string animalQueryMenuQuery = " "; public static Vector2? prevTile = null; + internal static void AnimalQueryMenuPatch(AnimalQueryMenu __instance, bool ___confirmingSell, FarmAnimal ___animal, TextBox ___textBox, string ___parentName) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); // For narrating animal details + bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box + string toSpeak = " ", details = " "; + + if (___textBox.Selected) + { + toSpeak = ___textBox.Text; + + if (isEscPressed) + { + ___textBox.Selected = false; + } + } + else + { + if (isCPressed) + { + 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 < (byte)___animal.ageWhenMature) + { + 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 = " "; + } + + 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.GetScreenReader().Say($"{details} {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 diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index 27e0f9d..8cac695 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.1.3", + "Version": "1.1.4", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", diff --git a/stardew-access/stardew-access.csproj b/stardew-access/stardew-access.csproj index b723494..b3ab566 100644 --- a/stardew-access/stardew-access.csproj +++ b/stardew-access/stardew-access.csproj @@ -7,7 +7,8 @@ enable preview AnyCPU - true + + From 025956f66ca51621ebc7f66d09e78f9f19ec7596 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 5 Apr 2022 17:37:09 +0530 Subject: [PATCH 032/232] Added recipe skipping when pressing c if it is not unlocked --- stardew-access/Patches/GameMenuPatches.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index 34edd8a..fc4c107 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -1144,6 +1144,10 @@ namespace stardew_access.Patches __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 From 8bd44fe360db69a0f361251837bbfef7f8c48406 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Fri, 8 Apr 2022 15:50:57 +0530 Subject: [PATCH 033/232] Fixed bug in crafting/cooking menu --- stardew-access/Patches/GameMenuPatches.cs | 40 +++++++++++++++-------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index fc4c107..b6dfaa4 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -1031,19 +1031,7 @@ namespace stardew_access.Patches #endregion #region Narrate hovered recipe - if (___hoverRecipe == null) - { - string query = $"unknown recipe:{__instance.getCurrentlySnappedComponent().myID}"; - - if (craftingPageQueryKey != query) - { - craftingPageQueryKey = query; - gameMenuQueryKey = ""; - hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say("unknown recipe", true); - } - } - else + if (___hoverRecipe != null) { string name = ___hoverRecipe.DisplayName; int numberOfProduce = ___hoverRecipe.numberProducedPerCraft; @@ -1119,6 +1107,32 @@ namespace stardew_access.Patches } 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.GetScreenReader().Say("unknown recipe", true); + } + return; + } + } #endregion #region Narrate hovered item From 6c7b845f15d02d68af7bb0708af1e044b14f865c Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Fri, 8 Apr 2022 17:07:32 +0530 Subject: [PATCH 034/232] Added keybind to donate item to museum --- .../Patches/BuildingNAnimalMenuPatches.cs | 2 +- stardew-access/Patches/DialoguePatches.cs | 4 +- stardew-access/Patches/GameMenuPatches.cs | 6 +- stardew-access/Patches/MuseumMenuPatches.cs | 153 ++++++++++++++++-- 4 files changed, 149 insertions(+), 16 deletions(-) diff --git a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs index 409b705..1a90347 100644 --- a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs +++ b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs @@ -143,7 +143,7 @@ namespace stardew_access.Patches int itemStack = ___ingredients[i].Stack; string itemQuality = ""; - int qualityValue = ((StardewValley.Object)___ingredients[i]).quality; + int qualityValue = ((StardewValley.Object)___ingredients[i]).Quality; if (qualityValue == 1) { itemQuality = "Silver quality"; diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index a253d84..9eaab5b 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -216,9 +216,9 @@ namespace stardew_access.Patches #endregion #region Add quality of item - if (hoveredItem is StardewValley.Object && ((StardewValley.Object)hoveredItem).quality > 0) + if (hoveredItem is StardewValley.Object && ((StardewValley.Object)hoveredItem).Quality > 0) { - int quality = ((StardewValley.Object)hoveredItem).quality; + int quality = ((StardewValley.Object)hoveredItem).Quality; if (quality == 1) { toSpeak = $"{toSpeak} Silver quality"; diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index b6dfaa4..a50aef0 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -281,7 +281,7 @@ namespace stardew_access.Patches if ((item as StardewValley.Object) != null) { - int quality = ((StardewValley.Object)item).quality; + int quality = ((StardewValley.Object)item).Quality; if (quality == 1) { toSpeak = $"Silver quality {toSpeak}"; @@ -1477,9 +1477,9 @@ namespace stardew_access.Patches string requirements = ""; #region Add quality of item - if (actualInventory[i] is StardewValley.Object && ((StardewValley.Object)actualInventory[i]).quality > 0) + if (actualInventory[i] is StardewValley.Object && ((StardewValley.Object)actualInventory[i]).Quality > 0) { - int qualityIndex = ((StardewValley.Object)actualInventory[i]).quality; + int qualityIndex = ((StardewValley.Object)actualInventory[i]).Quality; if (qualityIndex == 1) { quality = "Silver quality"; diff --git a/stardew-access/Patches/MuseumMenuPatches.cs b/stardew-access/Patches/MuseumMenuPatches.cs index 3949de2..e472b77 100644 --- a/stardew-access/Patches/MuseumMenuPatches.cs +++ b/stardew-access/Patches/MuseumMenuPatches.cs @@ -1,3 +1,6 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Content; +using StardewModdingAPI; using StardewValley; using StardewValley.Locations; using StardewValley.Menus; @@ -8,6 +11,23 @@ namespace stardew_access.Patches { 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 MuseumMenuKeyPressPatch() { @@ -57,17 +77,67 @@ namespace stardew_access.Patches else { // Player Inventory - if (!narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) + int i = narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y); + if (i != -9999) { - if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) + bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); // For donating hovered item + + if (isCPressed && __instance.inventory.actualInventory[i] != null) { - if (museumQueryKey != $"ok button") + foreach (var tile in donationTiles) { - museumQueryKey = $"ok button"; - MainClass.GetScreenReader().Say("ok button", true); + #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); + break; + case 40: + globalChatInfoMessage("Museum40", Game1.player.farmName); + 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.GetScreenReader().Say("ok button", true); + } + } } } catch (Exception e) @@ -76,7 +146,8 @@ namespace stardew_access.Patches } } - internal static bool narrateHoveredItemInInventory(InventoryMenu inventoryMenu, List inventory, IList actualInventory, int x, int y) + // Returns the index of the hovered item or -9999 + internal static int narrateHoveredItemInInventory(InventoryMenu inventoryMenu, List inventory, IList actualInventory, int x, int y) { #region Narrate hovered item for (int i = 0; i < inventory.Count; i++) @@ -93,9 +164,9 @@ namespace stardew_access.Patches string quality = ""; #region Add quality of item - if (actualInventory[i] is StardewValley.Object && ((StardewValley.Object)actualInventory[i]).quality > 0) + if (actualInventory[i] is StardewValley.Object && ((StardewValley.Object)actualInventory[i]).Quality > 0) { - int qualityIndex = ((StardewValley.Object)actualInventory[i]).quality; + int qualityIndex = ((StardewValley.Object)actualInventory[i]).Quality; if (qualityIndex == 1) { quality = "Silver quality"; @@ -136,11 +207,73 @@ namespace stardew_access.Patches museumQueryKey = $"{toSpeak}:{i}"; MainClass.GetScreenReader().Say(toSpeak, true); } - return true; + return i; } } #endregion - return false; + return -9999; } + + #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 } } \ No newline at end of file From 416b573f4301ae40218679cbc4cb2863a6a226fa Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 9 Apr 2022 15:15:50 +0530 Subject: [PATCH 035/232] Fixed naming menu --- stardew-access/Features/ReadTile.cs | 3 ++ stardew-access/HarmonyPatches.cs | 7 ++- stardew-access/ModEntry.cs | 16 +++---- stardew-access/Patches/GameMenuPatches.cs | 4 +- stardew-access/Patches/MenuPatches.cs | 57 +++++++++++++++++++++-- 5 files changed, 71 insertions(+), 16 deletions(-) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 3771d85..b90919a 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -571,6 +571,9 @@ namespace stardew_access.Features case "pine cone": treeName = "Pine"; break; + default: + treeName = "Coconut"; + break; } if (treeStage == 1) diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index b78dd19..37e225b 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -130,7 +130,12 @@ namespace stardew_access ); harmony.Patch( - original: AccessTools.Constructor(typeof(NamingMenu), new Type[] { typeof(NamingMenu.doneNamingBehavior), typeof(string), typeof(string) }), + original: AccessTools.Method(typeof(TitleTextInputMenu), nameof(TitleTextInputMenu.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.TitleTextInputMenuPatch)) + ); + + harmony.Patch( + original: AccessTools.Method(typeof(NamingMenu), nameof(NamingMenu.draw), new Type[] { typeof(SpriteBatch) }), postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.NamingMenuPatch)) ); diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 8cbb62e..8c344be 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -184,6 +184,14 @@ namespace stardew_access if (!Context.IsPlayerFree) return; + // Narrate Current Location + if (Config.LocationKey.JustPressed()) + { + string toSpeak = $"{Game1.currentLocation.Name}"; + MainClass.GetScreenReader().Say(toSpeak, true); + return; + } + // Narrate Position if (Config.PositionKey.JustPressed()) { @@ -209,14 +217,6 @@ namespace stardew_access return; } - // Narrate Current Location - if (Config.LocationKey.JustPressed()) - { - string toSpeak = $"{Game1.currentLocation.Name}"; - MainClass.GetScreenReader().Say(toSpeak, true); - return; - } - // Narrate money at hand if (Config.MoneyKey.JustPressed()) { diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index a50aef0..3c258f6 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -1068,7 +1068,7 @@ namespace stardew_access.Patches } } // These variables are taken from the game's code itself (IClickableMenu.cs -> 1016 line) - bool edibleItem = producesItem != null && producesItem is StardewValley.Object && (int)((StardewValley.Object)producesItem).edibility != -300; + bool edibleItem = producesItem != null && producesItem is StardewValley.Object && (int)((StardewValley.Object)producesItem).Edibility != -300; string[]? buffIconsToDisplay = (edibleItem && Game1.objectInformation[((StardewValley.Object)producesItem).ParentSheetIndex].Split('/').Length > 7) ? producesItem.ModifyItemBuffs(Game1.objectInformation[((StardewValley.Object)producesItem).ParentSheetIndex].Split('/')[7].Split(' ')) : null; @@ -1513,7 +1513,7 @@ namespace stardew_access.Patches #region Add buff items (effects like +1 walking speed) // These variables are taken from the game's code itself (IClickableMenu.cs -> 1016 line) - bool edibleItem = actualInventory[i] != null && actualInventory[i] is StardewValley.Object && (int)((StardewValley.Object)actualInventory[i]).edibility != -300; + bool edibleItem = actualInventory[i] != null && actualInventory[i] is StardewValley.Object && (int)((StardewValley.Object)actualInventory[i]).Edibility != -300; string[]? buffIconsToDisplay = (edibleItem && Game1.objectInformation[((StardewValley.Object)actualInventory[i]).ParentSheetIndex].Split('/').Length > 7) ? actualInventory[i].ModifyItemBuffs(Game1.objectInformation[((StardewValley.Object)actualInventory[i]).ParentSheetIndex].Split('/')[7].Split(' ')) : null; if (buffIconsToDisplay != null) { diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 6c4c1c0..a91f331 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -11,6 +11,7 @@ namespace stardew_access.Patches internal class MenuPatches { private static string currentLevelUpTitle = " "; + internal static bool firstTimeInNamingMenu = true; private static string animalQueryMenuQuery = " "; public static Vector2? prevTile = null; @@ -167,15 +168,61 @@ namespace stardew_access.Patches } } - internal static void NamingMenuPatch(NamingMenu __instance, string title, TextBox ___textBox) + internal static void TitleTextInputMenuPatch(TitleTextInputMenu __instance) { try { - __instance.textBoxCC.snapMouseCursor(); - ___textBox.SelectMe(); - string toSpeak = $"{title}"; + string toSpeak = ""; + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - MainClass.GetScreenReader().SayWithChecker(toSpeak, true); + if (__instance.pasteButton != null && __instance.pasteButton.containsPoint(x, y)) + toSpeak = $"Paste button"; + + if (toSpeak != "") + MainClass.GetScreenReader().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 + { + 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 (firstTimeInNamingMenu) + { + firstTimeInNamingMenu = false; + ___textBox.Selected = false; + } + + if (___textBox.Selected) + { + ___textBox.Update(); + toSpeak = ___textBox.Text; + + if (isEscPressed) + { + ___textBox.Selected = false; + } + } + else + { + 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.GetScreenReader().SayWithChecker(toSpeak, true); } catch (Exception e) { From 9e1c263cf8df9e0d6c8eb13e17d6f9dc401ec17d Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 9 Apr 2022 15:48:13 +0530 Subject: [PATCH 036/232] Fixed warnings --- stardew-access/API.cs | 20 ++-- stardew-access/CustomCommands.cs | 16 +-- stardew-access/Features/CurrentPlayer.cs | 2 +- stardew-access/Features/Other.cs | 6 +- stardew-access/Features/ReadTile.cs | 24 ++-- stardew-access/ModEntry.cs | 56 +++++---- .../Patches/BuildingNAnimalMenuPatches.cs | 38 +++--- stardew-access/Patches/ChatMenuPatches.cs | 4 +- stardew-access/Patches/DialoguePatches.cs | 22 ++-- stardew-access/Patches/GameMenuPatches.cs | 108 +++++++++--------- stardew-access/Patches/MenuPatches.cs | 34 +++--- stardew-access/Patches/MuseumMenuPatches.cs | 16 +-- stardew-access/Patches/QuestPatches.cs | 14 +-- stardew-access/Patches/TitleMenuPatches.cs | 20 ++-- 14 files changed, 190 insertions(+), 190 deletions(-) diff --git a/stardew-access/API.cs b/stardew-access/API.cs index 24a6c8b..902eed7 100644 --- a/stardew-access/API.cs +++ b/stardew-access/API.cs @@ -55,10 +55,10 @@ namespace stardew_access.ScreenReader /// Whether to skip the currently speaking text or not. public void Say(String text, Boolean interrupt) { - if (MainClass.GetScreenReader() == null) + if (MainClass.ScreenReader == null) return; - MainClass.GetScreenReader().Say(text, interrupt); + MainClass.ScreenReader.Say(text, interrupt); } /// Speaks the text via the loaded screen reader (if any). @@ -67,10 +67,10 @@ namespace stardew_access.ScreenReader /// Whether to skip the currently speaking text or not. public void SayWithChecker(String text, Boolean interrupt) { - if (MainClass.GetScreenReader() == null) + if (MainClass.ScreenReader == null) return; - MainClass.GetScreenReader().SayWithChecker(text, interrupt); + MainClass.ScreenReader.SayWithChecker(text, interrupt); } /// Speaks the text via the loaded screen reader (if any). @@ -80,10 +80,10 @@ namespace stardew_access.ScreenReader /// Whether to skip the currently speaking text or not. public void SayWithMenuChecker(String text, Boolean interrupt) { - if (MainClass.GetScreenReader() == null) + if (MainClass.ScreenReader == null) return; - MainClass.GetScreenReader().SayWithMenuChecker(text, interrupt); + MainClass.ScreenReader.SayWithMenuChecker(text, interrupt); } /// Speaks the text via the loaded screen reader (if any). @@ -93,10 +93,10 @@ namespace stardew_access.ScreenReader /// Whether to skip the currently speaking text or not. public void SayWithChatChecker(String text, Boolean interrupt) { - if (MainClass.GetScreenReader() == null) + if (MainClass.ScreenReader == null) return; - MainClass.GetScreenReader().SayWithChatChecker(text, interrupt); + MainClass.ScreenReader.SayWithChatChecker(text, interrupt); } /// Speaks the text via the loaded screen reader (if any). @@ -108,10 +108,10 @@ namespace stardew_access.ScreenReader /// Whether to skip the currently speaking text or not. public void SayWithTileQuery(String text, int x, int y, Boolean interrupt) { - if (MainClass.GetScreenReader() == null) + if (MainClass.ScreenReader == null) return; - MainClass.GetScreenReader().SayWithTileQuery(text, x, y, interrupt); + MainClass.ScreenReader.SayWithTileQuery(text, x, y, interrupt); } } diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index d4b2504..fc17160 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -11,12 +11,14 @@ namespace stardew_access { internal static void Initialize() { - IModHelper helper = MainClass.ModHelper; + IModHelper? helper = MainClass.ModHelper; + if (helper == null) + return; helper.ConsoleCommands.Add("readtile", "Toggle read tile feature.", (string commmand, string[] args) => { MainClass.Config.ReadTile = !MainClass.Config.ReadTile; - MainClass.ModHelper.WriteConfig(MainClass.Config); + helper.WriteConfig(MainClass.Config); MainClass.DebugLog("Read Tile is " + (MainClass.Config.ReadTile ? "on" : "off")); }); @@ -24,7 +26,7 @@ namespace stardew_access helper.ConsoleCommands.Add("snapmouse", "Toggle snap mouse feature.", (string commmand, string[] args) => { MainClass.Config.SnapMouse = !MainClass.Config.SnapMouse; - MainClass.ModHelper.WriteConfig(MainClass.Config); + helper.WriteConfig(MainClass.Config); MainClass.DebugLog("Snap Mouse is " + (MainClass.Config.SnapMouse ? "on" : "off")); }); @@ -32,7 +34,7 @@ namespace stardew_access helper.ConsoleCommands.Add("flooring", "Toggle flooring in read tile.", (string commmand, string[] args) => { MainClass.Config.ReadFlooring = !MainClass.Config.ReadFlooring; - MainClass.ModHelper.WriteConfig(MainClass.Config); + helper.WriteConfig(MainClass.Config); MainClass.DebugLog("Flooring is " + (MainClass.Config.ReadFlooring ? "on" : "off")); }); @@ -40,7 +42,7 @@ namespace stardew_access helper.ConsoleCommands.Add("radar", "Toggle radar feature.", (string commmand, string[] args) => { MainClass.Config.Radar = !MainClass.Config.Radar; - MainClass.ModHelper.WriteConfig(MainClass.Config); + helper.WriteConfig(MainClass.Config); MainClass.DebugLog("Radar " + (MainClass.Config.Radar ? "on" : "off")); }); @@ -56,7 +58,7 @@ namespace stardew_access helper.ConsoleCommands.Add("rstereo", "Toggle stereo sound in radar feature.", (string commmand, string[] args) => { MainClass.Config.RadarStereoSound = !MainClass.Config.RadarStereoSound; - MainClass.ModHelper.WriteConfig(MainClass.Config); + helper.WriteConfig(MainClass.Config); MainClass.DebugLog("Stereo sound is " + (MainClass.Config.RadarStereoSound ? "on" : "off")); }); @@ -467,7 +469,7 @@ namespace stardew_access helper.ConsoleCommands.Add("refsr", "Refresh screen reader", (string commmand, string[] args) => { - MainClass.GetScreenReader().InitializeScreenReader(); + MainClass.ScreenReader.InitializeScreenReader(); MainClass.DebugLog("Screen Reader refreshed!"); }); diff --git a/stardew-access/Features/CurrentPlayer.cs b/stardew-access/Features/CurrentPlayer.cs index 7ea330e..3f29382 100644 --- a/stardew-access/Features/CurrentPlayer.cs +++ b/stardew-access/Features/CurrentPlayer.cs @@ -23,7 +23,7 @@ namespace stardew_access.Features if (Game1.player == null) return 0; - int maxStamina = Game1.player.maxStamina; + int maxStamina = Game1.player.maxStamina.Value; int currentStamine = (int)Game1.player.stamina; int staminaPercentage = (int)(currentStamine * 100) / maxStamina; diff --git a/stardew-access/Features/Other.cs b/stardew-access/Features/Other.cs index 6876480..a21698f 100644 --- a/stardew-access/Features/Other.cs +++ b/stardew-access/Features/Other.cs @@ -23,7 +23,7 @@ namespace stardew_access.Features return; previousSlotItem = currentSlotItem; - MainClass.GetScreenReader().Say($"{currentSlotItem.DisplayName} Selected", true); + MainClass.ScreenReader.Say($"{currentSlotItem.DisplayName} Selected", true); } // Narrates current location's name @@ -38,7 +38,7 @@ namespace stardew_access.Features return; previousLocation = currentLocation; - MainClass.GetScreenReader().Say($"{currentLocation.Name} Entered", true); + MainClass.ScreenReader.Say($"{currentLocation.Name} Entered", true); } public static void SnapMouseToPlayer() @@ -88,7 +88,7 @@ namespace stardew_access.Features { MainClass.hudMessageQueryKey = searchQuery; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } } } diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index b90919a..33c5931 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -47,8 +47,8 @@ namespace stardew_access.Features { if (!manuallyTriggered && prevTile != tile) { - if (MainClass.GetScreenReader() != null) - MainClass.GetScreenReader().PrevTextTile = " "; + if (MainClass.ScreenReader != null) + MainClass.ScreenReader.PrevTextTile = " "; } bool isColliding = isCollidingAtTile(x, y); @@ -57,11 +57,11 @@ namespace stardew_access.Features #region Narrate toSpeak if (toSpeak != null) - if (MainClass.GetScreenReader() != null) + if (MainClass.ScreenReader != null) if (manuallyTriggered) - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); else - MainClass.GetScreenReader().SayWithTileQuery(toSpeak, x, y, true); + MainClass.ScreenReader.SayWithTileQuery(toSpeak, x, y, true); #endregion #region Play colliding sound effect @@ -202,13 +202,13 @@ namespace stardew_access.Features { string? toReturn = null; Bush bush = (Bush)Game1.currentLocation.getLargeTerrainFeatureAt(x, y); - int size = bush.size; + int size = bush.size.Value; #region Check if bush is harvestable or not - if (!bush.townBush && (int)bush.tileSheetOffset == 1 && bush.inBloom(Game1.GetSeasonForLocation(Game1.currentLocation), Game1.dayOfMonth)) + if (!bush.townBush.Value && (int)bush.tileSheetOffset.Value == 1 && bush.inBloom(Game1.GetSeasonForLocation(Game1.currentLocation), Game1.dayOfMonth)) { // Taken from the game's code - string season = ((int)bush.overrideSeason == -1) ? Game1.GetSeasonForLocation(Game1.currentLocation) : Utility.getSeasonNameFromNumber(bush.overrideSeason); + string season = ((int)bush.overrideSeason.Value == -1) ? Game1.GetSeasonForLocation(Game1.currentLocation) : Utility.getSeasonNameFromNumber(bush.overrideSeason.Value); int shakeOff = -1; if (!(season == "spring")) { @@ -238,9 +238,9 @@ namespace stardew_access.Features } #endregion - if (bush.townBush) + if (bush.townBush.Value) toReturn = $"{toReturn} Town Bush"; - else if (bush.greenhouseBush) + else if (bush.greenhouseBush.Value) toReturn = $"{toReturn} Greenhouse Bush"; else toReturn = $"{toReturn} Bush"; @@ -409,7 +409,7 @@ namespace stardew_access.Features HoeDirt dirt = (HoeDirt)terrain.Get(); if (dirt.crop != null) { - string cropName = Game1.objectInformation[dirt.crop.indexOfHarvest].Split('/')[0]; + string cropName = Game1.objectInformation[dirt.crop.indexOfHarvest.Value].Split('/')[0]; toReturn = $"{cropName}"; bool isWatered = dirt.state.Value == HoeDirt.watered; @@ -633,7 +633,7 @@ namespace stardew_access.Features if (machine is CrabPot crabPot) if (crabPot.bait.Value is not null && crabPot.heldObject.Value is null) return MachineState.Busy; - return GetMachineState(machine.readyForHarvest.Value, machine.MinutesUntilReady, machine.heldObject); + return GetMachineState(machine.readyForHarvest.Value, machine.MinutesUntilReady, machine.heldObject.Value); } private static MachineState GetMachineState(bool readyForHarvest, int minutesUntilReady, StardewValley.Object? heldObject) diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 8c344be..1dc8136 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -12,16 +12,16 @@ namespace stardew_access { public class MainClass : Mod { - #region Global Vars - private static ModConfig config; + #region Global Vars & Properties + private static ModConfig? config; private Harmony? harmony; private static IMonitor? monitor; private static Radar? radarFeature; private static IScreenReader? screenReader; - private static IModHelper modHelper; + private static IModHelper? modHelper; internal static ModConfig Config { get => config; set => config = value; } - public static IModHelper ModHelper { get => modHelper; } + public static IModHelper? ModHelper { get => modHelper; } public static Radar RadarFeature { get @@ -35,25 +35,21 @@ namespace stardew_access public static string hudMessageQueryKey = ""; public static bool isNarratingHudMessage = false; public static bool radarDebug = false; + + public static IScreenReader ScreenReader + { + get + { + if (screenReader == null) + screenReader = new ScreenReaderController().Initialize(); + + return screenReader; + } + + set => screenReader = value; + } #endregion - public static IScreenReader GetScreenReader() - { - if (screenReader == null) - screenReader = new ScreenReaderController().Initialize(); - return screenReader; - } - - public static void SetScreenReader(IScreenReader value) - { - screenReader = value; - } - - public static void SetMonitor(IMonitor value) - { - monitor = value; - } - /********* ** Public methods *********/ @@ -64,12 +60,12 @@ namespace stardew_access #region Initializations Config = helper.ReadConfig(); - SetMonitor(base.Monitor); // Inititalize monitor + monitor = base.Monitor; // Inititalize monitor modHelper = helper; Game1.options.setGamepadMode("force_on"); - SetScreenReader(new ScreenReaderController().Initialize()); + ScreenReader = new ScreenReaderController().Initialize(); CustomSoundEffects.Initialize(); @@ -99,8 +95,8 @@ namespace stardew_access public void OnExit(object? sender, EventArgs? e) { // Don't if this ever gets called or not but, just in case if it does. - if (GetScreenReader() != null) - GetScreenReader().CloseScreenReader(); + if (ScreenReader != null) + ScreenReader.CloseScreenReader(); } /// Returns the Screen Reader class for other mods to use. @@ -188,7 +184,7 @@ namespace stardew_access if (Config.LocationKey.JustPressed()) { string toSpeak = $"{Game1.currentLocation.Name}"; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); return; } @@ -205,7 +201,7 @@ namespace stardew_access toSpeak = $"{CurrentPlayer.getPositionX()}, {CurrentPlayer.getPositionY()}"; } - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); return; } @@ -213,7 +209,7 @@ namespace stardew_access if (Config.HealthNStaminaKey.JustPressed()) { string toSpeak = $"Health is {CurrentPlayer.getHealth()} and Stamina is {CurrentPlayer.getStamina()}"; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); return; } @@ -221,7 +217,7 @@ namespace stardew_access if (Config.MoneyKey.JustPressed()) { string toSpeak = $"You have {CurrentPlayer.getMoney()}g"; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); return; } @@ -229,7 +225,7 @@ namespace stardew_access if (Config.TimeNSeasonKey.JustPressed()) { string toSpeak = $"Time is {CurrentPlayer.getTimeOfDay()} and it is {CurrentPlayer.getDay()} {CurrentPlayer.getDate()} of {CurrentPlayer.getSeason()}"; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); return; } diff --git a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs index 1a90347..b172e95 100644 --- a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs +++ b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs @@ -61,7 +61,7 @@ namespace stardew_access.Patches firstTimeInNamingMenu = false; } - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } } else if (___onFarm && !___namingAnimal) @@ -90,7 +90,7 @@ namespace stardew_access.Patches if (purchaseAnimalMenuQuery != toSpeak) { purchaseAnimalMenuQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -180,7 +180,7 @@ namespace stardew_access.Patches if (carpenterMenuQuery != toSpeak) { carpenterMenuQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -191,7 +191,7 @@ namespace stardew_access.Patches if (carpenterMenuQuery != toSpeak) { carpenterMenuQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -202,7 +202,7 @@ namespace stardew_access.Patches if (carpenterMenuQuery != toSpeak) { carpenterMenuQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -213,7 +213,7 @@ namespace stardew_access.Patches if (carpenterMenuQuery != toSpeak) { carpenterMenuQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -224,7 +224,7 @@ namespace stardew_access.Patches if (carpenterMenuQuery != toSpeak) { carpenterMenuQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -235,7 +235,7 @@ namespace stardew_access.Patches if (carpenterMenuQuery != toSpeak) { carpenterMenuQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -246,7 +246,7 @@ namespace stardew_access.Patches if (carpenterMenuQuery != toSpeak) { carpenterMenuQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -276,7 +276,7 @@ namespace stardew_access.Patches private static async void SayBlueprintInfo(string info) { isSayingBlueprintInfo = true; - MainClass.GetScreenReader().Say(info, true); + MainClass.ScreenReader.Say(info, true); await Task.Delay(300); isSayingBlueprintInfo = false; } @@ -300,7 +300,7 @@ namespace stardew_access.Patches { if (isDemolishing && toDemolish != null && farm.buildings.Contains(toDemolish)) { - if ((int)toDemolish.daysOfConstructionLeft > 0 || (int)toDemolish.daysUntilUpgrade > 0) + if ((int)toDemolish.daysOfConstructionLeft.Value > 0 || (int)toDemolish.daysUntilUpgrade.Value > 0) { response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_DuringConstruction"); } @@ -345,8 +345,8 @@ namespace stardew_access.Patches } if (farm.destroyStructure(toDemolish)) { - _ = (int)toDemolish.tileY; - _ = (int)toDemolish.tilesHigh; + _ = (int)toDemolish.tileY.Value; + _ = (int)toDemolish.tilesHigh.Value; Game1.flashAlpha = 1f; toDemolish.showDestroyedAnimation(Game1.getFarm()); Game1.playSound("explosion"); @@ -356,7 +356,7 @@ namespace stardew_access.Patches // freeze = true; if (chest != null) { - farm.objects[new Vector2((int)toDemolish.tileX + (int)toDemolish.tilesWide / 2, (int)toDemolish.tileY + (int)toDemolish.tilesHigh / 2)] = chest; + farm.objects[new Vector2((int)toDemolish.tileX.Value + (int)toDemolish.tilesWide.Value / 2, (int)toDemolish.tileY.Value + (int)toDemolish.tilesHigh.Value / 2)] = chest; } } } @@ -385,7 +385,7 @@ namespace stardew_access.Patches if (toDemolish != null && toDemolish.indoors.Value is Cabin) { Cabin cabin = (Cabin)toDemolish.indoors.Value; - if (cabin.farmhand.Value != null && (bool)cabin.farmhand.Value.isCustomized) + 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) { @@ -458,7 +458,7 @@ namespace stardew_access.Patches 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); + // Game1.multiplayer.globalChatInfoMessage("BuildingBuild", Game1.player.Name, Utility.AOrAn(carpenterMenu.CurrentBlueprint.displayName), carpenterMenu.CurrentBlueprint.displayName, Game1.player.farmName.Value); } else if (toUpgrade != null) { @@ -517,7 +517,7 @@ namespace stardew_access.Patches string? name = buildingToMove.nameOfIndoorsWithoutUnique; name = (name == "null") ? buildingToMove.buildingType.Value : name; - if ((int)buildingToMove.daysOfConstructionLeft > 0) + if ((int)buildingToMove.daysOfConstructionLeft.Value > 0) { buildingToMove = null; return "Building under construction, cannot move"; @@ -566,8 +566,8 @@ namespace stardew_access.Patches if (purchaseAnimalsMenu == null) return; - int x = (selection.tileX * Game1.tileSize) - Game1.viewport.X; - int y = (selection.tileY * Game1.tileSize) - Game1.viewport.Y; + int x = (selection.tileX.Value * Game1.tileSize) - Game1.viewport.X; + int y = (selection.tileY.Value * Game1.tileSize) - Game1.viewport.Y; purchaseAnimalsMenu.receiveLeftClick(x, y); } } diff --git a/stardew-access/Patches/ChatMenuPatches.cs b/stardew-access/Patches/ChatMenuPatches.cs index 6b3a07a..6de3d2b 100644 --- a/stardew-access/Patches/ChatMenuPatches.cs +++ b/stardew-access/Patches/ChatMenuPatches.cs @@ -45,7 +45,7 @@ namespace stardew_access.Patches toSpeak += $"{message.message}, "; }); if (toSpeak != " ") - MainClass.GetScreenReader().SayWithChatChecker(toSpeak, false); + MainClass.ScreenReader.SayWithChatChecker(toSpeak, false); #endregion } } @@ -79,7 +79,7 @@ namespace stardew_access.Patches toSpeak += $"{message.message}, "; }); - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } } } diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index 9eaab5b..acdf755 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -57,7 +57,7 @@ namespace stardew_access.Patches else toSpeak = response; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } } else @@ -65,7 +65,7 @@ namespace stardew_access.Patches if (currentDialogue != dialogueText) { currentDialogue = dialogueText; - MainClass.GetScreenReader().Say(dialogueText, true); + MainClass.ScreenReader.Say(dialogueText, true); } } } @@ -106,7 +106,7 @@ namespace stardew_access.Patches else toSpeak = response; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } } else @@ -114,7 +114,7 @@ namespace stardew_access.Patches if (currentDialogue != dialogueText) { currentDialogue = dialogueText; - MainClass.GetScreenReader().Say(dialogueText, true); + MainClass.ScreenReader.Say(dialogueText, true); } } } @@ -124,7 +124,7 @@ namespace stardew_access.Patches if (currentDialogue != __instance.getCurrentString()) { currentDialogue = __instance.getCurrentString(); - MainClass.GetScreenReader().Say(__instance.getCurrentString(), true); + MainClass.ScreenReader.Say(__instance.getCurrentString(), true); } } } @@ -316,9 +316,9 @@ namespace stardew_access.Patches if (toSpeak.ToString() != " ") { if (Context.IsPlayerFree) - MainClass.GetScreenReader().SayWithChecker(toSpeak.ToString(), true); // Normal Checker + MainClass.ScreenReader.SayWithChecker(toSpeak.ToString(), true); // Normal Checker else - MainClass.GetScreenReader().SayWithMenuChecker(toSpeak.ToString(), true); // Menu Checker + MainClass.ScreenReader.SayWithMenuChecker(toSpeak.ToString(), true); // Menu Checker } #endregion } @@ -380,7 +380,7 @@ namespace stardew_access.Patches if (__instance.mailMessage.Count > 1) toSpeak = $"Page {__instance.page + 1} of {__instance.mailMessage.Count}:\n\t{toSpeak}"; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } #endregion @@ -393,17 +393,17 @@ namespace stardew_access.Patches string label = c.label; if (c.containsPoint(x, y)) - MainClass.GetScreenReader().SayWithChecker($"Grab: {name} \t\n {label}", false); + MainClass.ScreenReader.SayWithChecker($"Grab: {name} \t\n {label}", false); } } #endregion #region Narrate buttons if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y)) - MainClass.GetScreenReader().SayWithChecker($"Previous page button", false); + MainClass.ScreenReader.SayWithChecker($"Previous page button", false); if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y)) - MainClass.GetScreenReader().SayWithChecker($"Next page button", false); + MainClass.ScreenReader.SayWithChecker($"Next page button", false); #endregion } diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index 3c258f6..f0e72f0 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -59,7 +59,7 @@ namespace stardew_access.Patches if (junimoNoteMenuQuery != toSpeak) { junimoNoteMenuQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -71,7 +71,7 @@ namespace stardew_access.Patches if (junimoNoteMenuQuery != toSpeak) { junimoNoteMenuQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -82,7 +82,7 @@ namespace stardew_access.Patches if (junimoNoteMenuQuery != toSpeak) { junimoNoteMenuQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -94,7 +94,7 @@ namespace stardew_access.Patches if (junimoNoteMenuQuery != toSpeak) { junimoNoteMenuQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -104,7 +104,7 @@ namespace stardew_access.Patches if (junimoNoteMenuQuery != toSpeak) { junimoNoteMenuQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -140,12 +140,12 @@ namespace stardew_access.Patches else if (isBackPressed && __instance.backButton != null && !__instance.backButton.containsPoint(x, y)) { __instance.backButton.snapMouseCursorToCenter(); - MainClass.GetScreenReader().Say("Back Button", true); + MainClass.ScreenReader.Say("Back Button", true); } else if (isPPressed && __instance.purchaseButton != null && !__instance.purchaseButton.containsPoint(x, y)) { __instance.purchaseButton.snapMouseCursorToCenter(); - MainClass.GetScreenReader().Say("Purchase Button", true); + MainClass.ScreenReader.Say("Purchase Button", true); } } string reward = __instance.getRewardNameForArea(___whichArea); @@ -215,7 +215,7 @@ namespace stardew_access.Patches toSpeak = $"Completed {toSpeak}"; c.snapMouseCursorToCenter(); - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } } break; @@ -250,7 +250,7 @@ namespace stardew_access.Patches } c.snapMouseCursorToCenter(); - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } } break; @@ -303,7 +303,7 @@ namespace stardew_access.Patches toSpeak = "Empty Slot"; } c.snapMouseCursorToCenter(); - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } } break; @@ -401,7 +401,7 @@ namespace stardew_access.Patches if (socialPageQuery != toSpeak) { socialPageQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -446,7 +446,7 @@ namespace stardew_access.Patches if (socialPageQuery != toSpeak) { socialPageQuery = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -490,7 +490,7 @@ namespace stardew_access.Patches { shopMenuQueryKey = toSpeak; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); Game1.playSound("drop_item"); } return; @@ -502,7 +502,7 @@ namespace stardew_access.Patches { shopMenuQueryKey = toSpeak; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -513,7 +513,7 @@ namespace stardew_access.Patches { shopMenuQueryKey = toSpeak; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -560,7 +560,7 @@ namespace stardew_access.Patches { shopMenuQueryKey = toSpeak; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } } #endregion @@ -589,7 +589,7 @@ namespace stardew_access.Patches if (gameMenuQueryKey != toSpeak) { gameMenuQueryKey = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -618,7 +618,7 @@ namespace stardew_access.Patches if (geodeMenuQueryKey != toSpeak) { geodeMenuQueryKey = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -631,7 +631,7 @@ namespace stardew_access.Patches if (geodeMenuQueryKey != toSpeak) { geodeMenuQueryKey = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -643,7 +643,7 @@ namespace stardew_access.Patches if (geodeMenuQueryKey != toSpeak) { geodeMenuQueryKey = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); Game1.playSound("drop_item"); } return; @@ -656,7 +656,7 @@ namespace stardew_access.Patches if (geodeMenuQueryKey != toSpeak) { geodeMenuQueryKey = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -668,7 +668,7 @@ namespace stardew_access.Patches if (geodeMenuQueryKey != toSpeak) { geodeMenuQueryKey = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -713,7 +713,7 @@ namespace stardew_access.Patches itemGrabMenuQueryKey = toSpeak; hoveredItemQueryKey = ""; gameMenuQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -725,7 +725,7 @@ namespace stardew_access.Patches itemGrabMenuQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -738,7 +738,7 @@ namespace stardew_access.Patches itemGrabMenuQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -751,7 +751,7 @@ namespace stardew_access.Patches itemGrabMenuQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -764,7 +764,7 @@ namespace stardew_access.Patches itemGrabMenuQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -778,7 +778,7 @@ namespace stardew_access.Patches itemGrabMenuQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -792,7 +792,7 @@ namespace stardew_access.Patches itemGrabMenuQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -805,7 +805,7 @@ namespace stardew_access.Patches itemGrabMenuQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); Game1.playSound("drop_item"); } return; @@ -847,7 +847,7 @@ namespace stardew_access.Patches itemGrabMenuQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -987,7 +987,7 @@ namespace stardew_access.Patches { craftingPageQueryKey = toSpeak; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -999,7 +999,7 @@ namespace stardew_access.Patches { craftingPageQueryKey = toSpeak; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -1011,7 +1011,7 @@ namespace stardew_access.Patches { craftingPageQueryKey = toSpeak; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -1023,7 +1023,7 @@ namespace stardew_access.Patches { craftingPageQueryKey = toSpeak; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); Game1.playSound("drop_item"); } return; @@ -1057,20 +1057,22 @@ namespace stardew_access.Patches #region Health & stamina and buff items (effects like +1 walking speed) Item producesItem = ___hoverRecipe.createItem(); - if (producesItem is StardewValley.Object && ((StardewValley.Object)producesItem).Edibility != -300) + StardewValley.Object? producesItemObject = ((StardewValley.Object)producesItem); + + if (producesItem is StardewValley.Object && producesItemObject.Edibility != -300) { - int stamina_recovery = ((StardewValley.Object)producesItem).staminaRecoveredOnConsumption(); + int stamina_recovery = producesItemObject.staminaRecoveredOnConsumption(); buffs += $"{stamina_recovery} Energy"; if (stamina_recovery >= 0) { - int health_recovery = ((StardewValley.Object)producesItem).healthRecoveredOnConsumption(); + 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 && producesItem is StardewValley.Object && (int)((StardewValley.Object)producesItem).Edibility != -300; - string[]? buffIconsToDisplay = (edibleItem && Game1.objectInformation[((StardewValley.Object)producesItem).ParentSheetIndex].Split('/').Length > 7) - ? producesItem.ModifyItemBuffs(Game1.objectInformation[((StardewValley.Object)producesItem).ParentSheetIndex].Split('/')[7].Split(' ')) + bool edibleItem = producesItem != null && producesItem is StardewValley.Object && (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) @@ -1103,7 +1105,7 @@ namespace stardew_access.Patches craftingPageQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -1128,7 +1130,7 @@ namespace stardew_access.Patches craftingPageQueryKey = query; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say("unknown recipe", true); + MainClass.ScreenReader.Say("unknown recipe", true); } return; } @@ -1195,7 +1197,7 @@ namespace stardew_access.Patches inventoryPageQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); Game1.playSound("drop_item"); } return; @@ -1209,7 +1211,7 @@ namespace stardew_access.Patches inventoryPageQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -1222,7 +1224,7 @@ namespace stardew_access.Patches inventoryPageQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -1235,7 +1237,7 @@ namespace stardew_access.Patches itemGrabMenuQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -1249,7 +1251,7 @@ namespace stardew_access.Patches itemGrabMenuQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -1345,7 +1347,7 @@ namespace stardew_access.Patches inventoryPageQueryKey = toSpeak; gameMenuQueryKey = ""; hoveredItemQueryKey = ""; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -1408,7 +1410,7 @@ namespace stardew_access.Patches { gameMenuQueryKey = ""; optionsPageQueryKey = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -1432,7 +1434,7 @@ namespace stardew_access.Patches { gameMenuQueryKey = ""; exitPageQueryKey = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -1444,7 +1446,7 @@ namespace stardew_access.Patches { gameMenuQueryKey = ""; exitPageQueryKey = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return; } @@ -1583,7 +1585,7 @@ namespace stardew_access.Patches if (hoveredItemQueryKey != $"{toSpeak}:{i}") { hoveredItemQueryKey = $"{toSpeak}:{i}"; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return true; } diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index a91f331..d929049 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -42,7 +42,7 @@ namespace stardew_access.Patches 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 < (byte)___animal.ageWhenMature) + if ((int)___animal.age.Value < (byte)___animal.ageWhenMature.Value) { ageText += Game1.content.LoadString("Strings\\UI:AnimalQuery_AgeBaby"); } @@ -74,7 +74,7 @@ namespace stardew_access.Patches if (animalQueryMenuQuery != toSpeak) { animalQueryMenuQuery = toSpeak; - MainClass.GetScreenReader().Say($"{details} {toSpeak}", true); + MainClass.ScreenReader.Say($"{details} {toSpeak}", true); } } catch (System.Exception e) @@ -123,13 +123,13 @@ namespace stardew_access.Patches if (__instance.nextPageButton != null && __instance.nextPageButton.containsPoint(x, y)) { - MainClass.GetScreenReader().SayWithMenuChecker($"Next Page Button", true); + MainClass.ScreenReader.SayWithMenuChecker($"Next Page Button", true); return; } if (__instance.previousPageButton != null && __instance.previousPageButton.containsPoint(x, y)) { - MainClass.GetScreenReader().SayWithMenuChecker($"Previous Page Button", true); + MainClass.ScreenReader.SayWithMenuChecker($"Previous Page Button", true); return; } @@ -137,7 +137,7 @@ namespace stardew_access.Patches { if (__instance.languages[i].containsPoint(x, y)) { - MainClass.GetScreenReader().SayWithMenuChecker($"{__instance.languageList[i]} Button", true); + MainClass.ScreenReader.SayWithMenuChecker($"{__instance.languageList[i]} Button", true); break; } } @@ -157,7 +157,7 @@ namespace stardew_access.Patches { if (___elevators[i].containsPoint(x, y)) { - MainClass.GetScreenReader().SayWithMenuChecker($"{___elevators[i].name} level", true); + MainClass.ScreenReader.SayWithMenuChecker($"{___elevators[i].name} level", true); break; } } @@ -179,7 +179,7 @@ namespace stardew_access.Patches toSpeak = $"Paste button"; if (toSpeak != "") - MainClass.GetScreenReader().SayWithChecker(toSpeak, true); + MainClass.ScreenReader.SayWithChecker(toSpeak, true); } catch (System.Exception e) { @@ -222,7 +222,7 @@ namespace stardew_access.Patches } if (toSpeak != "") - MainClass.GetScreenReader().SayWithChecker(toSpeak, true); + MainClass.ScreenReader.SayWithChecker(toSpeak, true); } catch (Exception e) { @@ -236,14 +236,14 @@ namespace stardew_access.Patches { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); - MainClass.GetScreenReader().SayWithMenuChecker(___message, true); + MainClass.ScreenReader.SayWithMenuChecker(___message, true); if (__instance.okButton.containsPoint(x, y)) { - MainClass.GetScreenReader().SayWithMenuChecker("Ok Button", false); + MainClass.ScreenReader.SayWithMenuChecker("Ok Button", false); } else if (__instance.cancelButton.containsPoint(x, y)) { - MainClass.GetScreenReader().SayWithMenuChecker("Cancel Button", false); + MainClass.ScreenReader.SayWithMenuChecker("Cancel Button", false); } } catch (Exception e) @@ -336,10 +336,10 @@ namespace stardew_access.Patches } if (toSpeak != " ") - MainClass.GetScreenReader().SayWithMenuChecker(toSpeak, true); + MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true); else if (__instance.isProfessionChooser && currentLevelUpTitle != $"{___title}. Select a new profession.") { - MainClass.GetScreenReader().SayWithMenuChecker($"{___title}. Select a new profession.", true); + MainClass.ScreenReader.SayWithMenuChecker($"{___title}. Select a new profession.", true); currentLevelUpTitle = $"{___title}. Select a new profession."; } } @@ -366,14 +366,14 @@ namespace stardew_access.Patches Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); } toSpeak = $"{total}g in total. Press left mouse button to save."; - MainClass.GetScreenReader().SayWithChecker(toSpeak, true); + 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.GetScreenReader().SayWithChecker(toSpeak, true); + MainClass.ScreenReader.SayWithChecker(toSpeak, true); } } } @@ -491,8 +491,8 @@ namespace stardew_access.Patches internal static void ExitEventPatch() { - if (MainClass.GetScreenReader() != null) - MainClass.GetScreenReader().CloseScreenReader(); + if (MainClass.ScreenReader != null) + MainClass.ScreenReader.CloseScreenReader(); } } } diff --git a/stardew-access/Patches/MuseumMenuPatches.cs b/stardew-access/Patches/MuseumMenuPatches.cs index e472b77..6a06c65 100644 --- a/stardew-access/Patches/MuseumMenuPatches.cs +++ b/stardew-access/Patches/MuseumMenuPatches.cs @@ -71,7 +71,7 @@ namespace stardew_access.Patches if (museumQueryKey != toSpeak) { museumQueryKey = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } } else @@ -92,9 +92,9 @@ namespace stardew_access.Patches if (((LibraryMuseum)Game1.currentLocation).isTileSuitableForMuseumPiece(tileX, tileY) && ((LibraryMuseum)Game1.currentLocation).isItemSuitableForDonation(__instance.inventory.actualInventory[i])) { - int objectID = __instance.inventory.actualInventory[i].parentSheetIndex; + 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); + ((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) { @@ -115,13 +115,13 @@ namespace stardew_access.Patches switch (pieces) { case 95: - globalChatInfoMessage("MuseumComplete", Game1.player.farmName); + globalChatInfoMessage("MuseumComplete", Game1.player.farmName.Value); break; case 40: - globalChatInfoMessage("Museum40", Game1.player.farmName); + globalChatInfoMessage("Museum40", Game1.player.farmName.Value); break; default: - globalChatInfoMessage("donation", Game1.player.name, "object:" + objectID); + globalChatInfoMessage("donation", Game1.player.Name, "object:" + objectID); break; } break; @@ -135,7 +135,7 @@ namespace stardew_access.Patches if (museumQueryKey != $"ok button") { museumQueryKey = $"ok button"; - MainClass.GetScreenReader().Say("ok button", true); + MainClass.ScreenReader.Say("ok button", true); } } } @@ -205,7 +205,7 @@ namespace stardew_access.Patches if (museumQueryKey != $"{toSpeak}:{i}") { museumQueryKey = $"{toSpeak}:{i}"; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } return i; } diff --git a/stardew-access/Patches/QuestPatches.cs b/stardew-access/Patches/QuestPatches.cs index 1bd8303..b8d4a3d 100644 --- a/stardew-access/Patches/QuestPatches.cs +++ b/stardew-access/Patches/QuestPatches.cs @@ -23,7 +23,7 @@ namespace stardew_access.Patches toSpeak = $"Left Quest:\n\t{toSpeak}\n\tPress left click to accept this quest."; - MainClass.GetScreenReader().SayWithMenuChecker(toSpeak, true); + MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true); return; } @@ -33,7 +33,7 @@ namespace stardew_access.Patches toSpeak = $"Right Quest:\n\t{toSpeak}\n\tPress left click to accept this quest."; - MainClass.GetScreenReader().SayWithMenuChecker(toSpeak, true); + MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true); return; } } @@ -99,7 +99,7 @@ namespace stardew_access.Patches if (Game1.dayOfMonth == i + 1) toSpeak += $", Current"; - MainClass.GetScreenReader().SayWithChecker(toSpeak, true); + MainClass.ScreenReader.SayWithChecker(toSpeak, true); } } #endregion @@ -114,7 +114,7 @@ namespace stardew_access.Patches if (currentDailyQuestText != toSpeak) { currentDailyQuestText = toSpeak; - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } } else @@ -134,7 +134,7 @@ namespace stardew_access.Patches __instance.acceptQuestButton.snapMouseCursorToCenter(); } - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } } #endregion @@ -171,7 +171,7 @@ namespace stardew_access.Patches toSpeak += ___pages[___currentPage][i].ShouldDisplayAsComplete() ? " completed!" : ""; if (__instance.questLogButtons[i].containsPoint(Game1.getOldMouseX(), Game1.getOldMouseY())) { - MainClass.GetScreenReader().SayWithChecker(toSpeak, true); + MainClass.ScreenReader.SayWithChecker(toSpeak, true); } } } @@ -232,7 +232,7 @@ namespace stardew_access.Patches if (snapMouseToRewardBox) __instance.rewardBox.snapMouseCursorToCenter(); - MainClass.GetScreenReader().SayWithChecker(toSpeak, true); + MainClass.ScreenReader.SayWithChecker(toSpeak, true); #endregion } } diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index 38d8f8f..f8f2ec2 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -44,7 +44,7 @@ namespace stardew_access.Patches #endregion if (toSpeak != " ") - MainClass.GetScreenReader().SayWithChecker(toSpeak, true); + MainClass.ScreenReader.SayWithChecker(toSpeak, true); } catch (Exception e) { @@ -94,7 +94,7 @@ namespace stardew_access.Patches if (TitleMenu.subMenu != null && __instance.backButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true))) { string text = "Back Button"; - MainClass.GetScreenReader().SayWithChecker(text, true); + MainClass.ScreenReader.SayWithChecker(text, true); } // Fix for back button not working using keyboard @@ -108,7 +108,7 @@ namespace stardew_access.Patches } if (TitleMenu.subMenu == null && toSpeak != "") - MainClass.GetScreenReader().SayWithChecker(toSpeak, true); + MainClass.ScreenReader.SayWithChecker(toSpeak, true); } catch (Exception e) { @@ -128,7 +128,7 @@ namespace stardew_access.Patches #region Farms if (___menu.deleteButtons.Count > 0 && ___menu.deleteButtons[i].containsPoint(x, y)) { - MainClass.GetScreenReader().SayWithChecker($"Delete {__instance.Farmer.farmName} Farm", true); + MainClass.ScreenReader.SayWithChecker($"Delete {__instance.Farmer.farmName.Value} Farm", true); return; } @@ -137,20 +137,20 @@ namespace stardew_access.Patches // Used diff. functions to narrate to prevent it from speaking the message again on selecting another button. string message = "Really delete farm?"; - MainClass.GetScreenReader().SayWithChecker(message, true); + MainClass.ScreenReader.SayWithChecker(message, true); if (___menu.okDeleteButton.containsPoint(x, y)) { - MainClass.GetScreenReader().SayWithMenuChecker("Ok Button", false); + MainClass.ScreenReader.SayWithMenuChecker("Ok Button", false); } else if (___menu.cancelDeleteButton.containsPoint(x, y)) { - MainClass.GetScreenReader().SayWithMenuChecker("Cancel Button", false); + MainClass.ScreenReader.SayWithMenuChecker("Cancel Button", false); } return; } String farmerName = __instance.Farmer.displayName; - String farmName = __instance.Farmer.farmName; + 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 || @@ -159,7 +159,7 @@ namespace stardew_access.Patches string toSpeak = $"{farmName} Farm Selected, \t\n Farmer:{farmerName}, \t\nMoney:{money}, \t\nHours Played:{hoursPlayed}, \t\nDate:{dateStringForSaveGame}"; - MainClass.GetScreenReader().SayWithChecker(toSpeak, true); + MainClass.ScreenReader.SayWithChecker(toSpeak, true); #endregion } } @@ -318,7 +318,7 @@ namespace stardew_access.Patches if (toSpeak != " ") { - MainClass.GetScreenReader().Say(toSpeak, true); + MainClass.ScreenReader.Say(toSpeak, true); } } From 4b6879b1f38400758dcfbe95c4c075a4c17f496b Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 9 Apr 2022 16:24:55 +0530 Subject: [PATCH 037/232] Patched choose from list(jukebox) menu --- stardew-access/CustomSoundEffects.cs | 4 ++- stardew-access/HarmonyPatches.cs | 5 ++++ stardew-access/Patches/DialoguePatches.cs | 6 +++++ stardew-access/Patches/MenuPatches.cs | 32 ++++++++++++++++++++--- stardew-access/manifest.json | 2 +- 5 files changed, 44 insertions(+), 5 deletions(-) diff --git a/stardew-access/CustomSoundEffects.cs b/stardew-access/CustomSoundEffects.cs index de23067..11415cd 100644 --- a/stardew-access/CustomSoundEffects.cs +++ b/stardew-access/CustomSoundEffects.cs @@ -1,5 +1,4 @@ using Microsoft.Xna.Framework.Audio; -using StardewModdingAPI; using StardewValley; namespace stardew_access @@ -16,6 +15,9 @@ namespace stardew_access { try { + if (MainClass.ModHelper == null) + return; + Dictionary soundEffects = new Dictionary(); soundEffects.Add("drop_item", TYPE.Sound); diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index 37e225b..4cfaeb4 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -163,6 +163,11 @@ namespace stardew_access original: AccessTools.Method(typeof(AnimalQueryMenu), nameof(AnimalQueryMenu.draw), new Type[] { typeof(SpriteBatch) }), postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.AnimalQueryMenuPatch)) ); + + harmony.Patch( + original: AccessTools.Method(typeof(ChooseFromListMenu), nameof(ChooseFromListMenu.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ChooseFromListMenuPatch)) + ); #endregion #region Quest Patches diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index acdf755..c805b7c 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -197,6 +197,12 @@ namespace stardew_access.Patches if (Game1.activeClickableMenu is AnimalQueryMenu) return; + + if (Game1.activeClickableMenu is ConfirmationDialog) + return; + + if (Game1.activeClickableMenu is ReadyCheckDialog) + return; #endregion string toSpeak = " "; diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index d929049..12d3415 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -15,6 +15,30 @@ namespace stardew_access.Patches private static string animalQueryMenuQuery = " "; public static Vector2? prevTile = null; + 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 void AnimalQueryMenuPatch(AnimalQueryMenu __instance, bool ___confirmingSell, FarmAnimal ___animal, TextBox ___textBox, string ___parentName) { try @@ -235,16 +259,18 @@ namespace stardew_access.Patches try { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); + string toSpeak = ___message; - MainClass.ScreenReader.SayWithMenuChecker(___message, true); if (__instance.okButton.containsPoint(x, y)) { - MainClass.ScreenReader.SayWithMenuChecker("Ok Button", false); + toSpeak += "\n\tOk Button"; } else if (__instance.cancelButton.containsPoint(x, y)) { - MainClass.ScreenReader.SayWithMenuChecker("Cancel Button", false); + toSpeak += "\n\tCancel Button"; } + + MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true); } catch (Exception e) { diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index 8cac695..a795d8b 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.1.4", + "Version": "1.1.5", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From 0e1ab968e76f501ce12eba300355040cca4dadb0 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 11 Apr 2022 22:48:56 +0530 Subject: [PATCH 038/232] Patched joja community development menu --- stardew-access/HarmonyPatches.cs | 13 +- stardew-access/Patches/BundleMenuPatches.cs | 346 ++++++++++++++++++++ stardew-access/Patches/DialoguePatches.cs | 4 +- stardew-access/Patches/GameMenuPatches.cs | 277 ---------------- stardew-access/Patches/MenuPatches.cs | 8 +- 5 files changed, 363 insertions(+), 285 deletions(-) create mode 100644 stardew-access/Patches/BundleMenuPatches.cs diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index 4cfaeb4..f63e185 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -97,14 +97,21 @@ namespace stardew_access postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.SocialPagePatch)) ); + harmony.Patch( + original: AccessTools.Method(typeof(CollectionsPage), nameof(CollectionsPage.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.CollectionsPagePatch)) + ); + #endregion + + #region Bundle Menu Patches harmony.Patch( original: AccessTools.Method(typeof(JunimoNoteMenu), nameof(JunimoNoteMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.JunimoNoteMenuPatch)) + postfix: new HarmonyMethod(typeof(BundleMenuPatches), nameof(BundleMenuPatches.JunimoNoteMenuPatch)) ); harmony.Patch( - original: AccessTools.Method(typeof(CollectionsPage), nameof(CollectionsPage.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.CollectionsPagePatch)) + original: AccessTools.Method(typeof(JojaCDMenu), nameof(JojaCDMenu.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(BundleMenuPatches), nameof(BundleMenuPatches.JojaCDMenuPatch)) ); #endregion diff --git a/stardew-access/Patches/BundleMenuPatches.cs b/stardew-access/Patches/BundleMenuPatches.cs new file mode 100644 index 0000000..d9495e1 --- /dev/null +++ b/stardew-access/Patches/BundleMenuPatches.cs @@ -0,0 +1,346 @@ +using StardewValley; +using StardewValley.Locations; +using StardewValley.Menus; + +namespace stardew_access.Patches +{ + internal class BundleMenuPatches + { + internal static string junimoNoteMenuQuery = ""; + 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); + if (__instance.scrambledText) + { + string toSpeak = "Scrambled Text"; + if (junimoNoteMenuQuery != toSpeak) + { + junimoNoteMenuQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, 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 = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.I); // For the ingredients + bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); // For the items in inventory + bool isPPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.P); // For the Purchase Button + bool isVPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.V); // For the ingredient input slots + bool isBackPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Back); // 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); + } + } + string reward = __instance.getRewardNameForArea(___whichArea); + } + 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}"; + + } + 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/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index c805b7c..20a147f 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -203,6 +203,9 @@ namespace stardew_access.Patches if (Game1.activeClickableMenu is ReadyCheckDialog) return; + + if (Game1.activeClickableMenu is JojaCDMenu) + return; #endregion string toSpeak = " "; @@ -334,7 +337,6 @@ namespace stardew_access.Patches } } - internal static void LetterViewerMenuPatch(LetterViewerMenu __instance) { try diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index f0e72f0..d3d468a 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -19,12 +19,8 @@ namespace stardew_access.Patches internal static string shopMenuQueryKey = ""; internal static string socialPageQuery = ""; internal static string profilePageQuery = ""; - internal static string junimoNoteMenuQuery = ""; - internal static string collectionsPageQuery = ""; internal static int currentSelectedCraftingRecipe = -1; internal static bool isSelectingRecipe = false; - internal static bool isUsingCustomButtons = false; - internal static int currentIngredientListItem = -1, currentIngredientInputSlot = -1, currentInventorySlot = -1; internal static void CollectionsPagePatch(CollectionsPage __instance) { @@ -42,279 +38,6 @@ namespace stardew_access.Patches } } - 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); - if (__instance.scrambledText) - { - string toSpeak = "Scrambled Text"; - if (junimoNoteMenuQuery != toSpeak) - { - junimoNoteMenuQuery = toSpeak; - MainClass.ScreenReader.Say(toSpeak, 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 = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.I); // For the ingredients - bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); // For the items in inventory - bool isPPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.P); // For the Purchase Button - bool isVPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.V); // For the ingredient input slots - bool isBackPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Back); // 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); - } - } - string reward = __instance.getRewardNameForArea(___whichArea); - } - 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}"; - - } - 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}"); - } - } - internal static void SocialPagePatch(SocialPage __instance, List ___sprites, int ___slotPosition, List ___kidsNames) { try diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 12d3415..65673e9 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -466,10 +466,10 @@ namespace stardew_access.Patches if (menu is JunimoNoteMenu) { - GameMenuPatches.currentIngredientListItem = -1; - GameMenuPatches.currentIngredientInputSlot = -1; - GameMenuPatches.currentInventorySlot = -1; - GameMenuPatches.junimoNoteMenuQuery = ""; + BundleMenuPatches.currentIngredientListItem = -1; + BundleMenuPatches.currentIngredientInputSlot = -1; + BundleMenuPatches.currentInventorySlot = -1; + BundleMenuPatches.junimoNoteMenuQuery = ""; } if (menu is ShopMenu) From a1a7ed928149916f5bfff9efe27fdeba999498bf Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 11 Apr 2022 23:48:20 +0530 Subject: [PATCH 039/232] Bug fixes --- stardew-access/Patches/DialoguePatches.cs | 8 ++++--- stardew-access/Patches/MenuPatches.cs | 10 ++++++++ stardew-access/Patches/QuestPatches.cs | 28 +++++++++++++++++++---- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index 20a147f..1296281 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -397,11 +397,13 @@ namespace stardew_access.Patches { foreach (ClickableComponent c in __instance.itemsToGrab) { - string name = c.name; - string label = c.label; + if (c.item == null) + continue; + + string name = c.item.DisplayName; if (c.containsPoint(x, y)) - MainClass.ScreenReader.SayWithChecker($"Grab: {name} \t\n {label}", false); + MainClass.ScreenReader.SayWithChecker($"Left click to collect {name}", false); } } #endregion diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 65673e9..01ba637 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -511,6 +511,16 @@ namespace stardew_access.Patches DialoguePatches.currentDialogue = " "; } + if (menu is JojaCDMenu) + { + BundleMenuPatches.jojaCDMenuQuery = ""; + } + + if (menu is QuestLog) + { + QuestPatches.questLogQuery = " "; + } + GameMenuPatches.hoveredItemQueryKey = ""; } #endregion diff --git a/stardew-access/Patches/QuestPatches.cs b/stardew-access/Patches/QuestPatches.cs index b8d4a3d..142d811 100644 --- a/stardew-access/Patches/QuestPatches.cs +++ b/stardew-access/Patches/QuestPatches.cs @@ -9,6 +9,7 @@ namespace stardew_access.Patches internal class QuestPatches { internal static string currentDailyQuestText = " "; + internal static string questLogQuery = " "; #region For Special Orders Board internal static void SpecialOrdersBoardPatch(SpecialOrdersBoard __instance) @@ -153,28 +154,45 @@ namespace stardew_access.Patches try { bool snapMouseToRewardBox = false; + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position if (___questPage == -1) { #region Quest Lists + string toSpeak = " "; 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(); - string toSpeak = $"{name} quest"; + toSpeak = $"{name} quest"; if (daysLeft > 0 && ___pages[___currentPage][i].ShouldDisplayAsComplete()) toSpeak += $"\t\n {daysLeft} days left"; toSpeak += ___pages[___currentPage][i].ShouldDisplayAsComplete() ? " completed!" : ""; - if (__instance.questLogButtons[i].containsPoint(Game1.getOldMouseX(), Game1.getOldMouseY())) - { - MainClass.ScreenReader.SayWithChecker(toSpeak, true); - } + 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.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"; + + if (questLogQuery != toSpeak) + { + questLogQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } #endregion } else From a19186d30d4b1589c1ef214098159985b60d8929 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 12 Apr 2022 00:00:00 +0530 Subject: [PATCH 040/232] Patching tailoring menu --- stardew-access/HarmonyPatches.cs | 5 +++ stardew-access/Patches/MenuPatches.cs | 47 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index f63e185..7dc84b6 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -175,6 +175,11 @@ namespace stardew_access original: AccessTools.Method(typeof(ChooseFromListMenu), nameof(ChooseFromListMenu.draw), new Type[] { typeof(SpriteBatch) }), postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ChooseFromListMenuPatch)) ); + + harmony.Patch( + original: AccessTools.Method(typeof(TailoringMenu), nameof(TailoringMenu.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.TailoringMenuPatch)) + ); #endregion #region Quest Patches diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 01ba637..e820de6 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -15,6 +15,53 @@ namespace stardew_access.Patches private static string animalQueryMenuQuery = " "; public static Vector2? prevTile = null; + 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"; + } + + // TODO add other things + + if (toSpeak != "") + MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true); + } + 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 From 38f32c2b4827b3e8050ae01b92fda9eddb1e9ddc Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 12 Apr 2022 22:50:36 +0530 Subject: [PATCH 041/232] Patched tailoring menu --- stardew-access/Patches/DialoguePatches.cs | 3 ++ stardew-access/Patches/MenuPatches.cs | 64 ++++++++++++++++++++--- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index 1296281..3780600 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -206,6 +206,9 @@ namespace stardew_access.Patches if (Game1.activeClickableMenu is JojaCDMenu) return; + + if (Game1.activeClickableMenu is TailoringMenu) + return; #endregion string toSpeak = " "; diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index e820de6..73d3cef 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -5,14 +5,17 @@ using StardewModdingAPI; using StardewValley; using StardewValley.Locations; using StardewValley.Menus; +using StardewValley.Objects; namespace stardew_access.Patches { internal class MenuPatches { - private static string currentLevelUpTitle = " "; + internal static string currentLevelUpTitle = " "; internal static bool firstTimeInNamingMenu = true; - private static string animalQueryMenuQuery = " "; + internal static string animalQueryMenuQuery = " "; + internal static string tailoringMenuQuery = " "; + internal static bool isCyclingThroughInv = false; public static Vector2? prevTile = null; internal static void TailoringMenuPatch(TailoringMenu __instance) @@ -20,7 +23,7 @@ namespace stardew_access.Patches try { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - string toSpeak = ""; + string toSpeak = " "; if (__instance.leftIngredientSpot != null && __instance.leftIngredientSpot.containsPoint(x, y)) { @@ -50,11 +53,55 @@ namespace stardew_access.Patches { toSpeak = "Star tailoring button"; } + else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[0].containsPoint(x, y)) + { + toSpeak = "Hat Slot"; - // TODO add other things + if (((Hat)Game1.player.hat) != null) + toSpeak = $"{toSpeak}: {((Hat)Game1.player.hat).DisplayName}"; + } + else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[1].containsPoint(x, y)) + { + toSpeak = "Shirt Slot"; - if (toSpeak != "") - MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true); + if (((Clothing)Game1.player.shirtItem) != null) + toSpeak = $"{toSpeak}: {((Clothing)Game1.player.shirtItem).DisplayName}"; + } + else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[2].containsPoint(x, y)) + { + toSpeak = "Pants Slot"; + + if ((Clothing)Game1.player.pantsItem != null) + toSpeak = $"{toSpeak}: {((Clothing)Game1.player.pantsItem).DisplayName}"; + } + else + { + for (int i = 0; i < __instance.inventory.inventory.Count; i++) + { + if (!__instance.inventory.inventory[i].containsPoint(x, y)) + continue; + + if (__instance.inventory.actualInventory[i] == null) + toSpeak = "Empty slot"; + else + toSpeak = $"{__instance.inventory.actualInventory[i].Stack} {__instance.inventory.actualInventory[i].DisplayName}"; + + if (tailoringMenuQuery != $"{toSpeak}:{i}") + { + tailoringMenuQuery = $"{toSpeak}:{i}"; + MainClass.ScreenReader.Say(toSpeak, true); + } + + return; + } + } + + + if (tailoringMenuQuery != toSpeak) + { + tailoringMenuQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } } catch (System.Exception e) { @@ -568,6 +615,11 @@ namespace stardew_access.Patches QuestPatches.questLogQuery = " "; } + if (menu is TailoringMenu) + { + tailoringMenuQuery = " "; + } + GameMenuPatches.hoveredItemQueryKey = ""; } #endregion From 3b55110e1190790152fa9f7449dda279127f151a Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 12 Apr 2022 23:37:34 +0530 Subject: [PATCH 042/232] Patched pond query menu --- stardew-access/HarmonyPatches.cs | 5 + stardew-access/Patches/DialoguePatches.cs | 3 + stardew-access/Patches/MenuPatches.cs | 111 +++++++++++++++------- stardew-access/manifest.json | 2 +- 4 files changed, 87 insertions(+), 34 deletions(-) diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index 7dc84b6..c42454b 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -180,6 +180,11 @@ namespace stardew_access original: AccessTools.Method(typeof(TailoringMenu), nameof(TailoringMenu.draw), new Type[] { typeof(SpriteBatch) }), postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.TailoringMenuPatch)) ); + + harmony.Patch( + original: AccessTools.Method(typeof(PondQueryMenu), nameof(PondQueryMenu.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.PondQueryMenuPatch)) + ); #endregion #region Quest Patches diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index 3780600..7f5dd8f 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -209,6 +209,9 @@ namespace stardew_access.Patches if (Game1.activeClickableMenu is TailoringMenu) return; + + if (Game1.activeClickableMenu is PondQueryMenu) + return; #endregion string toSpeak = " "; diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 73d3cef..ecfd53b 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -3,6 +3,7 @@ using Microsoft.Xna.Framework.Input; using stardew_access.Features; using StardewModdingAPI; using StardewValley; +using StardewValley.Buildings; using StardewValley.Locations; using StardewValley.Menus; using StardewValley.Objects; @@ -15,9 +16,62 @@ namespace stardew_access.Patches internal static bool firstTimeInNamingMenu = true; internal static string animalQueryMenuQuery = " "; internal static string tailoringMenuQuery = " "; - internal static bool isCyclingThroughInv = false; + internal static string pondQueryMenuQuery = " "; public static Vector2? prevTile = null; + 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 isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); + bool isYPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Y); + bool isNPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.N); + 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 (isCPressed) + { + 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 = " "; + } + + 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 @@ -57,22 +111,22 @@ namespace stardew_access.Patches { toSpeak = "Hat Slot"; - if (((Hat)Game1.player.hat) != null) - toSpeak = $"{toSpeak}: {((Hat)Game1.player.hat).DisplayName}"; + 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 (((Clothing)Game1.player.shirtItem) != null) - toSpeak = $"{toSpeak}: {((Clothing)Game1.player.shirtItem).DisplayName}"; + 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 ((Clothing)Game1.player.pantsItem != null) - toSpeak = $"{toSpeak}: {((Clothing)Game1.player.pantsItem).DisplayName}"; + if (Game1.player.pantsItem.Value != null) + toSpeak = $"{toSpeak}: {Game1.player.pantsItem.Value.DisplayName}"; } else { @@ -535,18 +589,15 @@ namespace stardew_access.Patches { DialoguePatches.currentLetterText = " "; } - - if (menu is LevelUpMenu) + else if (menu is LevelUpMenu) { currentLevelUpTitle = " "; } - - if (menu is Billboard) + else if (menu is Billboard) { QuestPatches.currentDailyQuestText = " "; } - - if (menu is GameMenu) + else if (menu is GameMenu) { GameMenuPatches.gameMenuQueryKey = ""; GameMenuPatches.craftingPageQueryKey = ""; @@ -557,31 +608,26 @@ namespace stardew_access.Patches GameMenuPatches.currentSelectedCraftingRecipe = -1; GameMenuPatches.isSelectingRecipe = false; } - - if (menu is JunimoNoteMenu) + else if (menu is JunimoNoteMenu) { BundleMenuPatches.currentIngredientListItem = -1; BundleMenuPatches.currentIngredientInputSlot = -1; BundleMenuPatches.currentInventorySlot = -1; BundleMenuPatches.junimoNoteMenuQuery = ""; } - - if (menu is ShopMenu) + else if (menu is ShopMenu) { GameMenuPatches.shopMenuQueryKey = ""; } - - if (menu is ItemGrabMenu) + else if (menu is ItemGrabMenu) { GameMenuPatches.itemGrabMenuQueryKey = ""; } - - if (menu is GeodeMenu) + else if (menu is GeodeMenu) { GameMenuPatches.geodeMenuQueryKey = ""; } - - if (menu is CarpenterMenu) + else if (menu is CarpenterMenu) { BuildingNAnimalMenuPatches.carpenterMenuQuery = ""; BuildingNAnimalMenuPatches.isUpgrading = false; @@ -591,34 +637,33 @@ namespace stardew_access.Patches BuildingNAnimalMenuPatches.isConstructing = false; BuildingNAnimalMenuPatches.carpenterMenu = null; } - - if (menu is PurchaseAnimalsMenu) + else if (menu is PurchaseAnimalsMenu) { BuildingNAnimalMenuPatches.purchaseAnimalMenuQuery = ""; BuildingNAnimalMenuPatches.firstTimeInNamingMenu = true; BuildingNAnimalMenuPatches.purchaseAnimalsMenu = null; } - - if (menu is DialogueBox) + else if (menu is DialogueBox) { DialoguePatches.isDialogueAppearingFirstTime = true; DialoguePatches.currentDialogue = " "; } - - if (menu is JojaCDMenu) + else if (menu is JojaCDMenu) { BundleMenuPatches.jojaCDMenuQuery = ""; } - - if (menu is QuestLog) + else if (menu is QuestLog) { QuestPatches.questLogQuery = " "; } - - if (menu is TailoringMenu) + else if (menu is TailoringMenu) { tailoringMenuQuery = " "; } + else if (menu is PondQueryMenu) + { + pondQueryMenuQuery = " "; + } GameMenuPatches.hoveredItemQueryKey = ""; } diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index a795d8b..a213ac1 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.1.5", + "Version": "1.1.6", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From bf9ca47dc437f11c11df2aff30a482858b127b9d Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 12 Apr 2022 23:54:46 +0530 Subject: [PATCH 043/232] Patched forge menu --- stardew-access/HarmonyPatches.cs | 5 + stardew-access/Patches/DialoguePatches.cs | 65 ++++-------- stardew-access/Patches/MenuPatches.cs | 123 ++++++++++++++++++++++ 3 files changed, 151 insertions(+), 42 deletions(-) diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index c42454b..800c76f 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -185,6 +185,11 @@ namespace stardew_access original: AccessTools.Method(typeof(PondQueryMenu), nameof(PondQueryMenu.draw), new Type[] { typeof(SpriteBatch) }), postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.PondQueryMenuPatch)) ); + + harmony.Patch( + original: AccessTools.Method(typeof(ForgeMenu), nameof(ForgeMenu.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ForgeMenuPatch)) + ); #endregion #region Quest Patches diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index 7f5dd8f..846f158 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -149,68 +149,49 @@ namespace stardew_access.Patches #region Skip narrating hover text for certain menus if (Game1.activeClickableMenu is TitleMenu && !(((TitleMenu)Game1.activeClickableMenu).GetChildMenu() is CharacterCustomization)) return; - - if (Game1.activeClickableMenu is LetterViewerMenu || Game1.activeClickableMenu is QuestLog) + else if (Game1.activeClickableMenu is LetterViewerMenu || Game1.activeClickableMenu is QuestLog) return; - - if (Game1.activeClickableMenu is Billboard) + else if (Game1.activeClickableMenu is Billboard) return; - - if (Game1.activeClickableMenu is GeodeMenu) + else if (Game1.activeClickableMenu is GeodeMenu) return; - - if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is InventoryPage) + else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is InventoryPage) return; - - if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is CraftingPage) + else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is CraftingPage) return; - - if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is OptionsPage) + else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is OptionsPage) return; - - if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is ExitPage) + else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is ExitPage) return; - - if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is SocialPage) + else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is SocialPage) return; - - if (Game1.activeClickableMenu is ItemGrabMenu) + else if (Game1.activeClickableMenu is ItemGrabMenu) return; - - if (Game1.activeClickableMenu is ShopMenu) + else if (Game1.activeClickableMenu is ShopMenu) return; - - if (Game1.activeClickableMenu is ConfirmationDialog) + else if (Game1.activeClickableMenu is ConfirmationDialog) return; - - if (Game1.activeClickableMenu is JunimoNoteMenu) + else if (Game1.activeClickableMenu is JunimoNoteMenu) return; - - if (Game1.activeClickableMenu is CarpenterMenu) + else if (Game1.activeClickableMenu is CarpenterMenu) return; - - if (Game1.activeClickableMenu is PurchaseAnimalsMenu) + else if (Game1.activeClickableMenu is PurchaseAnimalsMenu) return; - - if (Game1.activeClickableMenu is CraftingPage) + else if (Game1.activeClickableMenu is CraftingPage) return; - - if (Game1.activeClickableMenu is AnimalQueryMenu) + else if (Game1.activeClickableMenu is AnimalQueryMenu) return; - - if (Game1.activeClickableMenu is ConfirmationDialog) + else if (Game1.activeClickableMenu is ConfirmationDialog) return; - - if (Game1.activeClickableMenu is ReadyCheckDialog) + else if (Game1.activeClickableMenu is ReadyCheckDialog) return; - - if (Game1.activeClickableMenu is JojaCDMenu) + else if (Game1.activeClickableMenu is JojaCDMenu) return; - - if (Game1.activeClickableMenu is TailoringMenu) + else if (Game1.activeClickableMenu is TailoringMenu) return; - - if (Game1.activeClickableMenu is PondQueryMenu) + else if (Game1.activeClickableMenu is PondQueryMenu) + return; + else if (Game1.activeClickableMenu is ForgeMenu) return; #endregion diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index ecfd53b..680d6e8 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -17,8 +17,112 @@ namespace stardew_access.Patches internal static string animalQueryMenuQuery = " "; internal static string tailoringMenuQuery = " "; internal static string pondQueryMenuQuery = " "; + internal static string forgeMenuQuery = " "; public static Vector2? prevTile = null; + 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}"; + } + else + { + for (int i = 0; i < __instance.inventory.inventory.Count; i++) + { + if (!__instance.inventory.inventory[i].containsPoint(x, y)) + continue; + + if (__instance.inventory.actualInventory[i] == null) + toSpeak = "Empty slot"; + else + toSpeak = $"{__instance.inventory.actualInventory[i].Stack} {__instance.inventory.actualInventory[i].DisplayName}"; + + if (forgeMenuQuery != $"{toSpeak}:{i}") + { + forgeMenuQuery = $"{toSpeak}:{i}"; + MainClass.ScreenReader.Say(toSpeak, true); + } + + 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 @@ -107,6 +211,18 @@ namespace stardew_access.Patches { 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"; @@ -155,6 +271,9 @@ namespace stardew_access.Patches { tailoringMenuQuery = toSpeak; MainClass.ScreenReader.Say(toSpeak, true); + + if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y)) + Game1.playSound("drop_item"); } } catch (System.Exception e) @@ -660,6 +779,10 @@ namespace stardew_access.Patches { tailoringMenuQuery = " "; } + else if (menu is ForgeMenu) + { + forgeMenuQuery = " "; + } else if (menu is PondQueryMenu) { pondQueryMenuQuery = " "; From ffe4fff5d2cc4b274f12cb0c8bcf127a5d66572d Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 14 Apr 2022 12:08:00 +0530 Subject: [PATCH 044/232] Patched item list menu --- stardew-access/HarmonyPatches.cs | 5 +++ stardew-access/Patches/MenuPatches.cs | 58 ++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index 800c76f..69bf890 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -190,6 +190,11 @@ namespace stardew_access original: AccessTools.Method(typeof(ForgeMenu), nameof(ForgeMenu.draw), new Type[] { typeof(SpriteBatch) }), postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ForgeMenuPatch)) ); + + harmony.Patch( + original: AccessTools.Method(typeof(ItemListMenu), nameof(ItemListMenu.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ItemListMenuPatch)) + ); #endregion #region Quest Patches diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 680d6e8..3cf6617 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -18,8 +18,64 @@ namespace stardew_access.Patches internal static string tailoringMenuQuery = " "; internal static string pondQueryMenuQuery = " "; internal static string forgeMenuQuery = " "; + internal static string itemListMenuQuery = " "; + internal static string itemListMenuPreviousList = " "; 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 = " "; + bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); + + if (isCPressed) + itemListMenuPreviousList = " "; + + 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 = "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 || itemListMenuPreviousList != currentList) + { + itemListMenuQuery = toSpeak; + + if (itemListMenuPreviousList != currentList) + { + itemListMenuPreviousList = currentList; + toSpeak = $"{currentList} \n {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 @@ -129,8 +185,6 @@ namespace stardew_access.Patches { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); - bool isYPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Y); - bool isNPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.N); string toSpeak = " ", extra = ""; if (___confirmingEmpty) From 559922c0a9790ac43032663b36b9ab5a2f77d868 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 14 Apr 2022 12:34:05 +0530 Subject: [PATCH 045/232] Fixed item list menu --- stardew-access/Patches/MenuPatches.cs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 3cf6617..d2298a0 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -19,7 +19,6 @@ namespace stardew_access.Patches internal static string pondQueryMenuQuery = " "; internal static string forgeMenuQuery = " "; internal static string itemListMenuQuery = " "; - internal static string itemListMenuPreviousList = " "; public static Vector2? prevTile = null; internal static void ItemListMenuPatch(ItemListMenu __instance, string ___title, int ___currentTab, int ___totalValueOfItems, List ___itemsToList) @@ -28,10 +27,6 @@ namespace stardew_access.Patches { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position string toSpeak = " ", currentList = " "; - bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); - - if (isCPressed) - itemListMenuPreviousList = " "; for (int i = ___currentTab * __instance.itemsPerCategoryPage; i < ___currentTab * __instance.itemsPerCategoryPage + __instance.itemsPerCategoryPage; i++) { @@ -51,22 +46,15 @@ namespace stardew_access.Patches } if (__instance.okButton != null && __instance.okButton.containsPoint(x, y)) - toSpeak = "ok button"; + 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 || itemListMenuPreviousList != currentList) + if (itemListMenuQuery != toSpeak) { itemListMenuQuery = toSpeak; - - if (itemListMenuPreviousList != currentList) - { - itemListMenuPreviousList = currentList; - toSpeak = $"{currentList} \n {toSpeak}"; - } - MainClass.ScreenReader.Say(toSpeak, true); } } From f0a85e0be911049c761a07bec0a3c3c78b01ec2f Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 14 Apr 2022 12:59:23 +0530 Subject: [PATCH 046/232] Patched field office menu --- stardew-access/HarmonyPatches.cs | 5 + stardew-access/Patches/DialoguePatches.cs | 4 + stardew-access/Patches/MenuPatches.cs | 132 ++++++++++++++++++++++ 3 files changed, 141 insertions(+) diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index 69bf890..96e0da4 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -195,6 +195,11 @@ namespace stardew_access original: AccessTools.Method(typeof(ItemListMenu), nameof(ItemListMenu.draw), new Type[] { typeof(SpriteBatch) }), postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ItemListMenuPatch)) ); + + harmony.Patch( + original: AccessTools.Method(typeof(FieldOfficeMenu), nameof(FieldOfficeMenu.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.FieldOfficeMenuPatch)) + ); #endregion #region Quest Patches diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index 846f158..1228cb8 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -193,6 +193,10 @@ namespace stardew_access.Patches return; else if (Game1.activeClickableMenu is ForgeMenu) return; + else if (Game1.activeClickableMenu is ItemListMenu) + return; + else if (Game1.activeClickableMenu is FieldOfficeMenu) + return; #endregion string toSpeak = " "; diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index d2298a0..33d0581 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -20,6 +20,130 @@ namespace stardew_access.Patches internal static string forgeMenuQuery = " "; internal static string itemListMenuQuery = " "; public static Vector2? prevTile = null; + private static string fieldOfficeMenuQuery = " "; + + 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 + { + for (int i = 0; i < __instance.inventory.inventory.Count; i++) + { + if (!__instance.inventory.inventory[i].containsPoint(x, y)) + continue; + + if (__instance.inventory.actualInventory[i] == null) + toSpeak = "Empty slot"; + else + toSpeak = $"{__instance.inventory.actualInventory[i].Stack} {__instance.inventory.actualInventory[i].DisplayName}"; + + if (fieldOfficeMenuQuery != $"{toSpeak}:{i}") + { + fieldOfficeMenuQuery = $"{toSpeak}:{i}"; + MainClass.ScreenReader.Say(toSpeak, true); + } + + 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 (__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 ItemListMenuPatch(ItemListMenu __instance, string ___title, int ___currentTab, int ___totalValueOfItems, List ___itemsToList) { @@ -825,6 +949,14 @@ namespace stardew_access.Patches { forgeMenuQuery = " "; } + else if (menu is ItemListMenu) + { + itemListMenuQuery = " "; + } + else if (menu is FieldOfficeMenu) + { + fieldOfficeMenuQuery = " "; + } else if (menu is PondQueryMenu) { pondQueryMenuQuery = " "; From 435118298444083b0ddbbc6d9a789ec3622421a5 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Fri, 15 Apr 2022 14:27:52 +0530 Subject: [PATCH 047/232] Organized code and added parrot perch to read tile --- .gitignore | 1 + stardew-access/API.cs | 4 +- stardew-access/Features/Radar.cs | 7 +- stardew-access/Features/ReadTile.cs | 838 +---------------- stardew-access/Features/TileInfo.cs | 876 ++++++++++++++++++ stardew-access/HarmonyPatches.cs | 24 +- stardew-access/Patches/DialoguePatches.cs | 3 +- ...mMenuPatches.cs => DonationMenuPatches.cs} | 137 ++- stardew-access/Patches/MenuPatches.cs | 132 +-- 9 files changed, 1039 insertions(+), 983 deletions(-) create mode 100644 stardew-access/Features/TileInfo.cs rename stardew-access/Patches/{MuseumMenuPatches.cs => DonationMenuPatches.cs} (71%) diff --git a/.gitignore b/.gitignore index 7bc055e..82c0f04 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore .vscode/* +.git-old/ # User-specific files *.rsuser diff --git a/stardew-access/API.cs b/stardew-access/API.cs index 902eed7..0362154 100644 --- a/stardew-access/API.cs +++ b/stardew-access/API.cs @@ -37,7 +37,7 @@ namespace stardew_access.ScreenReader /// Name of the object as the first item (Item1) and category as the second item (Item2). Returns null if no object found. public (string?, string?) GetNameWithCategoryNameAtTile(Vector2 tile) { - return ReadTile.getNameWithCategoryNameAtTile(tile); + return TileInfo.getNameWithCategoryNameAtTile(tile); } /// @@ -47,7 +47,7 @@ namespace stardew_access.ScreenReader /// Name of the object. Returns null if no object found. public string? GetNameAtTile(Vector2 tile) { - return ReadTile.getNameAtTile(tile); + return TileInfo.getNameAtTile(tile); } /// Speaks the text via the loaded screen reader (if any). diff --git a/stardew-access/Features/Radar.cs b/stardew-access/Features/Radar.cs index 62b4c27..c0b0eaa 100644 --- a/stardew-access/Features/Radar.cs +++ b/stardew-access/Features/Radar.cs @@ -1,7 +1,6 @@ using Microsoft.Xna.Framework; using StardewValley; using StardewValley.Objects; -using StardewValley.TerrainFeatures; namespace stardew_access.Features { @@ -187,7 +186,7 @@ namespace stardew_access.Features public (bool, string?, string) CheckTile(Vector2 position) { - (string? name, CATEGORY? category) tileDetail = ReadTile.getNameWithCategoryAtTile(position); + (string? name, CATEGORY? category) tileDetail = TileInfo.getNameWithCategoryAtTile(position); if (tileDetail.name == null) return (false, null, CATEGORY.Others.ToString()); @@ -204,7 +203,7 @@ namespace stardew_access.Features { if (Game1.currentLocation.isObjectAtTile((int)position.X, (int)position.Y)) { - (string? name, CATEGORY category) objDetails = ReadTile.getObjectAtTile((int)position.X, (int)position.Y); + (string? name, CATEGORY category) objDetails = TileInfo.getObjectAtTile((int)position.X, (int)position.Y); string? objectName = objDetails.name; CATEGORY category = objDetails.category; StardewValley.Object obj = Game1.currentLocation.getObjectAtTile((int)position.X, (int)position.Y); @@ -228,7 +227,7 @@ namespace stardew_access.Features } else { - (string? name, CATEGORY? category) tileDetail = ReadTile.getNameWithCategoryAtTile(position); + (string? name, CATEGORY? category) tileDetail = TileInfo.getNameWithCategoryAtTile(position); if (tileDetail.name != null) { if (tileDetail.category == null) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 33c5931..54dcb92 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -1,17 +1,9 @@ using Microsoft.Xna.Framework; using StardewModdingAPI; using StardewValley; -using StardewValley.Buildings; -using StardewValley.Locations; -using StardewValley.Objects; -using StardewValley.TerrainFeatures; namespace stardew_access.Features { - public enum MachineState - { - Ready, Busy, Waiting - } public class ReadTile { public static bool isReadingTile = false; @@ -51,9 +43,9 @@ namespace stardew_access.Features MainClass.ScreenReader.PrevTextTile = " "; } - bool isColliding = isCollidingAtTile(x, y); + bool isColliding = TileInfo.isCollidingAtTile(x, y); - string? toSpeak = getNameAtTile(tile); + string? toSpeak = TileInfo.getNameAtTile(tile); #region Narrate toSpeak if (toSpeak != null) @@ -81,831 +73,5 @@ namespace stardew_access.Features MainClass.ErrorLog($"Error in Read Tile:\n{e.Message}\n{e.StackTrace}"); } } - - ///Returns the name of the object at tile alongwith it's category's name - public static (string? name, string? categoryName) getNameWithCategoryNameAtTile(Vector2 tile) - { - (string? name, CATEGORY? category) tileDetail = getNameWithCategoryAtTile(tile); - - if (tileDetail.category == null) - tileDetail.category = CATEGORY.Others; - - return (tileDetail.name, tileDetail.category.ToString()); - } - - ///Returns the name of the object at tile - public static string? getNameAtTile(Vector2 tile) - { - return getNameWithCategoryAtTile(tile).name; - } - - ///Returns the name of the object at tile alongwith it's category - public static (string? name, CATEGORY? category) getNameWithCategoryAtTile(Vector2 tile) - { - int x = (int)tile.X; - int y = (int)tile.Y; - string? toReturn = ""; - CATEGORY? category = CATEGORY.Others; - - bool isColliding = isCollidingAtTile(x, y); - Dictionary> terrainFeature = Game1.currentLocation.terrainFeatures.FieldDict; - string? door = getDoorAtTile(x, y); - (CATEGORY? category, string? name) tileInfo = getTileInfo(x, y); - string? junimoBundle = getJunimoBundleAt(x, y); - string? resourceClump = getResourceClumpAtTile(x, y); - string? farmAnimal = getFarmAnimalAt(Game1.currentLocation, x, y); - - if (Game1.currentLocation.isCharacterAtTile(tile) != null) - { - NPC npc = Game1.currentLocation.isCharacterAtTile(tile); - toReturn = npc.displayName; - if (npc.isVillager() || npc.CanSocialize) - category = CATEGORY.Farmers; - else - category = CATEGORY.NPCs; - } - else if (farmAnimal != null) - { - toReturn = farmAnimal; - category = CATEGORY.FarmAnimals; - } - else if (Game1.currentLocation.isWaterTile(x, y) && isColliding) - { - toReturn = "Water"; - category = CATEGORY.WaterTiles; - } - else if (Game1.currentLocation.isObjectAtTile(x, y)) - { - (string? name, CATEGORY? category) obj = getObjectAtTile(x, y); - toReturn = obj.name; - category = obj.category; - } - else if (terrainFeature.ContainsKey(tile)) - { - (string? name, CATEGORY category) tf = getTerrainFeatureAtTile(terrainFeature[tile]); - string? terrain = tf.name; - if (terrain != null) - { - toReturn = terrain; - category = tf.category; - } - - } - else if (Game1.currentLocation.getLargeTerrainFeatureAt(x, y) != null) - { - toReturn = getBushAtTile(x, y); - category = CATEGORY.Bush; - } - else if (resourceClump != null) - { - toReturn = resourceClump; - category = CATEGORY.ResourceClumps; - } - else if (door != null) - { - toReturn = door; - category = CATEGORY.Doors; - } - else if (isMineDownLadderAtTile(x, y)) - { - toReturn = "Ladder"; - category = CATEGORY.Doors; - } - else if (isMineUpLadderAtTile(x, y)) - { - toReturn = "Up Ladder"; - category = CATEGORY.Doors; - } - else if (isElevatorAtTile(x, y)) - { - toReturn = "Elevator"; - category = CATEGORY.Doors; - } - else if (tileInfo.name != null) - { - toReturn = tileInfo.name; - category = tileInfo.category; - } - else if (junimoBundle != null) - { - toReturn = junimoBundle; - category = CATEGORY.JunimoBundle; - } - - if (toReturn == "") - return (null, category); - - return (toReturn, category); - } - - public static string? getBushAtTile(int x, int y) - { - string? toReturn = null; - Bush bush = (Bush)Game1.currentLocation.getLargeTerrainFeatureAt(x, y); - int size = bush.size.Value; - - #region Check if bush is harvestable or not - if (!bush.townBush.Value && (int)bush.tileSheetOffset.Value == 1 && bush.inBloom(Game1.GetSeasonForLocation(Game1.currentLocation), Game1.dayOfMonth)) - { - // Taken from the game's code - string season = ((int)bush.overrideSeason.Value == -1) ? Game1.GetSeasonForLocation(Game1.currentLocation) : Utility.getSeasonNameFromNumber(bush.overrideSeason.Value); - int shakeOff = -1; - if (!(season == "spring")) - { - if (season == "fall") - { - shakeOff = 410; - } - } - else - { - shakeOff = 296; - } - if ((int)size == 3) - { - shakeOff = 815; - } - if ((int)size == 4) - { - shakeOff = 73; - } - if (shakeOff == -1) - { - return null; - } - - toReturn = "Harvestable"; - } - #endregion - - if (bush.townBush.Value) - toReturn = $"{toReturn} Town Bush"; - else if (bush.greenhouseBush.Value) - toReturn = $"{toReturn} Greenhouse Bush"; - else - toReturn = $"{toReturn} Bush"; - - return toReturn; - } - - public static string? getJunimoBundleAt(int x, int y) - { - string? name = null; - if (Game1.currentLocation is CommunityCenter communityCenter) - { - name = (x, y) switch - { - (14, 5) => "Pantry", - (14, 23) => "Crafts Room", - (40, 10) => "Fish Tank", - (63, 14) => "Boiler Room", - (55, 6) => "Vault", - (46, 11) => "Bulletin Board", - _ => null, - }; - if (name != null && communityCenter.shouldNoteAppearInArea(CommunityCenter.getAreaNumberFromName(name))) - return $"{name} bundle"; - } - else if (Game1.currentLocation is AbandonedJojaMart) - { - name = (x, y) switch - { - (8, 8) => "Missing", - _ => null, - }; - - if (name != null) - return $"{name} bundle"; - } - - return null; - } - - public static bool isCollidingAtTile(int x, int y) - { - Rectangle rect = new Rectangle(x * 64 + 1, y * 64 + 1, 62, 62); - - if (Game1.currentLocation.isCollidingPosition(rect, Game1.viewport, true, 0, glider: false, Game1.player, pathfinding: true)) - { - return true; - } - - if (Game1.currentLocation is Woods && getStumpsInWoods(x, y) != null) - return true; - - return false; - } - - public static string? getFarmAnimalAt(GameLocation? location, int x, int y, bool onlyName = false) - { - if (location == null) - return null; - - if (location is not Farm && location is not AnimalHouse) - return null; - - List? farmAnimals = null; - - if (location is Farm) - farmAnimals = ((Farm)location).getAllFarmAnimals(); - else if (location is AnimalHouse) - farmAnimals = ((AnimalHouse)location).animals.Values.ToList(); - - if (farmAnimals == null || farmAnimals.Count <= 0) - return null; - - for (int i = 0; i < farmAnimals.Count; i++) - { - int fx = farmAnimals[i].getTileX(); - int fy = farmAnimals[i].getTileY(); - - if (fx.Equals(x) && fy.Equals(y)) - { - string name = farmAnimals[i].displayName; - int age = farmAnimals[i].age.Value; - string type = farmAnimals[i].displayType; - - if (onlyName) - return name; - - return $"{name}, {type}, age {age}"; - } - } - - return null; - } - - /// - /// - /// - /// - /// - /// category: This is the category of the tile. Default to Furnitures. - ///
name: This is the name of the tile. Default to null if the tile tile has nothing on it.
- public static (CATEGORY? category, string? name) getTileInfo(int x, int y) - { - - int? index = null; - - if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null) - index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex; - /* Add More - MainClass.monitor.Log(index.ToString(), LogLevel.Debug); - */ - - if (Game1.currentLocation is Farm) - { - Building building = ((Farm)Game1.currentLocation).getBuildingAt(new Vector2(x, y)); - if (building != null) - { - return (CATEGORY.Buildings, building.buildingType.Value); - } - } - - if (index != null) - { - switch (index) - { - case 1955: - case 41: - return (CATEGORY.Furnitures, "Mail Box"); - case 1003: - return (CATEGORY.Furnitures, "Street lamp"); - case 78: - return (CATEGORY.Furnitures, "Trash bin"); - case 617: - return (CATEGORY.Furnitures, "Daily quest"); - case 616: - return (CATEGORY.Furnitures, "Calender"); - } - - if (Game1.currentLocation is FarmHouse || Game1.currentLocation is IslandFarmHouse) - { - switch (index) - { - case 173: - return (CATEGORY.Furnitures, "Fridge"); - case 169: - case 170: - case 171: - case 172: - return (CATEGORY.Furnitures, "Kitchen"); - } - } - - } - - return (null, null); - } - - public static (string? name, CATEGORY category) getTerrainFeatureAtTile(Netcode.NetRef terrain) - { - string? toReturn = null; - CATEGORY category = CATEGORY.Others; - - if (terrain.Get() is HoeDirt) - { - category = CATEGORY.Crops; - HoeDirt dirt = (HoeDirt)terrain.Get(); - if (dirt.crop != null) - { - string cropName = Game1.objectInformation[dirt.crop.indexOfHarvest.Value].Split('/')[0]; - toReturn = $"{cropName}"; - - bool isWatered = dirt.state.Value == HoeDirt.watered; - bool isHarvestable = dirt.readyForHarvest(); - bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer; - - if (isWatered) - toReturn = "Watered " + toReturn; - - if (isFertilized) - toReturn = "Fertilized " + toReturn; - - if (isHarvestable) - toReturn = "Harvestable " + toReturn; - } - else - { - toReturn = "Soil"; - bool isWatered = dirt.state.Value == HoeDirt.watered; - bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer; - - if (isWatered) - toReturn = "Watered " + toReturn; - - if (isFertilized) - toReturn = "Fertilized " + toReturn; - } - } - else if (terrain.Get() is GiantCrop) - { - category = CATEGORY.Crops; - int whichCrop = ((GiantCrop)terrain.Get()).which.Value; - switch (whichCrop) - { - case 0: - toReturn = "Cauliflower"; - break; - case 1: - toReturn = "Melon"; - break; - case 2: - toReturn = "Pumpkin"; - break; - } - } - else if (terrain.Get() is CosmeticPlant) - { - category = CATEGORY.Furnitures; - CosmeticPlant cosmeticPlant = (CosmeticPlant)terrain.Get(); - toReturn = cosmeticPlant.textureName().ToLower(); - - if (toReturn.Contains("terrain")) - toReturn.Replace("terrain", ""); - - if (toReturn.Contains("feature")) - toReturn.Replace("feature", ""); - } - else if (terrain.Get() is Flooring && MainClass.Config.ReadFlooring) - { - category = CATEGORY.Flooring; - Flooring flooring = (Flooring)terrain.Get(); - bool isPathway = flooring.isPathway.Get(); - bool isSteppingStone = flooring.isSteppingStone.Get(); - - toReturn = "Flooring"; - - if (isPathway) - toReturn = "Pathway"; - - if (isSteppingStone) - toReturn = "Stepping Stone"; - } - else if (terrain.Get() is FruitTree) - { - category = CATEGORY.Trees; - toReturn = getFruitTree((FruitTree)terrain.Get()); - } - else if (terrain.Get() is Grass) - { - category = CATEGORY.Debris; - toReturn = "Grass"; - } - else if (terrain.Get() is Tree) - { - category = CATEGORY.Trees; - toReturn = getTree((Tree)terrain.Get()); - } - else if (terrain.Get() is Quartz) - { - category = CATEGORY.MineItems; - toReturn = "Quartz"; - } - - return (toReturn, category); - } - - public static string getFruitTree(FruitTree fruitTree) - { - int stage = fruitTree.growthStage.Value; - int fruitIndex = fruitTree.indexOfFruit.Get(); - - string toReturn = Game1.objectInformation[fruitIndex].Split('/')[0]; - - if (stage == 0) - toReturn = $"{toReturn} seed"; - else if (stage == 1) - toReturn = $"{toReturn} sprout"; - else if (stage == 2) - toReturn = $"{toReturn} sapling"; - else if (stage == 3) - toReturn = $"{toReturn} bush"; - else if (stage >= 4) - toReturn = $"{toReturn} tree"; - - if (fruitTree.fruitsOnTree.Value > 0) - toReturn = $"Harvestable {toReturn}"; - - return toReturn; - } - - public static string getTree(Tree tree) - { - int treeType = tree.treeType.Value; - int treeStage = tree.growthStage.Value; - string treeName = "tree"; - string seedName = ""; - - // Return with the name if it's one of the 3 special trees - switch (treeType) - { - case 4: - case 5: - return "Winter Tree"; - case 6: - return "Palm Tree"; - case 7: - return "Mushroom Tree"; - } - - - if (treeType <= 3) - seedName = Game1.objectInformation[308 + treeType].Split('/')[0]; - else if (treeType == 8) - seedName = Game1.objectInformation[292].Split('/')[0]; - - if (treeStage >= 1) - { - switch (seedName.ToLower()) - { - case "mahogany seed": - treeName = "Mahogany"; - break; - case "acorn": - treeName = "Oak"; - break; - case "maple seed": - treeName = "Maple"; - break; - case "pine cone": - treeName = "Pine"; - break; - default: - treeName = "Coconut"; - break; - } - - if (treeStage == 1) - treeName = $"{treeName} sprout"; - else if (treeStage == 2) - treeName = $"{treeName} sapling"; - else if (treeStage == 3 || treeStage == 4) - treeName = $"{treeName} bush"; - else if (treeStage >= 5) - treeName = $"{treeName} tree"; - - return treeName; - } - - return seedName; - } - - #region Objects - public static (string? name, CATEGORY category) getObjectAtTile(int x, int y) - { - (string? name, CATEGORY category) toReturn = (null, CATEGORY.Others); - - StardewValley.Object obj = Game1.currentLocation.getObjectAtTile(x, y); - int index = obj.ParentSheetIndex; - toReturn.name = obj.DisplayName; - - // Get object names based on index - (string? name, CATEGORY category) correctNameAndCategory = getCorrectNameAndCategoryFromIndex(index); - if (correctNameAndCategory.name != null) - toReturn = correctNameAndCategory; - - if (toReturn.name.ToLower().Equals("stone")) // Fix for `Busy stone` - toReturn.category = CATEGORY.Debris; - - if (obj is Chest) - { - Chest chest = (Chest)obj; - toReturn = (chest.DisplayName, CATEGORY.Chests); - } - - if (obj is Furniture) - toReturn.category = CATEGORY.Furnitures; - - if (toReturn.category == CATEGORY.Others) // Fix for `Harvestable table` and `Busy nodes` - { - MachineState machineState = GetMachineState(obj); - if (machineState == MachineState.Ready) - toReturn.name = $"Harvestable {toReturn.name}"; - else if (machineState == MachineState.Busy) - toReturn.name = $"Busy {toReturn.name}"; - } - return toReturn; - } - - private static MachineState GetMachineState(StardewValley.Object machine) - { - if (machine is CrabPot crabPot) - if (crabPot.bait.Value is not null && crabPot.heldObject.Value is null) - return MachineState.Busy; - return GetMachineState(machine.readyForHarvest.Value, machine.MinutesUntilReady, machine.heldObject.Value); - } - - private static MachineState GetMachineState(bool readyForHarvest, int minutesUntilReady, StardewValley.Object? heldObject) - { - if (readyForHarvest || (heldObject is not null && minutesUntilReady <= 0)) - return MachineState.Ready; - else if (minutesUntilReady > 0) - return MachineState.Busy; - else - return MachineState.Waiting; - } - - private static (string? name, CATEGORY category) getCorrectNameAndCategoryFromIndex(int index) - { - switch (index) - { - case 313: - case 314: - case 315: - case 316: - case 317: - case 318: - case 452: - case 674: - case 675: - case 676: - case 677: - case 678: - case 679: - case 750: - case 784: - case 785: - case 786: - return ("Weed", CATEGORY.Debris); - case 792: - case 793: - case 794: - return ("Fertile weed", CATEGORY.Debris); - case 319: - case 320: - case 321: - return ("Ice crystal", CATEGORY.Debris); - case 75: - return ("Geode", CATEGORY.MineItems); - case 32: - case 34: - case 36: - case 38: - case 40: - case 42: - case 48: - case 50: - case 52: - case 54: - case 56: - case 58: - return ("Coloured stone", CATEGORY.Debris); - case 668: - case 670: - case 845: - case 846: - case 847: - return ("Mine stone", CATEGORY.MineItems); - case 818: - return ("Clay stone", CATEGORY.Debris); - case 816: - case 817: - return ("Fossil stone", CATEGORY.Debris); - case 118: - case 120: - case 122: - case 124: - return ("Barrel", CATEGORY.MineItems); - case 119: - case 121: - case 123: - case 125: - return ("Item box", CATEGORY.MineItems); - } - - if (Game1.currentLocation is Mine or MineShaft) - { - switch (index) - { - case 76: - return ("Frozen geode", CATEGORY.MineItems); - case 77: - return ("Magma geode", CATEGORY.MineItems); - case 8: - case 66: - return ("Amethyst node", CATEGORY.MineItems); - case 14: - case 62: - return ("Aquamarine node", CATEGORY.MineItems); - case 843: - case 844: - return ("Cinder shard node", CATEGORY.MineItems); - case 2: - case 72: - return ("Diamond node", CATEGORY.MineItems); - case 12: - case 60: - return ("Emerald node", CATEGORY.MineItems); - case 44: - return ("Gem node", CATEGORY.MineItems); - case 6: - case 70: - return ("Jade node", CATEGORY.MineItems); - case 46: - return ("Mystic stone", CATEGORY.MineItems); - case 74: - return ("Prismatic node", CATEGORY.MineItems); - case 4: - case 64: - return ("Ruby node", CATEGORY.MineItems); - case 10: - case 68: - return ("Topaz node", CATEGORY.MineItems); - case 819: - return ("Omni geode node", CATEGORY.MineItems); - case 751: - case 849: - return ("Copper node", CATEGORY.MineItems); - case 764: - return ("Gold node", CATEGORY.MineItems); - case 765: - return ("Iridium node", CATEGORY.MineItems); - case 290: - case 850: - return ("Iron node", CATEGORY.MineItems); - } - } - - return (null, CATEGORY.Others); - } - #endregion - - public static bool isMineDownLadderAtTile(int x, int y) - { - try - { - if (Game1.currentLocation is Mine or MineShaft) - { - int? index = null; - - if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null) - index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex; - - if (index == 173 || index == 174) - return true; - } - } - catch (Exception) { } - - return false; - } - - public static bool isMineUpLadderAtTile(int x, int y) - { - try - { - if (Game1.currentLocation is Mine or MineShaft) - { - int? index = null; - - if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null) - index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex; - - if (index == 115) - return true; - } - } - catch (Exception) { } - - return false; - } - - public static bool isElevatorAtTile(int x, int y) - { - try - { - if (Game1.currentLocation is Mine or MineShaft) - { - int? index = null; - - if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null) - index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex; - - if (index == 112) - return true; - } - } - catch (Exception) { } - - return false; - } - - public static string? getDoorAtTile(int x, int y) - { - Point tilePoint = new Point(x, y); - StardewValley.Network.NetPointDictionary doorList = Game1.currentLocation.doors; - - for (int i = 0; i < doorList.Count(); i++) - { - if (doorList.ContainsKey(tilePoint)) - { - string? doorName; - doorList.TryGetValue(tilePoint, out doorName); - - if (doorName != null) - return $"{doorName} door"; - else - return "door"; - } - } - - return null; - } - - public static string? getResourceClumpAtTile(int x, int y) - { - if (Game1.currentLocation is Woods) - return getStumpsInWoods(x, y); - - for (int i = 0; i < Game1.currentLocation.resourceClumps.Count; i++) - { - if (Game1.currentLocation.resourceClumps[i].occupiesTile(x, y)) - { - int index = Game1.currentLocation.resourceClumps[i].parentSheetIndex.Value; - - switch (index) - { - case 600: - return "Large Stump"; - case 602: - return "Hollow Log"; - case 622: - return "Meteorite"; - case 752: - case 754: - case 756: - case 758: - return "Mine Rock"; - case 672: - return "Boulder"; - default: - return "Unknown"; - } - } - } - - return null; - } - - public static string? getStumpsInWoods(int x, int y) - { - if (Game1.currentLocation is not Woods) - return null; - - if ((x == 8 || x == 9) && y == 7) - { - return "Old Master Cannoli"; - } - Netcode.NetObjectList stumps = ((Woods)Game1.currentLocation).stumps; - for (int i = 0; i < stumps.Count; i++) - { - if (stumps[i].occupiesTile(x, y)) - { - return "Large Stump"; - } - } - return null; - } } } diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs new file mode 100644 index 0000000..1fb346f --- /dev/null +++ b/stardew-access/Features/TileInfo.cs @@ -0,0 +1,876 @@ +using Microsoft.Xna.Framework; +using StardewValley; +using StardewValley.Buildings; +using StardewValley.Locations; +using StardewValley.Objects; +using StardewValley.TerrainFeatures; + +namespace stardew_access.Features +{ + public enum MachineState + { + Ready, Busy, Waiting + } + + public class TileInfo + { + ///Returns the name of the object at tile alongwith it's category's name + public static (string? name, string? categoryName) getNameWithCategoryNameAtTile(Vector2 tile) + { + (string? name, CATEGORY? category) tileDetail = getNameWithCategoryAtTile(tile); + + if (tileDetail.category == null) + tileDetail.category = CATEGORY.Others; + + return (tileDetail.name, tileDetail.category.ToString()); + } + + ///Returns the name of the object at tile + public static string? getNameAtTile(Vector2 tile) + { + return getNameWithCategoryAtTile(tile).name; + } + + ///Returns the name of the object at tile alongwith it's category + public static (string? name, CATEGORY? category) getNameWithCategoryAtTile(Vector2 tile) + { + int x = (int)tile.X; + int y = (int)tile.Y; + string? toReturn = ""; + CATEGORY? category = CATEGORY.Others; + + bool isColliding = isCollidingAtTile(x, y); + Dictionary> terrainFeature = Game1.currentLocation.terrainFeatures.FieldDict; + string? door = getDoorAtTile(x, y); + (CATEGORY? category, string? name) tileInfo = getTileInfo(x, y); + string? junimoBundle = getJunimoBundleAt(x, y); + string? resourceClump = getResourceClumpAtTile(x, y); + string? farmAnimal = getFarmAnimalAt(Game1.currentLocation, x, y); + string? parrot = getParrotPerchAtTile(x, y); + + if (Game1.currentLocation.isCharacterAtTile(tile) != null) + { + NPC npc = Game1.currentLocation.isCharacterAtTile(tile); + toReturn = npc.displayName; + if (npc.isVillager() || npc.CanSocialize) + category = CATEGORY.Farmers; + else + category = CATEGORY.NPCs; + } + else if (farmAnimal != null) + { + toReturn = farmAnimal; + category = CATEGORY.FarmAnimals; + } + else if (Game1.currentLocation.isWaterTile(x, y) && isColliding) + { + toReturn = "Water"; + category = CATEGORY.WaterTiles; + } + else if (Game1.currentLocation.isObjectAtTile(x, y)) + { + (string? name, CATEGORY? category) obj = getObjectAtTile(x, y); + toReturn = obj.name; + category = obj.category; + } + else if (terrainFeature.ContainsKey(tile)) + { + (string? name, CATEGORY category) tf = getTerrainFeatureAtTile(terrainFeature[tile]); + string? terrain = tf.name; + if (terrain != null) + { + toReturn = terrain; + category = tf.category; + } + + } + else if (Game1.currentLocation.getLargeTerrainFeatureAt(x, y) != null) + { + toReturn = getBushAtTile(x, y); + category = CATEGORY.Bush; + } + else if (resourceClump != null) + { + toReturn = resourceClump; + category = CATEGORY.ResourceClumps; + } + else if (door != null) + { + toReturn = door; + category = CATEGORY.Doors; + } + else if (isMineDownLadderAtTile(x, y)) + { + toReturn = "Ladder"; + category = CATEGORY.Doors; + } + else if (isMineUpLadderAtTile(x, y)) + { + toReturn = "Up Ladder"; + category = CATEGORY.Doors; + } + else if (isElevatorAtTile(x, y)) + { + toReturn = "Elevator"; + category = CATEGORY.Doors; + } + else if (parrot != null) + { + toReturn = parrot; + category = CATEGORY.Buildings; + } + else if (tileInfo.name != null) + { + toReturn = tileInfo.name; + category = tileInfo.category; + } + else if (junimoBundle != null) + { + toReturn = junimoBundle; + category = CATEGORY.JunimoBundle; + } + + if (toReturn == "") + return (null, category); + + return (toReturn, category); + } + + public static string? getBushAtTile(int x, int y) + { + string? toReturn = null; + Bush bush = (Bush)Game1.currentLocation.getLargeTerrainFeatureAt(x, y); + int size = bush.size.Value; + + #region Check if bush is harvestable or not + if (!bush.townBush.Value && (int)bush.tileSheetOffset.Value == 1 && bush.inBloom(Game1.GetSeasonForLocation(Game1.currentLocation), Game1.dayOfMonth)) + { + // Taken from the game's code + string season = ((int)bush.overrideSeason.Value == -1) ? Game1.GetSeasonForLocation(Game1.currentLocation) : Utility.getSeasonNameFromNumber(bush.overrideSeason.Value); + int shakeOff = -1; + if (!(season == "spring")) + { + if (season == "fall") + { + shakeOff = 410; + } + } + else + { + shakeOff = 296; + } + if ((int)size == 3) + { + shakeOff = 815; + } + if ((int)size == 4) + { + shakeOff = 73; + } + if (shakeOff == -1) + { + return null; + } + + toReturn = "Harvestable"; + } + #endregion + + if (bush.townBush.Value) + toReturn = $"{toReturn} Town Bush"; + else if (bush.greenhouseBush.Value) + toReturn = $"{toReturn} Greenhouse Bush"; + else + toReturn = $"{toReturn} Bush"; + + return toReturn; + } + + public static string? getJunimoBundleAt(int x, int y) + { + string? name = null; + if (Game1.currentLocation is CommunityCenter communityCenter) + { + name = (x, y) switch + { + (14, 5) => "Pantry", + (14, 23) => "Crafts Room", + (40, 10) => "Fish Tank", + (63, 14) => "Boiler Room", + (55, 6) => "Vault", + (46, 11) => "Bulletin Board", + _ => null, + }; + if (name != null && communityCenter.shouldNoteAppearInArea(CommunityCenter.getAreaNumberFromName(name))) + return $"{name} bundle"; + } + else if (Game1.currentLocation is AbandonedJojaMart) + { + name = (x, y) switch + { + (8, 8) => "Missing", + _ => null, + }; + + if (name != null) + return $"{name} bundle"; + } + + return null; + } + + public static bool isCollidingAtTile(int x, int y) + { + Rectangle rect = new Rectangle(x * 64 + 1, y * 64 + 1, 62, 62); + + if (Game1.currentLocation.isCollidingPosition(rect, Game1.viewport, true, 0, glider: false, Game1.player, pathfinding: true)) + { + return true; + } + + if (Game1.currentLocation is Woods && getStumpsInWoods(x, y) != null) + return true; + + return false; + } + + public static string? getFarmAnimalAt(GameLocation? location, int x, int y, bool onlyName = false) + { + if (location == null) + return null; + + if (location is not Farm && location is not AnimalHouse) + return null; + + List? farmAnimals = null; + + if (location is Farm) + farmAnimals = ((Farm)location).getAllFarmAnimals(); + else if (location is AnimalHouse) + farmAnimals = ((AnimalHouse)location).animals.Values.ToList(); + + if (farmAnimals == null || farmAnimals.Count <= 0) + return null; + + for (int i = 0; i < farmAnimals.Count; i++) + { + int fx = farmAnimals[i].getTileX(); + int fy = farmAnimals[i].getTileY(); + + if (fx.Equals(x) && fy.Equals(y)) + { + string name = farmAnimals[i].displayName; + int age = farmAnimals[i].age.Value; + string type = farmAnimals[i].displayType; + + if (onlyName) + return name; + + return $"{name}, {type}, age {age}"; + } + } + + return null; + } + + /// + /// + /// + /// + /// + /// category: This is the category of the tile. Default to Furnitures. + ///
name: This is the name of the tile. Default to null if the tile tile has nothing on it.
+ public static (CATEGORY? category, string? name) getTileInfo(int x, int y) + { + + int? index = null; + + if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null) + index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex; + /* Add More + MainClass.monitor.Log(index.ToString(), LogLevel.Debug); + */ + + if (Game1.currentLocation is Farm) + { + Building building = ((Farm)Game1.currentLocation).getBuildingAt(new Vector2(x, y)); + if (building != null) + { + return (CATEGORY.Buildings, building.buildingType.Value); + } + } + + if (index != null) + { + switch (index) + { + case 1955: + case 41: + return (CATEGORY.Furnitures, "Mail Box"); + case 1003: + return (CATEGORY.Furnitures, "Street lamp"); + case 78: + return (CATEGORY.Furnitures, "Trash bin"); + case 617: + return (CATEGORY.Furnitures, "Daily quest"); + case 616: + return (CATEGORY.Furnitures, "Calender"); + } + + if (Game1.currentLocation is FarmHouse || Game1.currentLocation is IslandFarmHouse) + { + switch (index) + { + case 173: + return (CATEGORY.Furnitures, "Fridge"); + case 169: + case 170: + case 171: + case 172: + return (CATEGORY.Furnitures, "Kitchen"); + } + } + + } + + return (null, null); + } + + public static (string? name, CATEGORY category) getTerrainFeatureAtTile(Netcode.NetRef terrain) + { + string? toReturn = null; + CATEGORY category = CATEGORY.Others; + + if (terrain.Get() is HoeDirt) + { + category = CATEGORY.Crops; + HoeDirt dirt = (HoeDirt)terrain.Get(); + if (dirt.crop != null) + { + string cropName = Game1.objectInformation[dirt.crop.indexOfHarvest.Value].Split('/')[0]; + toReturn = $"{cropName}"; + + bool isWatered = dirt.state.Value == HoeDirt.watered; + bool isHarvestable = dirt.readyForHarvest(); + bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer; + + if (isWatered) + toReturn = "Watered " + toReturn; + + if (isFertilized) + toReturn = "Fertilized " + toReturn; + + if (isHarvestable) + toReturn = "Harvestable " + toReturn; + } + else + { + toReturn = "Soil"; + bool isWatered = dirt.state.Value == HoeDirt.watered; + bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer; + + if (isWatered) + toReturn = "Watered " + toReturn; + + if (isFertilized) + toReturn = "Fertilized " + toReturn; + } + } + else if (terrain.Get() is GiantCrop) + { + category = CATEGORY.Crops; + int whichCrop = ((GiantCrop)terrain.Get()).which.Value; + switch (whichCrop) + { + case 0: + toReturn = "Cauliflower"; + break; + case 1: + toReturn = "Melon"; + break; + case 2: + toReturn = "Pumpkin"; + break; + } + } + else if (terrain.Get() is CosmeticPlant) + { + category = CATEGORY.Furnitures; + CosmeticPlant cosmeticPlant = (CosmeticPlant)terrain.Get(); + toReturn = cosmeticPlant.textureName().ToLower(); + + if (toReturn.Contains("terrain")) + toReturn.Replace("terrain", ""); + + if (toReturn.Contains("feature")) + toReturn.Replace("feature", ""); + } + else if (terrain.Get() is Flooring && MainClass.Config.ReadFlooring) + { + category = CATEGORY.Flooring; + Flooring flooring = (Flooring)terrain.Get(); + bool isPathway = flooring.isPathway.Get(); + bool isSteppingStone = flooring.isSteppingStone.Get(); + + toReturn = "Flooring"; + + if (isPathway) + toReturn = "Pathway"; + + if (isSteppingStone) + toReturn = "Stepping Stone"; + } + else if (terrain.Get() is FruitTree) + { + category = CATEGORY.Trees; + toReturn = getFruitTree((FruitTree)terrain.Get()); + } + else if (terrain.Get() is Grass) + { + category = CATEGORY.Debris; + toReturn = "Grass"; + } + else if (terrain.Get() is Tree) + { + category = CATEGORY.Trees; + toReturn = getTree((Tree)terrain.Get()); + } + else if (terrain.Get() is Quartz) + { + category = CATEGORY.MineItems; + toReturn = "Quartz"; + } + + return (toReturn, category); + } + + public static string getFruitTree(FruitTree fruitTree) + { + int stage = fruitTree.growthStage.Value; + int fruitIndex = fruitTree.indexOfFruit.Get(); + + string toReturn = Game1.objectInformation[fruitIndex].Split('/')[0]; + + if (stage == 0) + toReturn = $"{toReturn} seed"; + else if (stage == 1) + toReturn = $"{toReturn} sprout"; + else if (stage == 2) + toReturn = $"{toReturn} sapling"; + else if (stage == 3) + toReturn = $"{toReturn} bush"; + else if (stage >= 4) + toReturn = $"{toReturn} tree"; + + if (fruitTree.fruitsOnTree.Value > 0) + toReturn = $"Harvestable {toReturn}"; + + return toReturn; + } + + public static string getTree(Tree tree) + { + int treeType = tree.treeType.Value; + int treeStage = tree.growthStage.Value; + string treeName = "tree"; + string seedName = ""; + + // Return with the name if it's one of the 3 special trees + switch (treeType) + { + case 4: + case 5: + return "Winter Tree"; + case 6: + return "Palm Tree"; + case 7: + return "Mushroom Tree"; + } + + + if (treeType <= 3) + seedName = Game1.objectInformation[308 + treeType].Split('/')[0]; + else if (treeType == 8) + seedName = Game1.objectInformation[292].Split('/')[0]; + + if (treeStage >= 1) + { + switch (seedName.ToLower()) + { + case "mahogany seed": + treeName = "Mahogany"; + break; + case "acorn": + treeName = "Oak"; + break; + case "maple seed": + treeName = "Maple"; + break; + case "pine cone": + treeName = "Pine"; + break; + default: + treeName = "Coconut"; + break; + } + + if (treeStage == 1) + treeName = $"{treeName} sprout"; + else if (treeStage == 2) + treeName = $"{treeName} sapling"; + else if (treeStage == 3 || treeStage == 4) + treeName = $"{treeName} bush"; + else if (treeStage >= 5) + treeName = $"{treeName} tree"; + + return treeName; + } + + return seedName; + } + + #region Objects + public static (string? name, CATEGORY category) getObjectAtTile(int x, int y) + { + (string? name, CATEGORY category) toReturn = (null, CATEGORY.Others); + + StardewValley.Object obj = Game1.currentLocation.getObjectAtTile(x, y); + int index = obj.ParentSheetIndex; + toReturn.name = obj.DisplayName; + + // Get object names based on index + (string? name, CATEGORY category) correctNameAndCategory = getCorrectNameAndCategoryFromIndex(index); + if (correctNameAndCategory.name != null) + toReturn = correctNameAndCategory; + + if (toReturn.name.ToLower().Equals("stone")) // Fix for `Busy stone` + toReturn.category = CATEGORY.Debris; + + if (obj is Chest) + { + Chest chest = (Chest)obj; + toReturn = (chest.DisplayName, CATEGORY.Chests); + } + + if (obj is Furniture) + toReturn.category = CATEGORY.Furnitures; + + if (toReturn.category == CATEGORY.Others) // Fix for `Harvestable table` and `Busy nodes` + { + MachineState machineState = GetMachineState(obj); + if (machineState == MachineState.Ready) + toReturn.name = $"Harvestable {toReturn.name}"; + else if (machineState == MachineState.Busy) + toReturn.name = $"Busy {toReturn.name}"; + } + return toReturn; + } + + private static MachineState GetMachineState(StardewValley.Object machine) + { + if (machine is CrabPot crabPot) + if (crabPot.bait.Value is not null && crabPot.heldObject.Value is null) + return MachineState.Busy; + return GetMachineState(machine.readyForHarvest.Value, machine.MinutesUntilReady, machine.heldObject.Value); + } + + private static MachineState GetMachineState(bool readyForHarvest, int minutesUntilReady, StardewValley.Object? heldObject) + { + if (readyForHarvest || (heldObject is not null && minutesUntilReady <= 0)) + return MachineState.Ready; + else if (minutesUntilReady > 0) + return MachineState.Busy; + else + return MachineState.Waiting; + } + + private static (string? name, CATEGORY category) getCorrectNameAndCategoryFromIndex(int index) + { + switch (index) + { + case 313: + case 314: + case 315: + case 316: + case 317: + case 318: + case 452: + case 674: + case 675: + case 676: + case 677: + case 678: + case 679: + case 750: + case 784: + case 785: + case 786: + return ("Weed", CATEGORY.Debris); + case 792: + case 793: + case 794: + return ("Fertile weed", CATEGORY.Debris); + case 319: + case 320: + case 321: + return ("Ice crystal", CATEGORY.Debris); + case 75: + return ("Geode", CATEGORY.MineItems); + case 32: + case 34: + case 36: + case 38: + case 40: + case 42: + case 48: + case 50: + case 52: + case 54: + case 56: + case 58: + return ("Coloured stone", CATEGORY.Debris); + case 668: + case 670: + case 845: + case 846: + case 847: + return ("Mine stone", CATEGORY.MineItems); + case 818: + return ("Clay stone", CATEGORY.Debris); + case 816: + case 817: + return ("Fossil stone", CATEGORY.Debris); + case 118: + case 120: + case 122: + case 124: + return ("Barrel", CATEGORY.MineItems); + case 119: + case 121: + case 123: + case 125: + return ("Item box", CATEGORY.MineItems); + } + + if (Game1.currentLocation is Mine or MineShaft) + { + switch (index) + { + case 76: + return ("Frozen geode", CATEGORY.MineItems); + case 77: + return ("Magma geode", CATEGORY.MineItems); + case 8: + case 66: + return ("Amethyst node", CATEGORY.MineItems); + case 14: + case 62: + return ("Aquamarine node", CATEGORY.MineItems); + case 843: + case 844: + return ("Cinder shard node", CATEGORY.MineItems); + case 2: + case 72: + return ("Diamond node", CATEGORY.MineItems); + case 12: + case 60: + return ("Emerald node", CATEGORY.MineItems); + case 44: + return ("Gem node", CATEGORY.MineItems); + case 6: + case 70: + return ("Jade node", CATEGORY.MineItems); + case 46: + return ("Mystic stone", CATEGORY.MineItems); + case 74: + return ("Prismatic node", CATEGORY.MineItems); + case 4: + case 64: + return ("Ruby node", CATEGORY.MineItems); + case 10: + case 68: + return ("Topaz node", CATEGORY.MineItems); + case 819: + return ("Omni geode node", CATEGORY.MineItems); + case 751: + case 849: + return ("Copper node", CATEGORY.MineItems); + case 764: + return ("Gold node", CATEGORY.MineItems); + case 765: + return ("Iridium node", CATEGORY.MineItems); + case 290: + case 850: + return ("Iron node", CATEGORY.MineItems); + } + } + + return (null, CATEGORY.Others); + } + #endregion + + public static bool isMineDownLadderAtTile(int x, int y) + { + try + { + if (Game1.currentLocation is Mine or MineShaft) + { + int? index = null; + + if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null) + index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex; + + if (index == 173 || index == 174) + return true; + } + } + catch (Exception) { } + + return false; + } + + public static bool isMineUpLadderAtTile(int x, int y) + { + try + { + if (Game1.currentLocation is Mine or MineShaft) + { + int? index = null; + + if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null) + index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex; + + if (index == 115) + return true; + } + } + catch (Exception) { } + + return false; + } + + public static bool isElevatorAtTile(int x, int y) + { + try + { + if (Game1.currentLocation is Mine or MineShaft) + { + int? index = null; + + if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null) + index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex; + + if (index == 112) + return true; + } + } + catch (Exception) { } + + return false; + } + + public static string? getDoorAtTile(int x, int y) + { + Point tilePoint = new Point(x, y); + StardewValley.Network.NetPointDictionary doorList = Game1.currentLocation.doors; + + for (int i = 0; i < doorList.Count(); i++) + { + if (doorList.ContainsKey(tilePoint)) + { + string? doorName; + doorList.TryGetValue(tilePoint, out doorName); + + if (doorName != null) + return $"{doorName} door"; + else + return "door"; + } + } + + return null; + } + + public static string? getResourceClumpAtTile(int x, int y) + { + if (Game1.currentLocation is Woods) + return getStumpsInWoods(x, y); + + for (int i = 0; i < Game1.currentLocation.resourceClumps.Count; i++) + { + if (Game1.currentLocation.resourceClumps[i].occupiesTile(x, y)) + { + int index = Game1.currentLocation.resourceClumps[i].parentSheetIndex.Value; + + switch (index) + { + case 600: + return "Large Stump"; + case 602: + return "Hollow Log"; + case 622: + return "Meteorite"; + case 752: + case 754: + case 756: + case 758: + return "Mine Rock"; + case 672: + return "Boulder"; + default: + return "Unknown"; + } + } + } + + return null; + } + + public static string? getStumpsInWoods(int x, int y) + { + if (Game1.currentLocation is not Woods) + return null; + + if ((x == 8 || x == 9) && y == 7) + { + return "Old Master Cannoli"; + } + Netcode.NetObjectList stumps = ((Woods)Game1.currentLocation).stumps; + for (int i = 0; i < stumps.Count; i++) + { + if (stumps[i].occupiesTile(x, y)) + { + return "Large Stump"; + } + } + return null; + } + + public static string? getParrotPerchAtTile(int x, int y) + { + if (Game1.currentLocation is not IslandLocation islandLocation) + return null; + + foreach (var perch in islandLocation.parrotUpgradePerches) + { + if (!perch.tilePosition.Value.Equals(new Point(x, y))) + continue; + + string toSpeak = $"Parrot required nuts {perch.requiredNuts.Value}"; + + if (perch.currentState == StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Complete) + return $"Request Completed"; + else if (perch.currentState == StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Idle) + return toSpeak; + else if (perch.currentState == StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.StartBuilding) + return "Parrots started building request"; + else if (perch.currentState == StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Building) + return "Parrots building request"; + else + return toSpeak; + } + + return null; + } + } +} \ No newline at end of file diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index 96e0da4..ae60975 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -158,12 +158,7 @@ namespace stardew_access harmony.Patch( original: AccessTools.Method(typeof(MuseumMenu), nameof(MuseumMenu.receiveKeyPress), new Type[] { typeof(Keys) }), - prefix: new HarmonyMethod(typeof(MuseumMenuPatches), nameof(MuseumMenuPatches.MuseumMenuKeyPressPatch)) - ); - - harmony.Patch( - original: AccessTools.Method(typeof(MuseumMenu), nameof(MuseumMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MuseumMenuPatches), nameof(MuseumMenuPatches.MuseumMenuPatch)) + prefix: new HarmonyMethod(typeof(DonationMenuPatches), nameof(DonationMenuPatches.MuseumMenuKeyPressPatch)) ); harmony.Patch( @@ -195,11 +190,6 @@ namespace stardew_access original: AccessTools.Method(typeof(ItemListMenu), nameof(ItemListMenu.draw), new Type[] { typeof(SpriteBatch) }), postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ItemListMenuPatch)) ); - - harmony.Patch( - original: AccessTools.Method(typeof(FieldOfficeMenu), nameof(FieldOfficeMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.FieldOfficeMenuPatch)) - ); #endregion #region Quest Patches @@ -251,6 +241,18 @@ namespace stardew_access #endregion + #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)) + ); + + harmony.Patch( + original: AccessTools.Method(typeof(FieldOfficeMenu), nameof(FieldOfficeMenu.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(DonationMenuPatches), nameof(DonationMenuPatches.FieldOfficeMenuPatch)) + ); + #endregion + harmony.Patch( original: AccessTools.Method(typeof(Game1), nameof(Game1.playSound)), prefix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.PlaySoundPatch)) diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index 1228cb8..b59bbef 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -2,7 +2,6 @@ using StardewModdingAPI; using StardewValley; using StardewValley.Menus; -using System.Text; namespace stardew_access.Patches { @@ -197,6 +196,8 @@ namespace stardew_access.Patches return; else if (Game1.activeClickableMenu is FieldOfficeMenu) return; + else if (Game1.activeClickableMenu is MuseumMenu) + return; #endregion string toSpeak = " "; diff --git a/stardew-access/Patches/MuseumMenuPatches.cs b/stardew-access/Patches/DonationMenuPatches.cs similarity index 71% rename from stardew-access/Patches/MuseumMenuPatches.cs rename to stardew-access/Patches/DonationMenuPatches.cs index 6a06c65..ce244ac 100644 --- a/stardew-access/Patches/MuseumMenuPatches.cs +++ b/stardew-access/Patches/DonationMenuPatches.cs @@ -1,15 +1,15 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; -using StardewModdingAPI; using StardewValley; using StardewValley.Locations; using StardewValley.Menus; namespace stardew_access.Patches { - internal class MuseumMenuPatches + internal class DonationMenuPatches { - private static string museumQueryKey = " "; + internal static string museumQueryKey = " "; + internal static string fieldOfficeMenuQuery = " "; private static bool isMoving = false; private static (int x, int y)[] donationTiles = { @@ -29,6 +29,8 @@ namespace stardew_access.Patches (42,16),(43,16),(44,16),(45,16),(46,16),(47,16), }; + #region Museum + internal static bool MuseumMenuKeyPressPatch() { try @@ -275,5 +277,134 @@ namespace stardew_access.Patches } } #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 + { + for (int i = 0; i < __instance.inventory.inventory.Count; i++) + { + if (!__instance.inventory.inventory[i].containsPoint(x, y)) + continue; + + if (__instance.inventory.actualInventory[i] == null) + toSpeak = "Empty slot"; + else + toSpeak = $"{__instance.inventory.actualInventory[i].Stack} {__instance.inventory.actualInventory[i].DisplayName}"; + + if (fieldOfficeMenuQuery != $"{toSpeak}:{i}") + { + fieldOfficeMenuQuery = $"{toSpeak}:{i}"; + MainClass.ScreenReader.Say(toSpeak, true); + } + + 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 (__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 } } \ No newline at end of file diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 33d0581..362c78b 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -20,130 +20,6 @@ namespace stardew_access.Patches internal static string forgeMenuQuery = " "; internal static string itemListMenuQuery = " "; public static Vector2? prevTile = null; - private static string fieldOfficeMenuQuery = " "; - - 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 - { - for (int i = 0; i < __instance.inventory.inventory.Count; i++) - { - if (!__instance.inventory.inventory[i].containsPoint(x, y)) - continue; - - if (__instance.inventory.actualInventory[i] == null) - toSpeak = "Empty slot"; - else - toSpeak = $"{__instance.inventory.actualInventory[i].Stack} {__instance.inventory.actualInventory[i].DisplayName}"; - - if (fieldOfficeMenuQuery != $"{toSpeak}:{i}") - { - fieldOfficeMenuQuery = $"{toSpeak}:{i}"; - MainClass.ScreenReader.Say(toSpeak, true); - } - - 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 (__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 ItemListMenuPatch(ItemListMenu __instance, string ___title, int ___currentTab, int ___totalValueOfItems, List ___itemsToList) { @@ -553,7 +429,7 @@ namespace stardew_access.Patches if (cueName == "grassyStep" || cueName == "sandyStep" || cueName == "snowyStep" || cueName == "stoneStep" || cueName == "thudStep" || cueName == "woodyStep") { Vector2 nextTile = CurrentPlayer.getNextTile(); - if (ReadTile.isCollidingAtTile((int)nextTile.X, (int)nextTile.Y)) + if (TileInfo.isCollidingAtTile((int)nextTile.X, (int)nextTile.Y)) { if (prevTile != nextTile) { @@ -955,7 +831,11 @@ namespace stardew_access.Patches } else if (menu is FieldOfficeMenu) { - fieldOfficeMenuQuery = " "; + DonationMenuPatches.fieldOfficeMenuQuery = " "; + } + else if (menu is MuseumMenu) + { + DonationMenuPatches.museumQueryKey = " "; } else if (menu is PondQueryMenu) { From a0b6159df8e5e8927f83e366e1ea06deadd7d93d Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Fri, 15 Apr 2022 14:49:40 +0530 Subject: [PATCH 048/232] Fixed bug --- stardew-access/Features/TileInfo.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 1fb346f..655a535 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -858,14 +858,16 @@ namespace stardew_access.Features string toSpeak = $"Parrot required nuts {perch.requiredNuts.Value}"; - if (perch.currentState == StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Complete) - return $"Request Completed"; - else if (perch.currentState == StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Idle) + if (!perch.IsAvailable()) + return "Empty parrot perch"; + else if (perch.currentState.Value == StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Idle) return toSpeak; - else if (perch.currentState == StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.StartBuilding) + else if (perch.currentState.Value == StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.StartBuilding) return "Parrots started building request"; - else if (perch.currentState == StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Building) + else if (perch.currentState.Value == StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Building) return "Parrots building request"; + else if (perch.currentState.Value == StardewValley.BellsAndWhistles.ParrotUpgradePerch.UpgradeState.Complete) + return $"Request Completed"; else return toSpeak; } From c3882e7ea9493555504b3081917f683690770276 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Fri, 15 Apr 2022 15:28:19 +0530 Subject: [PATCH 049/232] Added lava and cooled lava to read tile --- stardew-access/Features/TileInfo.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 655a535..0f29516 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -62,6 +62,16 @@ namespace stardew_access.Features toReturn = farmAnimal; category = CATEGORY.FarmAnimals; } + else if (Game1.currentLocation is VolcanoDungeon && ((VolcanoDungeon)Game1.currentLocation).IsCooledLava(x, y)) + { + toReturn = "Cooled lava"; + category = CATEGORY.WaterTiles; + } + else if (Game1.currentLocation is VolcanoDungeon && StardewValley.Monsters.LavaLurk.IsLavaTile((VolcanoDungeon)Game1.currentLocation, x, y)) + { + toReturn = "Lava"; + category = CATEGORY.WaterTiles; + } else if (Game1.currentLocation.isWaterTile(x, y) && isColliding) { toReturn = "Water"; From d9bb4aac35391caa81c0234a61632f1592713d99 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Fri, 15 Apr 2022 16:02:24 +0530 Subject: [PATCH 050/232] Bug fix in read tile --- stardew-access/Features/TileInfo.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 0f29516..3863cfc 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -355,7 +355,7 @@ namespace stardew_access.Features { category = CATEGORY.Crops; HoeDirt dirt = (HoeDirt)terrain.Get(); - if (dirt.crop != null) + if (dirt.crop != null && !dirt.crop.forageCrop.Value) { string cropName = Game1.objectInformation[dirt.crop.indexOfHarvest.Value].Split('/')[0]; toReturn = $"{cropName}"; @@ -373,6 +373,15 @@ namespace stardew_access.Features if (isHarvestable) toReturn = "Harvestable " + toReturn; } + else if (dirt.crop != null && dirt.crop.forageCrop.Value) + { + toReturn = dirt.crop.whichForageCrop.Value switch + { + 1 => "Spring onion", + 2 => "Ginger", + _ => "Forageable crop" + }; + } else { toReturn = "Soil"; From d051e4359cf4f2a415e6a4d07f4863da5aca3490 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 23 Apr 2022 15:30:47 +0530 Subject: [PATCH 051/232] Fixed stuttering in pond query menu --- stardew-access/Patches/MenuPatches.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 362c78b..92498d8 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -14,6 +14,7 @@ namespace stardew_access.Patches { internal static string currentLevelUpTitle = " "; internal static bool firstTimeInNamingMenu = true; + internal static bool isNarratingPondInfo = false; internal static string animalQueryMenuQuery = " "; internal static string tailoringMenuQuery = " "; internal static string pondQueryMenuQuery = " "; @@ -184,7 +185,7 @@ namespace stardew_access.Patches } else { - if (isCPressed) + if (isCPressed && !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); @@ -196,6 +197,9 @@ namespace stardew_access.Patches 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)) @@ -210,6 +214,7 @@ namespace stardew_access.Patches { pondQueryMenuQuery = toSpeak; MainClass.ScreenReader.Say(extra + " \n\t" + toSpeak, true); + MainClass.DebugLog(extra + " \n\t" + toSpeak); } } catch (System.Exception e) From 49ec3fc85b65ddd07dd9c7a93f3098c3b5fb6e06 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 23 Apr 2022 15:34:33 +0530 Subject: [PATCH 052/232] Fixed stuttering in animal menu --- stardew-access/Patches/MenuPatches.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 92498d8..d8c0ce8 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -15,6 +15,7 @@ namespace stardew_access.Patches internal static string currentLevelUpTitle = " "; internal static bool firstTimeInNamingMenu = true; internal static bool isNarratingPondInfo = false; + internal static bool isNarratingAnimalInfo = false; internal static string animalQueryMenuQuery = " "; internal static string tailoringMenuQuery = " "; internal static string pondQueryMenuQuery = " "; @@ -214,7 +215,6 @@ namespace stardew_access.Patches { pondQueryMenuQuery = toSpeak; MainClass.ScreenReader.Say(extra + " \n\t" + toSpeak, true); - MainClass.DebugLog(extra + " \n\t" + toSpeak); } } catch (System.Exception e) @@ -373,7 +373,7 @@ namespace stardew_access.Patches } else { - if (isCPressed) + if (isCPressed & !isNarratingAnimalInfo) { string name = ___animal.displayName; string type = ___animal.displayType; @@ -391,6 +391,9 @@ namespace stardew_access.Patches 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)) From bfdb7c9f5be1adc5ec7924e3fe56200b9369e9cb Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 23 Apr 2022 16:34:53 +0530 Subject: [PATCH 053/232] Bug fix in quest log patch --- stardew-access/Patches/QuestPatches.cs | 101 +++++++++++++++---------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/stardew-access/Patches/QuestPatches.cs b/stardew-access/Patches/QuestPatches.cs index 142d811..831636f 100644 --- a/stardew-access/Patches/QuestPatches.cs +++ b/stardew-access/Patches/QuestPatches.cs @@ -10,6 +10,7 @@ namespace stardew_access.Patches { 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) @@ -153,18 +154,21 @@ namespace stardew_access.Patches { try { - bool snapMouseToRewardBox = false; + bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + string toSpeak = " ", extra = ""; if (___questPage == -1) { #region Quest Lists - string toSpeak = " "; + 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)) + if (!__instance.questLogButtons[i].containsPoint(x, y)) continue; string name = ___pages[___currentPage][i].GetName(); @@ -183,8 +187,6 @@ namespace stardew_access.Patches toSpeak = "Previous page 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"; @@ -198,59 +200,76 @@ namespace stardew_access.Patches 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(); - string toSpeak = " "; - if (____shownQuest.ShouldDisplayAsComplete()) + + if (firstTimeInIndividualQuest || (isCPressed && !isNarratingQuestInfo)) { - #region Quest completed menu + if (firstTimeInIndividualQuest) + toSpeak = "Back button"; - toSpeak = $"Quest: {title} Completed!"; - - if (__instance.HasReward()) + if (____shownQuest.ShouldDisplayAsComplete()) { - snapMouseToRewardBox = true; + #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++) { - toSpeak += $"you recieved {____shownQuest.GetMoneyReward()}g"; + if (____shownQuest != null) + { + _ = ____shownQuest is SpecialOrder; + } + string parsed_text = Game1.parseText(____objectiveText[j], width: __instance.width - 192, whichFont: Game1.dialogueFont); + + extra += $"\t\nOrder {j + 1}: {parsed_text} \t\n"; } - toSpeak += "... left click to collect reward"; - } - - #endregion - } - else - { - #region Quest in-complete menu - toSpeak = $"Title: {title}. \t\n Description: {description}"; - - for (int j = 0; j < ____objectiveText.Count; j++) - { if (____shownQuest != null) { - _ = ____shownQuest is SpecialOrder; + int daysLeft = ____shownQuest.GetDaysLeft(); + + if (daysLeft > 0) + extra += $"\t\n{daysLeft} days left."; } - string parsed_text = Game1.parseText(____objectiveText[j], width: __instance.width - 192, whichFont: Game1.dialogueFont); - - toSpeak += $"\t\nOrder {j + 1}: {parsed_text} \t\n"; + #endregion } - if (____shownQuest != null) - { - int daysLeft = ____shownQuest.GetDaysLeft(); - - if (daysLeft > 0) - toSpeak += $"\t\n{daysLeft} days left."; - } - #endregion + isNarratingQuestInfo = true; + Task.Delay(200).ContinueWith(_ => { isNarratingQuestInfo = false; }); + questLogQuery = " "; } - // Move mouse to reward button - if (snapMouseToRewardBox) - __instance.rewardBox.snapMouseCursorToCenter(); + if (!firstTimeInIndividualQuest) + if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y)) + toSpeak = "Back 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; + } - MainClass.ScreenReader.SayWithChecker(toSpeak, true); #endregion } } From 4590f647362c0b94658848bdab2660fb9b76c67a Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 23 Apr 2022 16:47:36 +0530 Subject: [PATCH 054/232] Added quest progress for special order quests --- stardew-access/Patches/QuestPatches.cs | 16 ++++++++++------ stardew-access/manifest.json | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/stardew-access/Patches/QuestPatches.cs b/stardew-access/Patches/QuestPatches.cs index 831636f..73e6784 100644 --- a/stardew-access/Patches/QuestPatches.cs +++ b/stardew-access/Patches/QuestPatches.cs @@ -173,7 +173,7 @@ namespace stardew_access.Patches string name = ___pages[___currentPage][i].GetName(); int daysLeft = ___pages[___currentPage][i].GetDaysLeft(); - toSpeak = $"{name} quest"; + toSpeak = $"{name}"; if (daysLeft > 0 && ___pages[___currentPage][i].ShouldDisplayAsComplete()) toSpeak += $"\t\n {daysLeft} days left"; @@ -227,11 +227,13 @@ namespace stardew_access.Patches for (int j = 0; j < ____objectiveText.Count; j++) { - if (____shownQuest != null) - { - _ = ____shownQuest is SpecialOrder; - } 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"; } @@ -253,7 +255,9 @@ namespace stardew_access.Patches if (!firstTimeInIndividualQuest) if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y)) - toSpeak = "Back button"; + 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)) diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index a213ac1..0148672 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.1.6", + "Version": "1.1.7", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From 514813982560fec475a0dcd6cdf3ef016d0bd5b1 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 23 Apr 2022 17:54:19 +0530 Subject: [PATCH 055/232] Added static tiles to tile info --- stardew-access/Features/StaticTiles.cs | 64 ++++++++++++++++++++++++++ stardew-access/Features/TileInfo.cs | 7 +++ stardew-access/ModEntry.cs | 17 ++++++- stardew-access/static-tiles.json | 23 +++++++++ 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 stardew-access/Features/StaticTiles.cs create mode 100644 stardew-access/static-tiles.json diff --git a/stardew-access/Features/StaticTiles.cs b/stardew-access/Features/StaticTiles.cs new file mode 100644 index 0000000..e8d90ab --- /dev/null +++ b/stardew-access/Features/StaticTiles.cs @@ -0,0 +1,64 @@ +using Newtonsoft.Json.Linq; +using StardewValley; + +namespace stardew_access.Features +{ + public class StaticTiles + { + private JObject? data; + + public StaticTiles() + { + using (StreamReader file = new StreamReader("static-tiles.json")) + { + string json = file.ReadToEnd(); + data = JObject.Parse(json); + + } + } + + public bool isAvailable(string locationName) + { + if (data != null) + { + foreach (var location in data) + { + if (locationName.ToLower().Equals(location.Key)) + return true; + } + } + + return false; + } + + public string? getStaticTileAt(int x, int y) + { + if (data == null) + return null; + + foreach (var location in data) + { + if (!Game1.currentLocation.Name.ToLower().Equals(location.Key)) + continue; + + if (location.Value != null) + foreach (var tile in ((JObject)location.Value)) + { + if (tile.Value == null) + continue; + + JToken? tileX = tile.Value["x"]; + JToken? tileY = tile.Value["y"]; + + if (tileX == null || tileY == null) + continue; + + if (short.Parse(tileX.ToString()) == x && short.Parse(tileY.ToString()) == y) + return tile.Key; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 3863cfc..d21b465 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -47,6 +47,7 @@ namespace stardew_access.Features string? resourceClump = getResourceClumpAtTile(x, y); string? farmAnimal = getFarmAnimalAt(Game1.currentLocation, x, y); string? parrot = getParrotPerchAtTile(x, y); + string? staticTile = MainClass.STiles.getStaticTileAt(x, y); if (Game1.currentLocation.isCharacterAtTile(tile) != null) { @@ -62,6 +63,11 @@ namespace stardew_access.Features toReturn = farmAnimal; category = CATEGORY.FarmAnimals; } + else if (staticTile != null) + { + toReturn = staticTile; + category = CATEGORY.Others; + } else if (Game1.currentLocation is VolcanoDungeon && ((VolcanoDungeon)Game1.currentLocation).IsCooledLava(x, y)) { toReturn = "Cooled lava"; @@ -893,5 +899,6 @@ namespace stardew_access.Features return null; } + } } \ No newline at end of file diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 1dc8136..2fb2634 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -7,6 +7,7 @@ using stardew_access.Patches; using stardew_access.ScreenReader; using Microsoft.Xna.Framework; using StardewValley.Menus; +using Newtonsoft.Json.Linq; namespace stardew_access { @@ -17,16 +18,30 @@ namespace stardew_access private Harmony? harmony; private static IMonitor? monitor; private static Radar? radarFeature; + private static StaticTiles? sTiles; private static IScreenReader? screenReader; private static IModHelper? modHelper; internal static ModConfig Config { get => config; set => config = value; } public static IModHelper? ModHelper { get => modHelper; } + public static StaticTiles STiles + { + get + { + if (sTiles == null) + sTiles = new StaticTiles(); + + return sTiles; + } + set => sTiles = value; + } public static Radar RadarFeature { get { - if (radarFeature == null) { radarFeature = new Radar(); } + if (radarFeature == null) + radarFeature = new Radar(); + return radarFeature; } set => radarFeature = value; diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json new file mode 100644 index 0000000..cffffe6 --- /dev/null +++ b/stardew-access/static-tiles.json @@ -0,0 +1,23 @@ +{ + "busstop": + { + "Ticket Machine": + { + "x":7, + "y":11, + "type":"interactable" + }, + "Minecart": + { + "x":4, + "y":3, + "type":"interactable" + }, + "Minecart": + { + "x":5, + "y":3, + "type":"interactable" + } + } +} \ No newline at end of file From ca8ecd845b6a92f880e5ded95ee10b88ac046042 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 23 Apr 2022 18:21:50 +0530 Subject: [PATCH 056/232] Added category to static tiles --- stardew-access/Features/Radar.cs | 38 --------- stardew-access/Features/StaticTiles.cs | 18 ++-- stardew-access/Features/TileInfo.cs | 112 +++++++++++++++++++++++-- stardew-access/static-tiles.json | 10 +-- 4 files changed, 120 insertions(+), 58 deletions(-) diff --git a/stardew-access/Features/Radar.cs b/stardew-access/Features/Radar.cs index c0b0eaa..8235ebc 100644 --- a/stardew-access/Features/Radar.cs +++ b/stardew-access/Features/Radar.cs @@ -4,44 +4,6 @@ using StardewValley.Objects; namespace stardew_access.Features { - - /// - /// This is a custom enum class and contains the name of groups the objects are divided into for the feature - /// - public class CATEGORY - { - private string _typeKeyWord; - - private CATEGORY(string typeKeyWord) - { - _typeKeyWord = typeKeyWord; - } - - public override string ToString() - { - return _typeKeyWord; - } - - public static CATEGORY Farmers = new CATEGORY("farmer"); - public static CATEGORY FarmAnimals = new CATEGORY("animal"); - public static CATEGORY NPCs = new CATEGORY("npc"); - public static CATEGORY Furnitures = new CATEGORY("furniture"); - public static CATEGORY Flooring = new CATEGORY("flooring"); - public static CATEGORY Debris = new CATEGORY("debris"); - public static CATEGORY Crops = new CATEGORY("crop"); - public static CATEGORY Trees = new CATEGORY("tree"); - public static CATEGORY Bush = new CATEGORY("bush"); - public static CATEGORY Buildings = new CATEGORY("building"); - public static CATEGORY MineItems = new CATEGORY("mine item"); - public static CATEGORY ResourceClumps = new CATEGORY("resource clump"); - public static CATEGORY Chests = new CATEGORY("chest"); - public static CATEGORY JunimoBundle = new CATEGORY("bundle"); - public static CATEGORY Doors = new CATEGORY("door"); // Also includes ladders and elevators - public static CATEGORY Others = new CATEGORY("other"); - public static CATEGORY WaterTiles = new CATEGORY("water"); - - } - public class Radar { private List closed; diff --git a/stardew-access/Features/StaticTiles.cs b/stardew-access/Features/StaticTiles.cs index e8d90ab..ab8d803 100644 --- a/stardew-access/Features/StaticTiles.cs +++ b/stardew-access/Features/StaticTiles.cs @@ -31,10 +31,15 @@ namespace stardew_access.Features return false; } - public string? getStaticTileAt(int x, int y) + public string? getStaticTileInfoAt(int x, int y) + { + return getStaticTileInfoAtWithCategory(x, y).name; + } + + public (string? name, CATEGORY category) getStaticTileInfoAtWithCategory(int x, int y) { if (data == null) - return null; + return (null, CATEGORY.Others); foreach (var location in data) { @@ -49,16 +54,17 @@ namespace stardew_access.Features JToken? tileX = tile.Value["x"]; JToken? tileY = tile.Value["y"]; + JToken? tileType = tile.Value["type"]; - if (tileX == null || tileY == null) + if (tileX == null || tileY == null || tileType == null) continue; - + MainClass.DebugLog($"{tile.Key.ToString()}:\tx{tileX.ToString()}\ty{tileY.ToString()}\ttype{tileType.ToString()}"); if (short.Parse(tileX.ToString()) == x && short.Parse(tileY.ToString()) == y) - return tile.Key; + return (tile.Key, CATEGORY.FromString(tileType.ToString())); } } - return null; + return (null, CATEGORY.Others); } } } \ No newline at end of file diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index d21b465..d9338da 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -7,6 +7,93 @@ using StardewValley.TerrainFeatures; namespace stardew_access.Features { + + /// + /// This is a custom enum class and contains the name of groups the objects are divided into for the feature + /// + public class CATEGORY + { + private string _typeKeyWord; + + private CATEGORY(string typeKeyWord) + { + _typeKeyWord = typeKeyWord; + } + + public override string ToString() + { + return _typeKeyWord; + } + + public static CATEGORY FromString(string name) + { + if (name == "farmer") + return CATEGORY.Farmers; + else if (name == "animal") + return CATEGORY.FarmAnimals; + else if (name == "npc") + return CATEGORY.NPCs; + else if (name == "furniture") + return CATEGORY.Furnitures; + else if (name == "flooring") + return CATEGORY.Flooring; + else if (name == "debris") + return CATEGORY.Debris; + else if (name == "crop") + return CATEGORY.Crops; + else if (name == "tree") + return CATEGORY.Trees; + else if (name == "bush") + return CATEGORY.Bush; + else if (name == "building") + return CATEGORY.Buildings; + else if (name == "mine item") + return CATEGORY.MineItems; + else if (name == "resource clump") + return CATEGORY.ResourceClumps; + else if (name == "chest") + return CATEGORY.Chests; + else if (name == "bundle") + return CATEGORY.JunimoBundle; + else if (name == "door") + return CATEGORY.Doors; + else if (name == "water") + return CATEGORY.WaterTiles; + else if (name == "interactables") + return CATEGORY.Interactables; + else if (name == "decoration") + return CATEGORY.Decor; + else if (name == "machines") + return CATEGORY.Machines; + else if (name == "other") + return CATEGORY.Others; + + return Others; + } + + public static CATEGORY Farmers = new CATEGORY("farmer"); + public static CATEGORY FarmAnimals = new CATEGORY("animal"); + public static CATEGORY NPCs = new CATEGORY("npc"); + public static CATEGORY Furnitures = new CATEGORY("furniture"); + public static CATEGORY Flooring = new CATEGORY("flooring"); + public static CATEGORY Debris = new CATEGORY("debris"); + public static CATEGORY Crops = new CATEGORY("crop"); + public static CATEGORY Trees = new CATEGORY("tree"); + public static CATEGORY Bush = new CATEGORY("bush"); + public static CATEGORY Buildings = new CATEGORY("building"); + public static CATEGORY MineItems = new CATEGORY("mine item"); + public static CATEGORY ResourceClumps = new CATEGORY("resource clump"); + public static CATEGORY Chests = new CATEGORY("chest"); + public static CATEGORY JunimoBundle = new CATEGORY("bundle"); + public static CATEGORY Doors = new CATEGORY("door"); // Also includes ladders and elevators + public static CATEGORY WaterTiles = new CATEGORY("water"); + public static CATEGORY Interactables = new CATEGORY("interactables"); + public static CATEGORY Decor = new CATEGORY("decoration"); + public static CATEGORY Machines = new CATEGORY("machines"); + public static CATEGORY Others = new CATEGORY("other"); + + } + public enum MachineState { Ready, Busy, Waiting @@ -14,6 +101,8 @@ namespace stardew_access.Features public class TileInfo { + public static string[] trackable_machines = { "bee house", "cask", "press", "keg", "machine", "maker", "preserves jar", "bone mill", "kiln", "crystalarium", "furnace", "geode crusher", "tapper", "lightning rod", "incubator", "wood chipper", "worm bin", "loom" }; + ///Returns the name of the object at tile alongwith it's category's name public static (string? name, string? categoryName) getNameWithCategoryNameAtTile(Vector2 tile) { @@ -47,7 +136,7 @@ namespace stardew_access.Features string? resourceClump = getResourceClumpAtTile(x, y); string? farmAnimal = getFarmAnimalAt(Game1.currentLocation, x, y); string? parrot = getParrotPerchAtTile(x, y); - string? staticTile = MainClass.STiles.getStaticTileAt(x, y); + (string? name, CATEGORY category) staticTile = MainClass.STiles.getStaticTileInfoAtWithCategory(x, y); if (Game1.currentLocation.isCharacterAtTile(tile) != null) { @@ -63,10 +152,10 @@ namespace stardew_access.Features toReturn = farmAnimal; category = CATEGORY.FarmAnimals; } - else if (staticTile != null) + else if (staticTile.name != null) { - toReturn = staticTile; - category = CATEGORY.Others; + toReturn = staticTile.name; + category = staticTile.category; } else if (Game1.currentLocation is VolcanoDungeon && ((VolcanoDungeon)Game1.currentLocation).IsCooledLava(x, y)) { @@ -576,9 +665,20 @@ namespace stardew_access.Features Chest chest = (Chest)obj; toReturn = (chest.DisplayName, CATEGORY.Chests); } - - if (obj is Furniture) + else if (obj is Furniture) toReturn.category = CATEGORY.Furnitures; + else if (obj.type == "Crafting" && obj.bigCraftable) + { + + foreach (string machine in trackable_machines) + { + if (obj.Name.ToLower().Contains(machine)) + { + toReturn.name = obj.DisplayName; + toReturn.category = CATEGORY.Machines; + } + } + } if (toReturn.category == CATEGORY.Others) // Fix for `Harvestable table` and `Busy nodes` { diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index cffffe6..8064919 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -5,19 +5,13 @@ { "x":7, "y":11, - "type":"interactable" + "type":"interactables" }, "Minecart": { "x":4, "y":3, - "type":"interactable" - }, - "Minecart": - { - "x":5, - "y":3, - "type":"interactable" + "type":"interactables" } } } \ No newline at end of file From 98b4247cbb4c240d91fdf89d634bc4ba022c2d99 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 23 Apr 2022 18:29:35 +0530 Subject: [PATCH 057/232] Bug fix --- stardew-access/Features/StaticTiles.cs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/stardew-access/Features/StaticTiles.cs b/stardew-access/Features/StaticTiles.cs index ab8d803..deaa623 100644 --- a/stardew-access/Features/StaticTiles.cs +++ b/stardew-access/Features/StaticTiles.cs @@ -5,27 +5,29 @@ namespace stardew_access.Features { public class StaticTiles { - private JObject? data; + private JObject? data = null; public StaticTiles() { - using (StreamReader file = new StreamReader("static-tiles.json")) + if (MainClass.ModHelper == null) + return; + + using (StreamReader file = new StreamReader(Path.Combine(MainClass.ModHelper.DirectoryPath, "static-tiles.json"))) { string json = file.ReadToEnd(); data = JObject.Parse(json); - } } public bool isAvailable(string locationName) { - if (data != null) + if (data == null) + return false; + + foreach (var location in data) { - foreach (var location in data) - { - if (locationName.ToLower().Equals(location.Key)) - return true; - } + if (locationName.ToLower().Equals(location.Key)) + return true; } return false; @@ -58,7 +60,7 @@ namespace stardew_access.Features if (tileX == null || tileY == null || tileType == null) continue; - MainClass.DebugLog($"{tile.Key.ToString()}:\tx{tileX.ToString()}\ty{tileY.ToString()}\ttype{tileType.ToString()}"); + if (short.Parse(tileX.ToString()) == x && short.Parse(tileY.ToString()) == y) return (tile.Key, CATEGORY.FromString(tileType.ToString())); } From 4082cc033b1f8f99e87a58c5eb160ac4428ed5e7 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 24 Apr 2022 11:57:14 +0530 Subject: [PATCH 058/232] multiple values for same tile --- stardew-access/Features/StaticTiles.cs | 29 ++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/stardew-access/Features/StaticTiles.cs b/stardew-access/Features/StaticTiles.cs index deaa623..cd2d654 100644 --- a/stardew-access/Features/StaticTiles.cs +++ b/stardew-access/Features/StaticTiles.cs @@ -54,14 +54,35 @@ namespace stardew_access.Features if (tile.Value == null) continue; - JToken? tileX = tile.Value["x"]; - JToken? tileY = tile.Value["y"]; + JToken? tileXArray = tile.Value["x"]; + JToken? tileYArray = tile.Value["y"]; JToken? tileType = tile.Value["type"]; - if (tileX == null || tileY == null || tileType == null) + if (tileXArray == null || tileYArray == null || tileType == null) continue; - if (short.Parse(tileX.ToString()) == x && short.Parse(tileY.ToString()) == y) + bool isXPresent = false; + bool isYPresent = false; + + foreach (var item in tileXArray) + { + if (short.Parse(item.ToString()) == x) + { + isXPresent = true; + break; + } + } + + foreach (var item in tileYArray) + { + if (short.Parse(item.ToString()) == y) + { + isYPresent = true; + break; + } + } + + if (isXPresent && isYPresent) return (tile.Key, CATEGORY.FromString(tileType.ToString())); } } From 864ad56cfcec76d615750fe881a5e814e837d4c3 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 24 Apr 2022 12:10:13 +0530 Subject: [PATCH 059/232] Added bridge in CATEGORY --- stardew-access/Features/TileInfo.cs | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index d9338da..e5a0e24 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -59,12 +59,14 @@ namespace stardew_access.Features return CATEGORY.Doors; else if (name == "water") return CATEGORY.WaterTiles; - else if (name == "interactables") + else if (name == "interactable") return CATEGORY.Interactables; else if (name == "decoration") return CATEGORY.Decor; - else if (name == "machines") + else if (name == "machine") return CATEGORY.Machines; + else if (name == "bridge") + return CATEGORY.Bridges; else if (name == "other") return CATEGORY.Others; @@ -87,9 +89,10 @@ namespace stardew_access.Features public static CATEGORY JunimoBundle = new CATEGORY("bundle"); public static CATEGORY Doors = new CATEGORY("door"); // Also includes ladders and elevators public static CATEGORY WaterTiles = new CATEGORY("water"); - public static CATEGORY Interactables = new CATEGORY("interactables"); + public static CATEGORY Interactables = new CATEGORY("interactable"); public static CATEGORY Decor = new CATEGORY("decoration"); - public static CATEGORY Machines = new CATEGORY("machines"); + public static CATEGORY Machines = new CATEGORY("machine"); + public static CATEGORY Bridges = new CATEGORY("bridge"); public static CATEGORY Others = new CATEGORY("other"); } @@ -407,21 +410,6 @@ namespace stardew_access.Features if (index != null) { - switch (index) - { - case 1955: - case 41: - return (CATEGORY.Furnitures, "Mail Box"); - case 1003: - return (CATEGORY.Furnitures, "Street lamp"); - case 78: - return (CATEGORY.Furnitures, "Trash bin"); - case 617: - return (CATEGORY.Furnitures, "Daily quest"); - case 616: - return (CATEGORY.Furnitures, "Calender"); - } - if (Game1.currentLocation is FarmHouse || Game1.currentLocation is IslandFarmHouse) { switch (index) From 21c5080f7314ecc8d52afa4d00a48da5592cb1ad Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 24 Apr 2022 12:14:18 +0530 Subject: [PATCH 060/232] Added more to the default exclusion list --- stardew-access/Features/Radar.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/stardew-access/Features/Radar.cs b/stardew-access/Features/Radar.cs index 8235ebc..7cef653 100644 --- a/stardew-access/Features/Radar.cs +++ b/stardew-access/Features/Radar.cs @@ -41,7 +41,11 @@ namespace stardew_access.Features exclusions.Add("tree"); exclusions.Add("flooring"); exclusions.Add("water"); + exclusions.Add("debris"); exclusions.Add("grass"); + exclusions.Add("decoration"); + exclusions.Add("bridge"); + exclusions.Add("other"); /* Not excluded Categories * @@ -50,10 +54,14 @@ namespace stardew_access.Features * exclusions.Add("animal"); * exclusions.Add("npc"); * exclusions.Add("furniture") - * exclusions.Add("other"); * exclusions.Add("building"); + * exclusions.Add("resource clump"); * exclusions.Add("mine item"); * exclusions.Add("chest"); + * exclusions.Add("bundle"); + * exclusions.Add("door"); + * exclusions.Add("machine"); + * exclusions.Add("interactable"); */ } From 2a03fa67244cfd5a569c776602f01631e4b784ec Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 24 Apr 2022 12:26:53 +0530 Subject: [PATCH 061/232] Added interactables to town location --- stardew-access/Features/TileInfo.cs | 10 +++---- stardew-access/static-tiles.json | 45 +++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index e5a0e24..b2c557f 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -395,17 +395,17 @@ namespace stardew_access.Features if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null) index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex; - /* Add More - MainClass.monitor.Log(index.ToString(), LogLevel.Debug); - */ if (Game1.currentLocation is Farm) { Building building = ((Farm)Game1.currentLocation).getBuildingAt(new Vector2(x, y)); if (building != null) - { return (CATEGORY.Buildings, building.buildingType.Value); - } + } + else if (Game1.currentLocation is Town) + { + if (SpecialOrder.IsSpecialOrdersBoardUnlocked() && x == 62 && y == 93) + return (CATEGORY.Interactables, "Special quest board"); } if (index != null) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 8064919..4e2816d 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -3,15 +3,48 @@ { "Ticket Machine": { - "x":7, - "y":11, - "type":"interactables" + "x":[7], + "y":[11], + "type":"interactable" }, "Minecart": { - "x":4, - "y":3, - "type":"interactables" + "x":[4,5], + "y":[3], + "type":"interactable" + } + }, + "town": + { + "Calender Board": + { + "x":[41], + "y":[56], + "type":"interactable" + }, + "Daily Quest Board": + { + "x":[42], + "y":[56], + "type":"interactable" + }, + "Sewer": + { + "x":[34,35], + "y":[95,96], + "type":"interactable" + }, + "Ice Cream Stand": + { + "x":[88], + "y":[92], + "type":"interactable" + }, + "Minecart": + { + "x":[105,106], + "y":[79], + "type":"interactable" } } } \ No newline at end of file From bafc9660722a5307829416c1f8418dbc3b4cd34d Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 24 Apr 2022 13:14:34 +0530 Subject: [PATCH 062/232] Added command to refresh static tiles json --- stardew-access/CustomCommands.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index fc17160..e8bba02 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -480,6 +480,13 @@ namespace stardew_access MainClass.DebugLog("Mod Config refreshed!"); }); + + helper.ConsoleCommands.Add("refst", "Refresh static tiles", (string commmand, string[] args) => + { + MainClass.STiles = new Features.StaticTiles(); + + MainClass.DebugLog("Static tiles refreshed!"); + }); } } } From d9f99ac922a63a219f7f52161e37f5dddf486750 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 24 Apr 2022 13:14:52 +0530 Subject: [PATCH 063/232] Added static tiles in coops --- stardew-access/static-tiles.json | 333 +++++++++++++++++++++++++++++++ 1 file changed, 333 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 4e2816d..7249e20 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -46,5 +46,338 @@ "y":[79], "type":"interactable" } + }, + "coop": + { + "Hay Hopper": + { + "x":[3], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[7], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[9], + "y":[3], + "type":"interactable" + } + }, + "big coop": + { + "Hay Hopper": + { + "x":[3], + "y":[3], + "type":"interactable" + }, + "Incubator": + { + "x":[2], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[7], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[13], + "y":[3], + "type":"interactable" + } + }, + "coop2": + { + "Hay Hopper": + { + "x":[3], + "y":[3], + "type":"interactable" + }, + "Incubator": + { + "x":[2], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[7], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[13], + "y":[3], + "type":"interactable" + } + }, + "deluxe coop": + { + "Hay Hopper": + { + "x":[3], + "y":[3], + "type":"interactable" + }, + "Incubator": + { + "x":[2], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[7], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[13], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 9": + { + "x":[14], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 10": + { + "x":[15], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 11": + { + "x":[16], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 12": + { + "x":[17], + "y":[3], + "type":"interactable" + } + }, + "coop3": + { + "Hay Hopper": + { + "x":[3], + "y":[3], + "type":"interactable" + }, + "Incubator": + { + "x":[2], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[7], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[13], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 9": + { + "x":[14], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 10": + { + "x":[15], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 11": + { + "x":[16], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 12": + { + "x":[17], + "y":[3], + "type":"interactable" + } } } \ No newline at end of file From 5355a2015ed127ce11d51c4f9d4edb3cba8bda35 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 28 Apr 2022 20:00:27 +0530 Subject: [PATCH 064/232] Added more dynamic tiles --- stardew-access/Features/TileInfo.cs | 48 +++++++++++++---------------- stardew-access/manifest.json | 2 +- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index b2c557f..fe2e987 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -134,7 +134,7 @@ namespace stardew_access.Features bool isColliding = isCollidingAtTile(x, y); Dictionary> terrainFeature = Game1.currentLocation.terrainFeatures.FieldDict; string? door = getDoorAtTile(x, y); - (CATEGORY? category, string? name) tileInfo = getTileInfo(x, y); + (CATEGORY? category, string? name) tileInfo = getDynamicTilesInfo(x, y); string? junimoBundle = getJunimoBundleAt(x, y); string? resourceClump = getResourceClumpAtTile(x, y); string? farmAnimal = getFarmAnimalAt(Game1.currentLocation, x, y); @@ -388,17 +388,11 @@ namespace stardew_access.Features /// /// category: This is the category of the tile. Default to Furnitures. ///
name: This is the name of the tile. Default to null if the tile tile has nothing on it.
- public static (CATEGORY? category, string? name) getTileInfo(int x, int y) + public static (CATEGORY? category, string? name) getDynamicTilesInfo(int x, int y) { - - int? index = null; - - if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null) - index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex; - - if (Game1.currentLocation is Farm) + if (Game1.currentLocation is Farm farm) { - Building building = ((Farm)Game1.currentLocation).getBuildingAt(new Vector2(x, y)); + Building building = farm.getBuildingAt(new Vector2(x, y)); if (building != null) return (CATEGORY.Buildings, building.buildingType.Value); } @@ -407,23 +401,25 @@ namespace stardew_access.Features if (SpecialOrder.IsSpecialOrdersBoardUnlocked() && x == 62 && y == 93) return (CATEGORY.Interactables, "Special quest board"); } - - if (index != null) + else if (Game1.currentLocation is FarmHouse farmHouse) { - if (Game1.currentLocation is FarmHouse || Game1.currentLocation is IslandFarmHouse) - { - switch (index) - { - case 173: - return (CATEGORY.Furnitures, "Fridge"); - case 169: - case 170: - case 171: - case 172: - return (CATEGORY.Furnitures, "Kitchen"); - } - } - + if (farmHouse.upgradeLevel >= 1) + if (farmHouse.getKitchenStandingSpot().X == x && (farmHouse.getKitchenStandingSpot().Y - 1) == y) // Standing spot is where the player will stand + return (CATEGORY.Interactables, "Kitchen"); + else if (farmHouse.fridgePosition.X == x && farmHouse.fridgePosition.Y == y) + return (CATEGORY.Interactables, "Fridge"); + } + else if (Game1.currentLocation is IslandFarmHouse islandFarmHouse) + { + if ((islandFarmHouse.fridgePosition.X - 1) == x && islandFarmHouse.fridgePosition.Y == y) + return (CATEGORY.Interactables, "Kitchen"); + else if (islandFarmHouse.fridgePosition.X == x && islandFarmHouse.fridgePosition.Y == y) + return (CATEGORY.Interactables, "Fridge"); + } + else if (Game1.currentLocation is Forest forest) + { + if (forest.travelingMerchantDay && x == 27 && y == 11) + return (CATEGORY.Interactables, "Travelling Merchant"); } return (null, null); diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index 0148672..4f0403b 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.1.7", + "Version": "1.1.8", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From 253d01ac6fb31d61fa1e8383798837e7d4b33edf Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 28 Apr 2022 20:12:07 +0530 Subject: [PATCH 065/232] Added barn to static tiles --- stardew-access/static-tiles.json | 309 +++++++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 7249e20..dc9c013 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -379,5 +379,314 @@ "y":[3], "type":"interactable" } + }, + "barn": + { + "Hay Hopper": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[11], + "y":[3], + "type":"interactable" + } + }, + "barn2": + { + "Hay Hopper": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[13], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[14], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[15], + "y":[3], + "type":"interactable" + } + }, + "big barn": + { + "Hay Hopper": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[13], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[14], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[15], + "y":[3], + "type":"interactable" + } + }, + "barn3": + { + "Hay Hopper": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[13], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[14], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[15], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 9": + { + "x":[16], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 10": + { + "x":[17], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 11": + { + "x":[18], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 12": + { + "x":[19], + "y":[3], + "type":"interactable" + } + }, + "deluxe barn": + { + "Hay Hopper": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[13], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[14], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[15], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 9": + { + "x":[16], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 10": + { + "x":[17], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 11": + { + "x":[18], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 12": + { + "x":[19], + "y":[3], + "type":"interactable" + } } } \ No newline at end of file From c0ad4b83ab330e2338931f66097d3b56fd5b1ea4 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 28 Apr 2022 22:54:19 +0530 Subject: [PATCH 066/232] Code organization --- stardew-access/Features/TileInfo.cs | 95 ---------------------------- stardew-access/Features/Utils.cs | 96 +++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 95 deletions(-) create mode 100644 stardew-access/Features/Utils.cs diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index fe2e987..bc74204 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -7,101 +7,6 @@ using StardewValley.TerrainFeatures; namespace stardew_access.Features { - - /// - /// This is a custom enum class and contains the name of groups the objects are divided into for the feature - /// - public class CATEGORY - { - private string _typeKeyWord; - - private CATEGORY(string typeKeyWord) - { - _typeKeyWord = typeKeyWord; - } - - public override string ToString() - { - return _typeKeyWord; - } - - public static CATEGORY FromString(string name) - { - if (name == "farmer") - return CATEGORY.Farmers; - else if (name == "animal") - return CATEGORY.FarmAnimals; - else if (name == "npc") - return CATEGORY.NPCs; - else if (name == "furniture") - return CATEGORY.Furnitures; - else if (name == "flooring") - return CATEGORY.Flooring; - else if (name == "debris") - return CATEGORY.Debris; - else if (name == "crop") - return CATEGORY.Crops; - else if (name == "tree") - return CATEGORY.Trees; - else if (name == "bush") - return CATEGORY.Bush; - else if (name == "building") - return CATEGORY.Buildings; - else if (name == "mine item") - return CATEGORY.MineItems; - else if (name == "resource clump") - return CATEGORY.ResourceClumps; - else if (name == "chest") - return CATEGORY.Chests; - else if (name == "bundle") - return CATEGORY.JunimoBundle; - else if (name == "door") - return CATEGORY.Doors; - else if (name == "water") - return CATEGORY.WaterTiles; - else if (name == "interactable") - return CATEGORY.Interactables; - else if (name == "decoration") - return CATEGORY.Decor; - else if (name == "machine") - return CATEGORY.Machines; - else if (name == "bridge") - return CATEGORY.Bridges; - else if (name == "other") - return CATEGORY.Others; - - return Others; - } - - public static CATEGORY Farmers = new CATEGORY("farmer"); - public static CATEGORY FarmAnimals = new CATEGORY("animal"); - public static CATEGORY NPCs = new CATEGORY("npc"); - public static CATEGORY Furnitures = new CATEGORY("furniture"); - public static CATEGORY Flooring = new CATEGORY("flooring"); - public static CATEGORY Debris = new CATEGORY("debris"); - public static CATEGORY Crops = new CATEGORY("crop"); - public static CATEGORY Trees = new CATEGORY("tree"); - public static CATEGORY Bush = new CATEGORY("bush"); - public static CATEGORY Buildings = new CATEGORY("building"); - public static CATEGORY MineItems = new CATEGORY("mine item"); - public static CATEGORY ResourceClumps = new CATEGORY("resource clump"); - public static CATEGORY Chests = new CATEGORY("chest"); - public static CATEGORY JunimoBundle = new CATEGORY("bundle"); - public static CATEGORY Doors = new CATEGORY("door"); // Also includes ladders and elevators - public static CATEGORY WaterTiles = new CATEGORY("water"); - public static CATEGORY Interactables = new CATEGORY("interactable"); - public static CATEGORY Decor = new CATEGORY("decoration"); - public static CATEGORY Machines = new CATEGORY("machine"); - public static CATEGORY Bridges = new CATEGORY("bridge"); - public static CATEGORY Others = new CATEGORY("other"); - - } - - public enum MachineState - { - Ready, Busy, Waiting - } - public class TileInfo { public static string[] trackable_machines = { "bee house", "cask", "press", "keg", "machine", "maker", "preserves jar", "bone mill", "kiln", "crystalarium", "furnace", "geode crusher", "tapper", "lightning rod", "incubator", "wood chipper", "worm bin", "loom" }; diff --git a/stardew-access/Features/Utils.cs b/stardew-access/Features/Utils.cs new file mode 100644 index 0000000..f8d6945 --- /dev/null +++ b/stardew-access/Features/Utils.cs @@ -0,0 +1,96 @@ +namespace stardew_access.Features +{ + /// + /// This is a custom enum class and contains the name of groups the objects are divided into for the feature + /// + public class CATEGORY + { + private string _typeKeyWord; + + private CATEGORY(string typeKeyWord) + { + _typeKeyWord = typeKeyWord; + } + + public override string ToString() + { + return _typeKeyWord; + } + + public static CATEGORY FromString(string name) + { + if (name == "farmer") + return CATEGORY.Farmers; + else if (name == "animal") + return CATEGORY.FarmAnimals; + else if (name == "npc") + return CATEGORY.NPCs; + else if (name == "furniture") + return CATEGORY.Furnitures; + else if (name == "flooring") + return CATEGORY.Flooring; + else if (name == "debris") + return CATEGORY.Debris; + else if (name == "crop") + return CATEGORY.Crops; + else if (name == "tree") + return CATEGORY.Trees; + else if (name == "bush") + return CATEGORY.Bush; + else if (name == "building") + return CATEGORY.Buildings; + else if (name == "mine item") + return CATEGORY.MineItems; + else if (name == "resource clump") + return CATEGORY.ResourceClumps; + else if (name == "chest") + return CATEGORY.Chests; + else if (name == "bundle") + return CATEGORY.JunimoBundle; + else if (name == "door") + return CATEGORY.Doors; + else if (name == "water") + return CATEGORY.WaterTiles; + else if (name == "interactable") + return CATEGORY.Interactables; + else if (name == "decoration") + return CATEGORY.Decor; + else if (name == "machine") + return CATEGORY.Machines; + else if (name == "bridge") + return CATEGORY.Bridges; + else if (name == "other") + return CATEGORY.Others; + + return Others; + } + + public static CATEGORY Farmers = new CATEGORY("farmer"); + public static CATEGORY FarmAnimals = new CATEGORY("animal"); + public static CATEGORY NPCs = new CATEGORY("npc"); + public static CATEGORY Furnitures = new CATEGORY("furniture"); + public static CATEGORY Flooring = new CATEGORY("flooring"); + public static CATEGORY Debris = new CATEGORY("debris"); + public static CATEGORY Crops = new CATEGORY("crop"); + public static CATEGORY Trees = new CATEGORY("tree"); + public static CATEGORY Bush = new CATEGORY("bush"); + public static CATEGORY Buildings = new CATEGORY("building"); + public static CATEGORY MineItems = new CATEGORY("mine item"); + public static CATEGORY ResourceClumps = new CATEGORY("resource clump"); + public static CATEGORY Chests = new CATEGORY("chest"); + public static CATEGORY JunimoBundle = new CATEGORY("bundle"); + public static CATEGORY Doors = new CATEGORY("door"); // Also includes ladders and elevators + public static CATEGORY WaterTiles = new CATEGORY("water"); + public static CATEGORY Interactables = new CATEGORY("interactable"); + public static CATEGORY Decor = new CATEGORY("decoration"); + public static CATEGORY Machines = new CATEGORY("machine"); + public static CATEGORY Bridges = new CATEGORY("bridge"); + public static CATEGORY Others = new CATEGORY("other"); + + } + + public enum MachineState + { + Ready, Busy, Waiting + } +} \ No newline at end of file From 4db3c305f338a0d5f1df7c3218def3686bb32831 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 28 Apr 2022 23:05:52 +0530 Subject: [PATCH 067/232] Added slime hutch static tiles --- stardew-access/static-tiles.json | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index dc9c013..dadd8b9 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -688,5 +688,32 @@ "y":[3], "type":"interactable" } + }, + "slime hutch": + { + "Water Trough 1": + { + "x":[16], + "y":[6], + "type":"interactable" + }, + "Water Trough 2": + { + "x":[16], + "y":[7], + "type":"interactable" + }, + "Water Trough 3": + { + "x":[16], + "y":[8], + "type":"interactable" + }, + "Water Trough 4": + { + "x":[16], + "y":[9], + "type":"interactable" + } } } \ No newline at end of file From fb7791e7ba883808877939f6ac27ccec62a12488 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 28 Apr 2022 23:12:53 +0530 Subject: [PATCH 068/232] Added adventure guild static tiles --- stardew-access/static-tiles.json | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index dadd8b9..56f8d2f 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -715,5 +715,26 @@ "y":[9], "type":"interactable" } + }, + "adventureguild": + { + "Goals Board": + { + "x":[8], + "y":[10], + "type":"interactable" + }, + "Counter": + { + "x":[5], + "y":[12], + "type":"interactable" + }, + "Gil": + { + "x":[11], + "y":[12], + "type":"farmer" + } } } \ No newline at end of file From 4dff690bbe755aa16df58d46ce5b7bafd4efa2b1 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 28 Apr 2022 23:50:26 +0530 Subject: [PATCH 069/232] Added beach bridge --- stardew-access/Features/TileInfo.cs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index bc74204..247370b 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -39,7 +39,7 @@ namespace stardew_access.Features bool isColliding = isCollidingAtTile(x, y); Dictionary> terrainFeature = Game1.currentLocation.terrainFeatures.FieldDict; string? door = getDoorAtTile(x, y); - (CATEGORY? category, string? name) tileInfo = getDynamicTilesInfo(x, y); + (CATEGORY? category, string? name) dynamicTile = getDynamicTilesInfo(x, y); string? junimoBundle = getJunimoBundleAt(x, y); string? resourceClump = getResourceClumpAtTile(x, y); string? farmAnimal = getFarmAnimalAt(Game1.currentLocation, x, y); @@ -65,6 +65,11 @@ namespace stardew_access.Features toReturn = staticTile.name; category = staticTile.category; } + else if (dynamicTile.name != null) + { + toReturn = dynamicTile.name; + category = dynamicTile.category; + } else if (Game1.currentLocation is VolcanoDungeon && ((VolcanoDungeon)Game1.currentLocation).IsCooledLava(x, y)) { toReturn = "Cooled lava"; @@ -132,11 +137,6 @@ namespace stardew_access.Features toReturn = parrot; category = CATEGORY.Buildings; } - else if (tileInfo.name != null) - { - toReturn = tileInfo.name; - category = tileInfo.category; - } else if (junimoBundle != null) { toReturn = junimoBundle; @@ -326,6 +326,16 @@ namespace stardew_access.Features if (forest.travelingMerchantDay && x == 27 && y == 11) return (CATEGORY.Interactables, "Travelling Merchant"); } + else if (Game1.currentLocation is Beach beach) + { + if (x == 58 && y == 13) + { + if (!beach.bridgeFixed.Value) + return (CATEGORY.Interactables, "Repair Bridge"); + else + return (CATEGORY.Bridges, "Bridge"); + } + } return (null, null); } @@ -556,7 +566,7 @@ namespace stardew_access.Features } else if (obj is Furniture) toReturn.category = CATEGORY.Furnitures; - else if (obj.type == "Crafting" && obj.bigCraftable) + else if (obj.Type == "Crafting" && obj.bigCraftable.Value) { foreach (string machine in trackable_machines) From d4e31f8a1afe080213780d0eea9341abc76f77fd Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Fri, 29 Apr 2022 00:12:13 +0530 Subject: [PATCH 070/232] Added caldera static tiles --- stardew-access/static-tiles.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 56f8d2f..8d036c8 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -736,5 +736,20 @@ "y":[12], "type":"farmer" } + }, + "caldera": + { + "Rare Chest": + { + "x":[25], + "y":[28], + "type":"chest" + }, + "Forge": + { + "x":[23], + "y":[21], + "type":"interactable" + } } } \ No newline at end of file From 2dbc52a994c3d2f0af5e1d67a528247323ccd151 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Fri, 29 Apr 2022 19:41:30 +0530 Subject: [PATCH 071/232] Added club/casino static tiles --- stardew-access/static-tiles.json | 61 ++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 8d036c8..c74e63e 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -724,7 +724,7 @@ "y":[10], "type":"interactable" }, - "Counter": + "Shop Counter": { "x":[5], "y":[12], @@ -734,7 +734,7 @@ { "x":[11], "y":[12], - "type":"farmer" + "type":"npc" } }, "caldera": @@ -751,5 +751,62 @@ "y":[21], "type":"interactable" } + }, + "club": + { + "Coin Machine": + { + "x":[12], + "y":[4], + "type":"interactable" + }, + "Shop Counter": + { + "x":[25], + "y":[3], + "type":"interactable" + }, + "Calico Spin Machine": + { + "x":[11,13,15], + "y":[8,10], + "type":"interactable" + }, + "High Stakes Calico Jack Table": + { + "x":[23,24], + "y":[10,11], + "type":"interactable" + }, + "Low Stakes Calico Jack Table": + { + "x":[3], + "y":[7,9], + "type":"interactable" + }, + "Man": + { + "x":[13], + "y":[11], + "type":"npc" + }, + "Welwick": + { + "x":[18], + "y":[9], + "type":"npc" + }, + "Unknown person": + { + "x":[16], + "y":[4], + "type":"npc" + }, + "Stats Checker": + { + "x":[3], + "y":[4], + "type":"interactable" + } } } \ No newline at end of file From c4290173fdb39bfa670cfca54d097177b234f54e Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Fri, 29 Apr 2022 19:46:26 +0530 Subject: [PATCH 072/232] Added missed rewards bundle to read tile --- stardew-access/Features/TileInfo.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 247370b..169eb88 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -336,6 +336,11 @@ namespace stardew_access.Features return (CATEGORY.Bridges, "Bridge"); } } + else if (Game1.currentLocation is CommunityCenter communityCenter) + { + if (communityCenter.missedRewardsChestVisible.Value && x == 22 && y == 10) + return (CATEGORY.Chests, "Missed Rewards Chest"); + } return (null, null); } From 9e28399a0e03a7bcf5fd425691196cf815dddd45 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Fri, 29 Apr 2022 20:00:26 +0530 Subject: [PATCH 073/232] Added desert static tiles --- stardew-access/static-tiles.json | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index c74e63e..bca0e91 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -808,5 +808,50 @@ "y":[4], "type":"interactable" } + }, + "desert": + { + "Bus": + { + "x":[18], + "y":[27], + "type":"interactable" + }, + "Desert Trader": + { + "x":[42], + "y":[23], + "type":"interactable" + }, + "Three Pillars": + { + "x":[34,37,40], + "y":[8,13], + "type":"decoration" + }, + "Three Pillars Center": + { + "x":[37], + "y":[11], + "type":"interactable" + }, + "Skull Cavern Entrance": + { + "x":[8], + "y":[6], + "type":"door" + }, + "Desert Warp Statue": + { + "x":[35], + "y":[43], + "type":"decoration" + }, + "Sand Dragon Skull": + { + "x":[9,10], + "y":[35,36], + "type":"decoration" + } } } \ No newline at end of file From 08af64d560edfa714d5fe47e833b97a2760ac821 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Fri, 29 Apr 2022 20:22:56 +0530 Subject: [PATCH 074/232] Added fish shop and boat tunnel static tiles --- stardew-access/Features/TileInfo.cs | 9 +++++++++ stardew-access/static-tiles.json | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 169eb88..e01b56b 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -341,6 +341,15 @@ namespace stardew_access.Features if (communityCenter.missedRewardsChestVisible.Value && x == 22 && y == 10) return (CATEGORY.Chests, "Missed Rewards Chest"); } + else if (Game1.currentLocation is BoatTunnel) + { + if (x == 4 && y == 9) + return (CATEGORY.Interactables, ((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatFixed")) ? "Repair " : "") + "Ticket Machine"); + else if (x == 6 && y == 8) + return (((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatHull")) ? CATEGORY.Interactables : CATEGORY.Decor), ((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatHull")) ? "Repair " : "") + "Boat Hull"); + else if (x == 8 && y == 9) + return (((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatAnchor")) ? CATEGORY.Interactables : CATEGORY.Decor), ((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatAnchor")) ? "Repair " : "") + "Boat Anchor"); + } return (null, null); } diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index bca0e91..fa05c02 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -853,5 +853,14 @@ "y":[35,36], "type":"decoration" } + }, + "fishshop": + { + "Shop Counter": + { + "x":[5], + "y":[5], + "type":"interactable" + } } } \ No newline at end of file From e1e75ab4495729e1e6f3a943660f799260657254 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 30 Apr 2022 20:04:54 +0530 Subject: [PATCH 075/232] Added exit points to existing maps --- stardew-access/static-tiles.json | 156 +++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index fa05c02..ca1d8d5 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -12,6 +12,18 @@ "x":[4,5], "y":[3], "type":"interactable" + }, + "Farm Entrance": + { + "x":[0], + "y":[23], + "type":"door" + }, + "Town Entrance": + { + "x":[34], + "y":[23], + "type":"door" } }, "town": @@ -45,6 +57,30 @@ "x":[105,106], "y":[79], "type":"interactable" + }, + "Bus Stop Entrance": + { + "x":[0], + "y":[54], + "type":"door" + }, + "Cindersap Forest Entrance": + { + "x":[0], + "y":[90], + "type":"door" + }, + "Beach Entrance": + { + "x":[54], + "y":[109], + "type":"door" + }, + "Mountain Entrance": + { + "x":[81], + "y":[0], + "type":"door" } }, "coop": @@ -78,6 +114,12 @@ "x":[9], "y":[3], "type":"interactable" + }, + "Exit": + { + "x":[2], + "y":[9], + "type":"door" } }, "big coop": @@ -141,6 +183,12 @@ "x":[13], "y":[3], "type":"interactable" + }, + "Exit": + { + "x":[2], + "y":[9], + "type":"door" } }, "coop2": @@ -204,6 +252,12 @@ "x":[13], "y":[3], "type":"interactable" + }, + "Exit": + { + "x":[2], + "y":[9], + "type":"door" } }, "deluxe coop": @@ -291,6 +345,12 @@ "x":[17], "y":[3], "type":"interactable" + }, + "Exit": + { + "x":[2], + "y":[9], + "type":"door" } }, "coop3": @@ -378,6 +438,12 @@ "x":[17], "y":[3], "type":"interactable" + }, + "Exit": + { + "x":[2], + "y":[9], + "type":"door" } }, "barn": @@ -411,6 +477,12 @@ "x":[11], "y":[3], "type":"interactable" + }, + "Exit": + { + "x":[11], + "y":[14], + "type":"door" } }, "barn2": @@ -468,6 +540,12 @@ "x":[15], "y":[3], "type":"interactable" + }, + "Exit": + { + "x":[11], + "y":[14], + "type":"door" } }, "big barn": @@ -525,6 +603,12 @@ "x":[15], "y":[3], "type":"interactable" + }, + "Exit": + { + "x":[11], + "y":[14], + "type":"door" } }, "barn3": @@ -606,6 +690,12 @@ "x":[19], "y":[3], "type":"interactable" + }, + "Exit": + { + "x":[11], + "y":[14], + "type":"door" } }, "deluxe barn": @@ -687,6 +777,12 @@ "x":[19], "y":[3], "type":"interactable" + }, + "Exit": + { + "x":[11], + "y":[14], + "type":"door" } }, "slime hutch": @@ -714,6 +810,12 @@ "x":[16], "y":[9], "type":"interactable" + }, + "Exit": + { + "x":[8], + "y":[12], + "type":"door" } }, "adventureguild": @@ -735,6 +837,12 @@ "x":[11], "y":[12], "type":"npc" + }, + "Exit": + { + "x":[6], + "y":[19], + "type":"door" } }, "caldera": @@ -750,6 +858,33 @@ "x":[23], "y":[21], "type":"interactable" + }, + "Volcano Dungeon 0 Entrance": + { + "x":[11], + "y":[36], + "type":"door" + }, + "Volcano Dungeon 9 Entrance": + { + "x":[21], + "y":[39], + "type":"door" + } + }, + "volcanodungeon0": + { + "Caldera Entrance": + { + "x":[44], + "y":[50], + "type":"door" + }, + "Volcano Dungeon 1 Entrance": + { + "x":[37], + "y":[5], + "type":"door" } }, "club": @@ -807,6 +942,12 @@ "x":[3], "y":[4], "type":"interactable" + }, + "Exit": + { + "x":[8], + "y":[12], + "type":"door" } }, "desert": @@ -861,6 +1002,21 @@ "x":[5], "y":[5], "type":"interactable" + }, + "Exit": + { + "x":[5], + "y":[9], + "type":"door" + } + }, + "boattunnel": + { + "Exit": + { + "x":[6], + "y":[11], + "type":"door" } } } \ No newline at end of file From 97fabf76382ccce3575fe61261816e8e1d73fd90 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 30 Apr 2022 20:16:51 +0530 Subject: [PATCH 076/232] Added building human and animal doors to read tile --- stardew-access/Features/TileInfo.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index e01b56b..b809df3 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -299,7 +299,14 @@ namespace stardew_access.Features { Building building = farm.getBuildingAt(new Vector2(x, y)); if (building != null) - return (CATEGORY.Buildings, building.buildingType.Value); + { + if ((building.humanDoor.Value.X + building.tileX.Value) == x && (building.humanDoor.Value.Y + building.tileY.Value) == y) + return (CATEGORY.Doors, building.buildingType.Value + " Door"); + else if ((building.animalDoor.Value.X + building.tileX.Value) == x && (building.animalDoor.Value.Y + building.tileY.Value) == y) + return (CATEGORY.Doors, building.buildingType.Value + " Animal Door " + ((building.animalDoorOpen.Value) ? "Opened" : "Closed")); + else + return (CATEGORY.Buildings, building.buildingType.Value); + } } else if (Game1.currentLocation is Town) { From cb450b95fa378d7179199aadddebe2dc33aad977 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 30 Apr 2022 20:23:17 +0530 Subject: [PATCH 077/232] Added exits for farm houses --- stardew-access/Features/TileInfo.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index b809df3..875c408 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -315,7 +315,9 @@ namespace stardew_access.Features } else if (Game1.currentLocation is FarmHouse farmHouse) { - if (farmHouse.upgradeLevel >= 1) + if (farmHouse.getEntryLocation().X == x && farmHouse.getEntryLocation().Y == y) + return (CATEGORY.Doors, "Exit"); + else if (farmHouse.upgradeLevel >= 1) if (farmHouse.getKitchenStandingSpot().X == x && (farmHouse.getKitchenStandingSpot().Y - 1) == y) // Standing spot is where the player will stand return (CATEGORY.Interactables, "Kitchen"); else if (farmHouse.fridgePosition.X == x && farmHouse.fridgePosition.Y == y) @@ -323,7 +325,9 @@ namespace stardew_access.Features } else if (Game1.currentLocation is IslandFarmHouse islandFarmHouse) { - if ((islandFarmHouse.fridgePosition.X - 1) == x && islandFarmHouse.fridgePosition.Y == y) + if (x == 14 && y == 17) + return (CATEGORY.Doors, "Exit"); + else if ((islandFarmHouse.fridgePosition.X - 1) == x && islandFarmHouse.fridgePosition.Y == y) return (CATEGORY.Interactables, "Kitchen"); else if (islandFarmHouse.fridgePosition.X == x && islandFarmHouse.fridgePosition.Y == y) return (CATEGORY.Interactables, "Fridge"); From d93315298277ba33b425ed8506aa93f7cbbb04c9 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 30 Apr 2022 22:09:53 +0530 Subject: [PATCH 078/232] Added forest static & dynamic tiles --- stardew-access/Features/TileInfo.cs | 4 ++ stardew-access/static-tiles.json | 66 +++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 875c408..dfe6c69 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -336,6 +336,10 @@ namespace stardew_access.Features { if (forest.travelingMerchantDay && x == 27 && y == 11) return (CATEGORY.Interactables, "Travelling Merchant"); + else if (forest.log != null && x == 2 && y == 7) + return (CATEGORY.Interactables, "Log"); + else if (forest.log == null && x == 0 && y == 7) + return (CATEGORY.Doors, "Secret Woods Entrance"); } else if (Game1.currentLocation is Beach beach) { diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index ca1d8d5..32c8b7b 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1018,5 +1018,71 @@ "y":[11], "type":"door" } + }, + "beach": + { + "Town Entrance": + { + "x":[38], + "y":[0], + "type":"door" + }, + "Beach Warp Statue": + { + "x":[20], + "y":[4], + "type":"decoration" + } + }, + "forest": + { + "Farm Entrance": + { + "x":[68], + "y":[0], + "type":"door" + }, + "Town Entrance": + { + "x":[119], + "y":[25], + "type":"door" + }, + "Bridge 1": + { + "x":[77,82], + "y":[49], + "type":"bridge" + }, + "Bridge 2": + { + "x":[87], + "y":[52,56], + "type":"bridge" + }, + "Bridge 3": + { + "x":[65,62], + "y":[70], + "type":"bridge" + }, + "Bridge 4": + { + "x":[41], + "y":[79,82], + "type":"bridge" + }, + "Bridge 5": + { + "x":[38], + "y":[85,87], + "type":"bridge" + }, + "Abandoned House": + { + "x":[34], + "y":[95], + "type":"interactable" + } } } \ No newline at end of file From ababbcbe07d48aff61eee1b3a434fc8eebaea908 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 30 Apr 2022 22:29:20 +0530 Subject: [PATCH 079/232] Added Beach night market static tiles --- stardew-access/Features/TileInfo.cs | 2 +- stardew-access/static-tiles.json | 105 ++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index dfe6c69..8dd8f6a 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -335,7 +335,7 @@ namespace stardew_access.Features else if (Game1.currentLocation is Forest forest) { if (forest.travelingMerchantDay && x == 27 && y == 11) - return (CATEGORY.Interactables, "Travelling Merchant"); + return (CATEGORY.Interactables, "Travelling Cart"); else if (forest.log != null && x == 2 && y == 7) return (CATEGORY.Interactables, "Log"); else if (forest.log == null && x == 0 && y == 7) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 32c8b7b..4fd4694 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1084,5 +1084,110 @@ "y":[95], "type":"interactable" } + }, + "beachnightmarket": + { + "Desert Trader": + { + "x":[14], + "y":[37], + "type":"npc" + }, + "Famous Painter Lupini": + { + "x":[43], + "y":[34], + "type":"npc" + }, + "Fishing Submarine": + { + "x":[5], + "y":[34], + "type":"door" + }, + "Travelling Cart": + { + "x":[39], + "y":[30], + "type":"interactable" + }, + "Shrouded Figure": + { + "x":[32], + "y":[34], + "type":"npc" + }, + "Decoration Boat": + { + "x":[19], + "y":[33], + "type":"interactable" + }, + "Magic Shop Boat": + { + "x":[48], + "y":[34], + "type":"interactable" + }, + "Mermaid Boat": + { + "x":[58], + "y":[31], + "type":"door" + } + }, + "mermaidhouse": + { + "Exit": + { + "x":[4], + "y":[10], + "type":"door" + }, + "Clam Shell 1": + { + "x":[2], + "y":[6], + "type":"interactable" + }, + "Clam Shell 2": + { + "x":[3], + "y":[6], + "type":"interactable" + }, + "Clam Shell 3": + { + "x":[4], + "y":[6], + "type":"interactable" + }, + "Clam Shell 4": + { + "x":[5], + "y":[6], + "type":"interactable" + }, + "Clam Shell 5": + { + "x":[6], + "y":[6], + "type":"interactable" + } + }, + "submarine": + { + "Exit": + { + "x":[14], + "y":[15], + "type":"door" + }, + "Captain": + { + "x":[2], + "y":[9], + "type":"npc" + } } } \ No newline at end of file From 45ad9ec42f6c2146adf0568321957808e50e9cac Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 30 Apr 2022 22:34:31 +0530 Subject: [PATCH 080/232] Added cellar exit --- stardew-access/static-tiles.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 4fd4694..e12fd5e 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1189,5 +1189,14 @@ "y":[9], "type":"npc" } + }, + "cellar": + { + "Exit": + { + "x":[3], + "y":[2], + "type":"door" + } } } \ No newline at end of file From b16fa50cd29c8cb39a071164152a274086a8789b Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 30 Apr 2022 22:37:35 +0530 Subject: [PATCH 081/232] Added community center exit --- stardew-access/static-tiles.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index e12fd5e..edb3f50 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1198,5 +1198,14 @@ "y":[2], "type":"door" } + }, + "communitycenter": + { + "Exit": + { + "x":[32], + "y":[23], + "type":"door" + } } } \ No newline at end of file From d3055f7cbe69c0ae9dbcdf9e6af91396b3213a97 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 30 Apr 2022 23:37:41 +0530 Subject: [PATCH 082/232] Added ginger island tiles and stuff --- stardew-access/Features/TileInfo.cs | 10 + stardew-access/static-tiles.json | 319 +++++++++++++++++++++++++++- 2 files changed, 327 insertions(+), 2 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 8dd8f6a..97154c3 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -365,6 +365,16 @@ namespace stardew_access.Features else if (x == 8 && y == 9) return (((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatAnchor")) ? CATEGORY.Interactables : CATEGORY.Decor), ((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatAnchor")) ? "Repair " : "") + "Boat Anchor"); } + else if (Game1.currentLocation is IslandWest islandWest) + { + if (islandWest.shippingBinPosition.X == x && islandWest.shippingBinPosition.Y == y) + return (CATEGORY.Interactables, "Shipping Bin"); + } + else if (Game1.currentLocation is IslandNorth islandNorth) + { + if (islandNorth.traderActivated.Value && x == 36 && y == 71) + return (CATEGORY.Interactables, "Island Trader"); + } return (null, null); } diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index edb3f50..fd8f2bf 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1099,7 +1099,7 @@ "y":[34], "type":"npc" }, - "Fishing Submarine": + "Fishing Submarine Door": { "x":[5], "y":[34], @@ -1129,7 +1129,7 @@ "y":[34], "type":"interactable" }, - "Mermaid Boat": + "Mermaid Boat Door": { "x":[58], "y":[31], @@ -1207,5 +1207,320 @@ "y":[23], "type":"door" } + }, + "islandeast": + { + "Banana Shrine": + { + "x":[16], + "y":[26], + "type":"interactable" + }, + "Jungle Parrot Express": + { + "x":[28], + "y":[27], + "type":"interactable" + }, + "Island Hut Entrance": + { + "x":[22], + "y":[10], + "type":"door" + }, + "Island South Entrance": + { + "x":[0], + "y":[46], + "type":"door" + }, + "Island Shrine Entrance": + { + "x":[32], + "y":[30], + "type":"door" + } + }, + "islandhut": + { + "Exit": + { + "x":[7], + "y":[13], + "type":"door" + } + }, + "islandsouth": + { + "Island East Entrance": + { + "x":[34], + "y":[12], + "type":"door" + }, + "Ginger Island Warp Statue": + { + "x":[11], + "y":[11], + "type":"decoration" + }, + "Island West Entrance": + { + "x":[0], + "y":[11], + "type":"door" + }, + "Island North Entrance": + { + "x":[17], + "y":[0], + "type":"door" + }, + "Docks Parrot Express": + { + "x":[6], + "y":[31], + "type":"interactable" + }, + "Return Boat": + { + "x":[19], + "y":[43], + "type":"interactable" + } + }, + "islandwest": + { + "Farm Parrot Express": + { + "x":[74], + "y":[9], + "type":"interactable" + }, + "Bridge 1": + { + "x":[67,62], + "y":[16], + "type":"bridge" + }, + "Qi's Walnut Room Door": + { + "x":[20], + "y":[22], + "type":"door" + }, + "Birdie": + { + "x":[18], + "y":[58], + "type":"npc" + }, + "Hole 1": + { + "x":[37], + "y":[87], + "type":"interactable" + }, + "Hole 2": + { + "x":[41], + "y":[86], + "type":"interactable" + }, + "Hole 3": + { + "x":[45], + "y":[86], + "type":"interactable" + }, + "Hole 4": + { + "x":[48], + "y":[87], + "type":"interactable" + }, + "Bridge 2": + { + "x":[55,52], + "y":[80], + "type":"bridge" + }, + "Island Farm House Door": + { + "x":[77], + "y":[39], + "type":"door" + }, + "Island Farm Cave Entrance": + { + "x":[96], + "y":[33], + "type":"door" + }, + "Island South Entrance": + { + "x":[105], + "y":[40], + "type":"door" + } + }, + "islandfarmcave": + { + "Exit": + { + "x":[4], + "y":[10], + "type":"door" + }, + "Gourmand Frog": + { + "x":[5], + "y":[4], + "type":"npc" + } + }, + "islandnorth": + { + "Island South Entrance": + { + "x":[35], + "y":[89], + "type":"door" + }, + "Island Field Office Entrance": + { + "x":[46], + "y":[46], + "type":"door" + }, + "Island North Cave Entrance": + { + "x":[21,22], + "y":[47], + "type":"door" + }, + "Dig Site Parrot Express": + { + "x":[5], + "y":[48], + "type":"interactable" + }, + "Volcano Dungeon Entrance": + { + "x":[40], + "y":[22], + "type":"door" + }, + "Volcano Parrot Express": + { + "x":[60], + "y":[16], + "type":"interactable" + } + }, + "islandfieldoffice": + { + "Exit": + { + "x":[4], + "y":[10], + "type":"door" + }, + "Counter": + { + "x":[8], + "y":[7], + "type":"interactable" + }, + "Island Survey": + { + "x":[5], + "y":[3], + "type":"interactable" + } + }, + "qinutroom": + { + "Exit": + { + "x":[7], + "y":[7], + "type":"door" + }, + "Perfection Tracker": + { + "x":[13], + "y":[4], + "type":"interactable" + }, + "Vending Machine": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Special Order Board": + { + "x":[3], + "y":[3], + "type":"interactable" + } + }, + "islandsoutheast": + { + "Island South East Cave Entrance": + { + "x":[30], + "y":[18], + "type":"door" + } + }, + "islandsoutheastcave": + { + "Exit": + { + "x":[1], + "y":[8], + "type":"door" + } + }, + "islandshrine": + { + "Exit": + { + "x":[13], + "y":[28], + "type":"door" + }, + "Shrine": + { + "x":[24], + "y":[22], + "type":"interactable" + }, + "North Pedestal": + { + "x":[24], + "y":[25], + "type":"interactable" + }, + "East Pedestal": + { + "x":[27], + "y":[27], + "type":"interactable" + }, + "West Pedestal": + { + "x":[21], + "y":[27], + "type":"interactable" + }, + "South Pedestal": + { + "x":[24], + "y":[28], + "type":"interactable" + } } } \ No newline at end of file From 33b42b09012c2eaed93976f9b0d384393c320492 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 30 Apr 2022 23:43:00 +0530 Subject: [PATCH 083/232] Added joja mart tiles --- stardew-access/static-tiles.json | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index fd8f2bf..83de85e 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1522,5 +1522,26 @@ "y":[28], "type":"interactable" } + }, + "jojamart": + { + "Exit": + { + "x":[13], + "y":[29], + "type":"door" + }, + "Morris's Kiosk": + { + "x":[21], + "y":[25], + "type":"interactable" + }, + "Counter": + { + "x":[10], + "y":[25], + "type":"interactable" + } } } \ No newline at end of file From 666dfd745b9e64ae400d48f552308b2cb13ec27c Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 30 Apr 2022 23:45:58 +0530 Subject: [PATCH 084/232] Added library museum tiles --- stardew-access/static-tiles.json | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 83de85e..b88892d 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1537,11 +1537,26 @@ "y":[25], "type":"interactable" }, - "Counter": + "Shop Counter": { "x":[10], "y":[25], "type":"interactable" } + }, + "archaeologyhouse": + { + "Exit": + { + "x":[3], + "y":[14], + "type":"door" + }, + "Counter": + { + "x":[3], + "y":[9], + "type":"interactable" + } } } \ No newline at end of file From c3c0169ac6c697b94d5db08ac664cb99e4211fdd Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 1 May 2022 13:20:18 +0530 Subject: [PATCH 085/232] Added method to scan entire location for tiles --- stardew-access/Features/Radar.cs | 32 +++++++- stardew-access/Features/TileInfo.cs | 121 +++++++++++++++++----------- 2 files changed, 103 insertions(+), 50 deletions(-) diff --git a/stardew-access/Features/Radar.cs b/stardew-access/Features/Radar.cs index 7cef653..129a2dd 100644 --- a/stardew-access/Features/Radar.cs +++ b/stardew-access/Features/Radar.cs @@ -133,6 +133,34 @@ namespace stardew_access.Features return detectedTiles; } + /// + /// Search the entire location. + /// + /// A dictionary with all the detected tiles along with the name of the object on it and it's category. + public Dictionary SearchLocation() + { + Dictionary detectedTiles = new Dictionary(); + Vector2 position = Vector2.Zero; + (bool, string? name, string category) tileInfo; + // FIXME try using BFS + for (int i = 0; i < Game1.currentLocation.Map.Layers[0].LayerSize.Width; i++) + { + for (int j = 0; j < Game1.currentLocation.Map.Layers[0].LayerSize.Height; j++) + { + position.X = i; + position.Y = j; + tileInfo = CheckTile(position, true); + if (tileInfo.Item1 && tileInfo.name != null) + { + MainClass.DebugLog($"\n{tileInfo.name}:\t{tileInfo.category}"); + detectedTiles.Add(position, (tileInfo.name, tileInfo.category)); + } + } + } + + return detectedTiles; + } + /// /// Checks if the provided tile position is within the range/radius and whether the tile has already been checked or not. /// @@ -154,9 +182,9 @@ namespace stardew_access.Features return true; } - public (bool, string?, string) CheckTile(Vector2 position) + public (bool, string? name, string category) CheckTile(Vector2 position, bool lessInfo = false) { - (string? name, CATEGORY? category) tileDetail = TileInfo.getNameWithCategoryAtTile(position); + (string? name, CATEGORY? category) tileDetail = TileInfo.getNameWithCategoryAtTile(position, lessInfo); if (tileDetail.name == null) return (false, null, CATEGORY.Others.ToString()); diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 97154c3..e222543 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -29,7 +29,7 @@ namespace stardew_access.Features } ///Returns the name of the object at tile alongwith it's category - public static (string? name, CATEGORY? category) getNameWithCategoryAtTile(Vector2 tile) + public static (string? name, CATEGORY? category) getNameWithCategoryAtTile(Vector2 tile, bool lessInfo = false) { int x = (int)tile.X; int y = (int)tile.Y; @@ -37,14 +37,15 @@ namespace stardew_access.Features CATEGORY? category = CATEGORY.Others; bool isColliding = isCollidingAtTile(x, y); - Dictionary> terrainFeature = Game1.currentLocation.terrainFeatures.FieldDict; + var terrainFeature = Game1.currentLocation.terrainFeatures.FieldDict; string? door = getDoorAtTile(x, y); - (CATEGORY? category, string? name) dynamicTile = getDynamicTilesInfo(x, y); + (CATEGORY? category, string? name) dynamicTile = getDynamicTilesInfo(x, y, lessInfo); string? junimoBundle = getJunimoBundleAt(x, y); - string? resourceClump = getResourceClumpAtTile(x, y); + string? resourceClump = getResourceClumpAtTile(x, y, lessInfo); string? farmAnimal = getFarmAnimalAt(Game1.currentLocation, x, y); string? parrot = getParrotPerchAtTile(x, y); (string? name, CATEGORY category) staticTile = MainClass.STiles.getStaticTileInfoAtWithCategory(x, y); + string? bush = getBushAtTile(x, y, lessInfo); if (Game1.currentLocation.isCharacterAtTile(tile) != null) { @@ -70,24 +71,24 @@ namespace stardew_access.Features toReturn = dynamicTile.name; category = dynamicTile.category; } - else if (Game1.currentLocation is VolcanoDungeon && ((VolcanoDungeon)Game1.currentLocation).IsCooledLava(x, y)) + else if (Game1.currentLocation is VolcanoDungeon && ((VolcanoDungeon)Game1.currentLocation).IsCooledLava(x, y) && !lessInfo) { toReturn = "Cooled lava"; category = CATEGORY.WaterTiles; } - else if (Game1.currentLocation is VolcanoDungeon && StardewValley.Monsters.LavaLurk.IsLavaTile((VolcanoDungeon)Game1.currentLocation, x, y)) + else if (Game1.currentLocation is VolcanoDungeon && StardewValley.Monsters.LavaLurk.IsLavaTile((VolcanoDungeon)Game1.currentLocation, x, y) && !lessInfo) { toReturn = "Lava"; category = CATEGORY.WaterTiles; } - else if (Game1.currentLocation.isWaterTile(x, y) && isColliding) + else if (Game1.currentLocation.isWaterTile(x, y) && isColliding && !lessInfo) { toReturn = "Water"; category = CATEGORY.WaterTiles; } else if (Game1.currentLocation.isObjectAtTile(x, y)) { - (string? name, CATEGORY? category) obj = getObjectAtTile(x, y); + (string? name, CATEGORY? category) obj = getObjectAtTile(x, y, lessInfo); toReturn = obj.name; category = obj.category; } @@ -102,9 +103,9 @@ namespace stardew_access.Features } } - else if (Game1.currentLocation.getLargeTerrainFeatureAt(x, y) != null) + else if (bush != null) { - toReturn = getBushAtTile(x, y); + toReturn = bush; category = CATEGORY.Bush; } else if (resourceClump != null) @@ -149,10 +150,15 @@ namespace stardew_access.Features return (toReturn, category); } - public static string? getBushAtTile(int x, int y) + public static string? getBushAtTile(int x, int y, bool lessInfo = false) { string? toReturn = null; - Bush bush = (Bush)Game1.currentLocation.getLargeTerrainFeatureAt(x, y); + Bush? bush = (Bush)Game1.currentLocation.getLargeTerrainFeatureAt(x, y); + if (bush == null) + return null; + if (lessInfo && (bush.tilePosition.Value.X != x || bush.tilePosition.Value.Y != y)) + return null; + int size = bush.size.Value; #region Check if bush is harvestable or not @@ -293,19 +299,23 @@ namespace stardew_access.Features /// /// category: This is the category of the tile. Default to Furnitures. ///
name: This is the name of the tile. Default to null if the tile tile has nothing on it.
- public static (CATEGORY? category, string? name) getDynamicTilesInfo(int x, int y) + public static (CATEGORY? category, string? name) getDynamicTilesInfo(int x, int y, bool lessInfo = false) { if (Game1.currentLocation is Farm farm) { Building building = farm.getBuildingAt(new Vector2(x, y)); if (building != null) { + string name = building.buildingType.Value; + if ((building.humanDoor.Value.X + building.tileX.Value) == x && (building.humanDoor.Value.Y + building.tileY.Value) == y) - return (CATEGORY.Doors, building.buildingType.Value + " Door"); + return (CATEGORY.Doors, name + " Door"); else if ((building.animalDoor.Value.X + building.tileX.Value) == x && (building.animalDoor.Value.Y + building.tileY.Value) == y) - return (CATEGORY.Doors, building.buildingType.Value + " Animal Door " + ((building.animalDoorOpen.Value) ? "Opened" : "Closed")); - else - return (CATEGORY.Buildings, building.buildingType.Value); + return (CATEGORY.Doors, name + " Animal Door " + ((building.animalDoorOpen.Value) ? "Opened" : "Closed")); + else if (building.tileX.Value == x && building.tileY.Value == y) + return (CATEGORY.Buildings, name); + else if (!lessInfo) + return (CATEGORY.Buildings, name); } } else if (Game1.currentLocation is Town) @@ -582,7 +592,7 @@ namespace stardew_access.Features } #region Objects - public static (string? name, CATEGORY category) getObjectAtTile(int x, int y) + public static (string? name, CATEGORY category) getObjectAtTile(int x, int y, bool lessInfo = false) { (string? name, CATEGORY category) toReturn = (null, CATEGORY.Others); @@ -604,7 +614,16 @@ namespace stardew_access.Features toReturn = (chest.DisplayName, CATEGORY.Chests); } else if (obj is Furniture) - toReturn.category = CATEGORY.Furnitures; + { + if (lessInfo && (((Furniture)obj).TileLocation.X != x || ((Furniture)obj).TileLocation.Y != y)) + { + toReturn.category = CATEGORY.Others; + toReturn.name = null; + } + else + toReturn.category = CATEGORY.Furnitures; + + } else if (obj.Type == "Crafting" && obj.bigCraftable.Value) { @@ -618,7 +637,7 @@ namespace stardew_access.Features } } - if (toReturn.category == CATEGORY.Others) // Fix for `Harvestable table` and `Busy nodes` + if (toReturn.category == CATEGORY.Machines) // Fix for `Harvestable table` and `Busy nodes` { MachineState machineState = GetMachineState(obj); if (machineState == MachineState.Ready) @@ -854,42 +873,45 @@ namespace stardew_access.Features return null; } - public static string? getResourceClumpAtTile(int x, int y) + public static string? getResourceClumpAtTile(int x, int y, bool lessInfo = false) { if (Game1.currentLocation is Woods) - return getStumpsInWoods(x, y); + return getStumpsInWoods(x, y, true); for (int i = 0; i < Game1.currentLocation.resourceClumps.Count; i++) { - if (Game1.currentLocation.resourceClumps[i].occupiesTile(x, y)) - { - int index = Game1.currentLocation.resourceClumps[i].parentSheetIndex.Value; + if (!Game1.currentLocation.resourceClumps[i].occupiesTile(x, y)) + continue; - switch (index) - { - case 600: - return "Large Stump"; - case 602: - return "Hollow Log"; - case 622: - return "Meteorite"; - case 752: - case 754: - case 756: - case 758: - return "Mine Rock"; - case 672: - return "Boulder"; - default: - return "Unknown"; - } + if (lessInfo && (Game1.currentLocation.resourceClumps[i].tile.X != x || Game1.currentLocation.resourceClumps[i].tile.Y != y)) + continue; + + int index = Game1.currentLocation.resourceClumps[i].parentSheetIndex.Value; + + switch (index) + { + case 600: + return "Large Stump"; + case 602: + return "Hollow Log"; + case 622: + return "Meteorite"; + case 752: + case 754: + case 756: + case 758: + return "Mine Rock"; + case 672: + return "Boulder"; + default: + return "Unknown"; } } return null; } - public static string? getStumpsInWoods(int x, int y) + public static string? getStumpsInWoods(int x, int y, bool lessInfo = false) { if (Game1.currentLocation is not Woods) return null; @@ -901,10 +923,13 @@ namespace stardew_access.Features Netcode.NetObjectList stumps = ((Woods)Game1.currentLocation).stumps; for (int i = 0; i < stumps.Count; i++) { - if (stumps[i].occupiesTile(x, y)) - { - return "Large Stump"; - } + if (!stumps[i].occupiesTile(x, y)) + continue; + + if (lessInfo && (stumps[i].tile.X != x || stumps[i].tile.Y != y)) + continue; + + return "Large Stump"; } return null; } From 6acfb4ff4eae060e4dfbc8533d0afb9d76fc3501 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 1 May 2022 14:17:11 +0530 Subject: [PATCH 086/232] Implemented BFS to SearchLocation() --- stardew-access/Features/Radar.cs | 41 ++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/stardew-access/Features/Radar.cs b/stardew-access/Features/Radar.cs index 129a2dd..f510e12 100644 --- a/stardew-access/Features/Radar.cs +++ b/stardew-access/Features/Radar.cs @@ -97,7 +97,6 @@ namespace stardew_access.Features List searched = new List(); int[] dirX = { -1, 0, 1, 0 }; int[] dirY = { 0, 1, 0, -1 }; - int count = 0; toSearch.Enqueue(center); searched.Add(center); @@ -116,7 +115,6 @@ namespace stardew_access.Features detectedTiles.Add(item, (tileInfo.Item2, tileInfo.Item3)); } } - count++; for (int i = 0; i < 4; i++) { @@ -134,7 +132,7 @@ namespace stardew_access.Features } /// - /// Search the entire location. + /// Search the entire location using Breadth First Search algorithm(BFS). /// /// A dictionary with all the detected tiles along with the name of the object on it and it's category. public Dictionary SearchLocation() @@ -142,18 +140,37 @@ namespace stardew_access.Features Dictionary detectedTiles = new Dictionary(); Vector2 position = Vector2.Zero; (bool, string? name, string category) tileInfo; - // FIXME try using BFS - for (int i = 0; i < Game1.currentLocation.Map.Layers[0].LayerSize.Width; i++) + + Queue toSearch = new Queue(); + List searched = new List(); + int[] dirX = { -1, 0, 1, 0 }; + int[] dirY = { 0, 1, 0, -1 }; + int count = 0; + + toSearch.Enqueue(Game1.player.getTileLocation()); + searched.Add(Game1.player.getTileLocation()); + MainClass.DebugLog(Game1.player.getTileLocation().ToString()); + + while (toSearch.Count > 0) { - for (int j = 0; j < Game1.currentLocation.Map.Layers[0].LayerSize.Height; j++) + Vector2 item = toSearch.Dequeue(); + tileInfo = CheckTile(item, true); + if (tileInfo.Item1 && tileInfo.name != null) { - position.X = i; - position.Y = j; - tileInfo = CheckTile(position, true); - if (tileInfo.Item1 && tileInfo.name != null) + // Add detected tile to the dictionary + detectedTiles.Add(item, (tileInfo.name, tileInfo.category)); + } + + count++; + + for (int i = 0; i < 4; i++) + { + Vector2 dir = new Vector2(item.X + dirX[i], item.Y + dirY[i]); + + if (!searched.Contains(dir) && Game1.currentLocation.isTileOnMap(dir)) { - MainClass.DebugLog($"\n{tileInfo.name}:\t{tileInfo.category}"); - detectedTiles.Add(position, (tileInfo.name, tileInfo.category)); + toSearch.Enqueue(dir); + searched.Add(dir); } } } From ce69a5ad98bfc6cd0fe4b9a80af94c16705464e2 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 1 May 2022 14:26:08 +0530 Subject: [PATCH 087/232] Added SearchLocation() to API --- stardew-access/API.cs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/stardew-access/API.cs b/stardew-access/API.cs index 0362154..1a2f9fa 100644 --- a/stardew-access/API.cs +++ b/stardew-access/API.cs @@ -16,13 +16,13 @@ namespace stardew_access.ScreenReader /// The starting point. /// The limiting factor or simply radius of the search area. /// A dictionary with all the detected tiles along with the name of the object on it and it's category. - public Dictionary SearchNearbyTiles(Vector2 center, int limit) + public Dictionary SearchNearbyTiles(Vector2 center, int limit) { /* * How to use the Dictionary to get the name and category of a tile:- * - * string? objectName = detectedTiles.GetValueOrDefault(center).Item1; - * string? objectCategory = detectedTiles.GetValueOrDefault(center).Item2; + * string tileName = detectedTiles.GetValueOrDefault(tilePosition).name; + * string tileCategory = detectedTiles.GetValueOrDefault(tilePosition).category; * * Here detectedTiles is the Dictionary returned by this method */ @@ -30,12 +30,30 @@ namespace stardew_access.ScreenReader return new Radar().SearchNearbyTiles(center, limit, false); } + /// + /// Search the entire location using Breadth First Search algorithm(BFS). + /// + /// A dictionary with all the detected tiles along with the name of the object on it and it's category. + public Dictionary SearchLocation() + { + /* + * How to use the Dictionary to get the name and category of a tile:- + * + * string tileName = detectedTiles.GetValueOrDefault(tilePosition).name; + * string tileCategory = detectedTiles.GetValueOrDefault(tilePosition).category; + * + * Here detectedTiles is the Dictionary returned by this method + */ + + return new Radar().SearchLocation(); + } + /// /// Check the tile for any object /// /// The tile where we want to check the name and category of object if any - /// Name of the object as the first item (Item1) and category as the second item (Item2). Returns null if no object found. - public (string?, string?) GetNameWithCategoryNameAtTile(Vector2 tile) + /// Name of the object as the first item (name) and category as the second item (category). Returns null if no object found. + public (string? name, string? category) GetNameWithCategoryNameAtTile(Vector2 tile) { return TileInfo.getNameWithCategoryNameAtTile(tile); } From 130204e39e9f2e2be997768ae9237ead1195555c Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 12:53:34 +0530 Subject: [PATCH 088/232] Added mail box to read tile --- stardew-access/Features/Radar.cs | 1 - stardew-access/Features/TileInfo.cs | 27 ++++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/stardew-access/Features/Radar.cs b/stardew-access/Features/Radar.cs index f510e12..86b5e46 100644 --- a/stardew-access/Features/Radar.cs +++ b/stardew-access/Features/Radar.cs @@ -149,7 +149,6 @@ namespace stardew_access.Features toSearch.Enqueue(Game1.player.getTileLocation()); searched.Add(Game1.player.getTileLocation()); - MainClass.DebugLog(Game1.player.getTileLocation().ToString()); while (toSearch.Count > 0) { diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index e222543..5cc73f6 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -303,19 +303,24 @@ namespace stardew_access.Features { if (Game1.currentLocation is Farm farm) { - Building building = farm.getBuildingAt(new Vector2(x, y)); - if (building != null) + if (farm.GetMainMailboxPosition().X == x && farm.GetMainMailboxPosition().Y == y) + return (CATEGORY.Interactables, "Mail box"); + else { - string name = building.buildingType.Value; + Building building = farm.getBuildingAt(new Vector2(x, y)); + if (building != null) + { + string name = building.buildingType.Value; - if ((building.humanDoor.Value.X + building.tileX.Value) == x && (building.humanDoor.Value.Y + building.tileY.Value) == y) - return (CATEGORY.Doors, name + " Door"); - else if ((building.animalDoor.Value.X + building.tileX.Value) == x && (building.animalDoor.Value.Y + building.tileY.Value) == y) - return (CATEGORY.Doors, name + " Animal Door " + ((building.animalDoorOpen.Value) ? "Opened" : "Closed")); - else if (building.tileX.Value == x && building.tileY.Value == y) - return (CATEGORY.Buildings, name); - else if (!lessInfo) - return (CATEGORY.Buildings, name); + if ((building.humanDoor.Value.X + building.tileX.Value) == x && (building.humanDoor.Value.Y + building.tileY.Value) == y) + return (CATEGORY.Doors, name + " Door"); + else if ((building.animalDoor.Value.X + building.tileX.Value) == x && (building.animalDoor.Value.Y + building.tileY.Value) == y) + return (CATEGORY.Doors, name + " Animal Door " + ((building.animalDoorOpen.Value) ? "Opened" : "Closed")); + else if (building.tileX.Value == x && building.tileY.Value == y) + return (CATEGORY.Buildings, name); + else if (!lessInfo) + return (CATEGORY.Buildings, name); + } } } else if (Game1.currentLocation is Town) From 5cdf1da37fa4bcfe663f86b860d640f337a92ad3 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 13:02:02 +0530 Subject: [PATCH 089/232] Added manor house tiles --- stardew-access/static-tiles.json | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index b88892d..b117bb5 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1558,5 +1558,50 @@ "y":[9], "type":"interactable" } + }, + "manorhouse": + { + "Exit": + { + "x":[4], + "y":[11], + "type":"door" + }, + "Town Ledger Book": + { + "x":[2], + "y":[5], + "type":"interactable" + }, + "Marriage Log Book": + { + "x":[3], + "y":[5], + "type":"interactable" + }, + "Lost and Found Box": + { + "x":[4], + "y":[5], + "type":"interactable" + }, + "Mayor's Room Door": + { + "x":[16], + "y":[9], + "type":"door" + }, + "Mayor's Oven": + { + "x":[7], + "y":[4], + "type":"decoration" + }, + "Mayor's Fridge": + { + "x":[9], + "y":[4], + "type":"decoration" + } } } \ No newline at end of file From 825c8b6a564da6ff2f6caeff07937169128347a1 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 13:06:49 +0530 Subject: [PATCH 090/232] Added mine tiles --- stardew-access/static-tiles.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index b117bb5..4beffd2 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1603,5 +1603,20 @@ "y":[4], "type":"decoration" } + }, + "mine": + { + "Exit": + { + "x":[18], + "y":[13], + "type":"door" + }, + "Minecart": + { + "x":[11,12], + "y":[10], + "type":"interactable" + } } } \ No newline at end of file From c2630ffb703029ad7ca5e3af9f4d0aaab09abd30 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 13:41:56 +0530 Subject: [PATCH 091/232] Added mountain, backwoods, railroad tiles --- stardew-access/static-tiles.json | 131 ++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 4beffd2..84ed3f5 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1606,7 +1606,7 @@ }, "mine": { - "Exit": + "Mountain Exit": { "x":[18], "y":[13], @@ -1617,6 +1617,135 @@ "x":[11,12], "y":[10], "type":"interactable" + }, + "Quarry Mine Ladder": + { + "x":[67], + "y":[9], + "type":"door" + }, + "Quarry Exit": + { + "x":[18], + "y":[13], + "type":"door" + } + }, + "mountain": + { + "Mine Entrance": + { + "x":[54], + "y":[5], + "type":"door" + }, + "Mine Bridge": + { + "x":[47], + "y":[7], + "type":"bridge" + }, + "Quarry Bridge": + { + "x":[90], + "y":[26], + "type":"bridge" + }, + "Minecart": + { + "x":[124,125], + "y":[11], + "type":"interactable" + }, + "Quarry Mine Entrance": + { + "x":[103], + "y":[17], + "type":"door" + }, + "Bridge 1": + { + "x":[57], + "y":[30], + "type":"bridge" + }, + "Bridge 2": + { + "x":[61], + "y":[21], + "type":"bridge" + }, + "Mountain Warp Statue": + { + "x":[31], + "y":[20], + "type":"decoration" + }, + "Linus Tent Entrance": + { + "x":[29], + "y":[7], + "type":"door" + }, + "Backwoods Entrance": + { + "x":[0], + "y":[13], + "type":"door" + }, + "Town Entrance": + { + "x":[15], + "y":[40], + "type":"door" + }, + "Railroad Entrance": + { + "x":[9], + "y":[0], + "type":"door" + } + }, + "undergroundmine77377": + { + "Grim Reaper Statue": + { + "x":[29,30], + "y":[6], + "type":"interactable" + } + }, + "Tent": + { + "Exit": + { + "x":[2], + "y":[5], + "type":"door" + } + }, + "railroad": + { + "Mountain Entrance": + { + "x":[29], + "y":[61], + "type":"door" + } + }, + "backwoods": + { + "Mountain Entrance": + { + "x":[49], + "y":[14], + "type":"door" + }, + "Farm Entrance": + { + "x":[14], + "y":[39], + "type":"door" } } } \ No newline at end of file From 83a26bb04af1cf0c07a82fbb1906c8e93d81f264 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 14:12:17 +0530 Subject: [PATCH 092/232] Added movie theater tiles --- stardew-access/static-tiles.json | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 84ed3f5..02cb286 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1747,5 +1747,38 @@ "y":[39], "type":"door" } + }, + "movietheater": + { + "Exit": + { + "x":[13], + "y":[15], + "type":"door" + }, + "Concessions Counter": + { + "x":[7], + "y":[6], + "type":"interactable" + }, + "Crane Game": + { + "x":[1,2], + "y":[8], + "type":"interactable" + }, + "Theater Door": + { + "x":[13], + "y":[3], + "type":"door" + }, + "Crane Man": + { + "x":[2], + "y":[9], + "type":"npc" + } } } \ No newline at end of file From 03e8eead73ac68545b60b767413dbef64aad182c Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 14:21:25 +0530 Subject: [PATCH 093/232] Added seed shop tiles --- stardew-access/static-tiles.json | 51 ++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 02cb286..94ebf5d 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1780,5 +1780,56 @@ "y":[9], "type":"npc" } + }, + "seedshop": + { + "Exit": + { + "x":[6], + "y":[29], + "type":"door" + }, + "Shop Counter": + { + "x":[4], + "y":[18], + "type":"interactable" + }, + "Backpack Upgrade": + { + "x":[7], + "y":[18], + "type":"interactable" + }, + "Shrine of Yoba": + { + "x":[37], + "y":[17], + "type":"decoration" + }, + "Fridge": + { + "x":[39], + "y":[4], + "type":"decoration" + }, + "Abigail's Room Door": + { + "x":[13], + "y":[11], + "type":"door" + }, + "Pierre and Caroline's Room Door": + { + "x":[20], + "y":[11], + "type":"door" + }, + "Living Room Door": + { + "x":[14], + "y":[16], + "type":"door" + } } } \ No newline at end of file From c00ae77d03a958a969f475521e5f64a9a6aaa7f1 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 14:28:13 +0530 Subject: [PATCH 094/232] Added sewer tiles --- stardew-access/static-tiles.json | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 94ebf5d..485947c 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1831,5 +1831,26 @@ "y":[16], "type":"door" } + }, + "sewer": + { + "Exit Ladder": + { + "x":[16], + "y":[10], + "type":"door" + }, + "Statue Of Uncertainty": + { + "x":[8], + "y":[20], + "type":"interactable" + }, + "Mutant Bug Lair": + { + "x":[3], + "y":[19], + "type":"door" + } } } \ No newline at end of file From a6f8e5fe35208ade040bd6101552a596904b7825 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 14:36:17 +0530 Subject: [PATCH 095/232] Added wizard house tiles --- stardew-access/static-tiles.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 485947c..4eeb776 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1852,5 +1852,35 @@ "y":[19], "type":"door" } + }, + "wizardhouse": + { + "Exit": + { + "x":[8], + "y":[24], + "type":"door" + }, + "Basement Door": + { + "x":[4], + "y":[4], + "type":"door" + } + }, + "wizardhousebasement": + { + "Exit Ladder": + { + "x":[4], + "y":[3], + "type":"door" + }, + "Shrine of Illusions": + { + "x":[12], + "y":[4], + "type":"door" + } } } \ No newline at end of file From 4735bde498981c95f51b709528c370cf2fd41bf9 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 22:23:06 +0530 Subject: [PATCH 096/232] Added secret woods tiles --- stardew-access/Features/TileInfo.cs | 6 +----- stardew-access/static-tiles.json | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 5cc73f6..27fc00f 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -881,7 +881,7 @@ namespace stardew_access.Features public static string? getResourceClumpAtTile(int x, int y, bool lessInfo = false) { if (Game1.currentLocation is Woods) - return getStumpsInWoods(x, y, true); + return getStumpsInWoods(x, y, lessInfo); for (int i = 0; i < Game1.currentLocation.resourceClumps.Count; i++) { @@ -921,10 +921,6 @@ namespace stardew_access.Features if (Game1.currentLocation is not Woods) return null; - if ((x == 8 || x == 9) && y == 7) - { - return "Old Master Cannoli"; - } Netcode.NetObjectList stumps = ((Woods)Game1.currentLocation).stumps; for (int i = 0; i < stumps.Count; i++) { diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 4eeb776..1cca150 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1880,7 +1880,22 @@ { "x":[12], "y":[4], + "type":"interactable" + } + }, + "woods": + { + "Forest Entrance": + { + "x":[59], + "y":[17], "type":"door" + }, + "Old Master Cannoli": + { + "x":[8,9], + "y":[7], + "type":"interactable" } } } \ No newline at end of file From 03c3ba498110100c655adf757199261be401330d Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 22:46:08 +0530 Subject: [PATCH 097/232] Added tiles for hospital & blacksmith --- stardew-access/static-tiles.json | 123 +++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 1cca150..4234c6a 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1897,5 +1897,128 @@ "y":[7], "type":"interactable" } + }, + "harveyroom": + { + "Exit": + { + "x":[6], + "y":[12], + "type":"door" + }, + "Fridge": + { + "x":[21], + "y":[6], + "type":"decoration" + }, + "Oven": + { + "x":[19], + "y":[6], + "type":"decoration" + }, + "Airplane Collection": + { + "x":[6,7], + "y":[3], + "type":"decoration" + }, + "Radio Broadcasting Set": + { + "x":[4,5], + "y":[4], + "type":"decoration" + }, + "Cassette Deck": + { + "x":[8], + "y":[4], + "type":"decoration" + } + }, + "hospital": + { + "Exit": + { + "x":[10], + "y":[19], + "type":"door" + }, + "Harvey's Room Entrance": + { + "x":[10], + "y":[2], + "type":"door" + }, + "Harvey's Room Entrance Door": + { + "x":[10], + "y":[5], + "type":"door" + }, + "Main Area Door": + { + "x":[10], + "y":[13], + "type":"door" + }, + "Counter": + { + "x":[6], + "y":[16], + "type":"interactable" + } + }, + "blacksmith": + { + "Exit": + { + "x":[5], + "y":[19], + "type":"door" + }, + "Counter": + { + "x":[3], + "y":[14], + "type":"interactable" + }, + "Clint's Room Door": + { + "x":[4], + "y":[9], + "type":"door" + }, + "Clint's Furnace": + { + "x":[9,10], + "y":[12], + "type":"decoration" + }, + "Blueprints": + { + "x":[13], + "y":[15,16], + "type":"decoration" + }, + "Anvil": + { + "x":[12,13], + "y":[13], + "type":"decoration" + }, + "Cassette Deck": + { + "x":[2], + "y":[4], + "type":"decoration" + }, + "Almirah": + { + "x":[5,6], + "y":[4], + "type":"decoration" + } } } \ No newline at end of file From 56c17bf96d187b2619c035b073a27ecc914d427b Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 23:02:22 +0530 Subject: [PATCH 098/232] Added animal shop tiles --- stardew-access/static-tiles.json | 125 ++++++++++++++++++++++++++++++- 1 file changed, 124 insertions(+), 1 deletion(-) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 4234c6a..806db3d 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -2014,11 +2014,134 @@ "y":[4], "type":"decoration" }, - "Almirah": + "Clint's Drawer": { "x":[5,6], "y":[4], "type":"decoration" } + }, + "animalshop": + { + "Exit": + { + "x":[13], + "y":[19], + "type":"door" + }, + "Counter": + { + "x":[12], + "y":[15], + "type":"interactable" + }, + "Marnie's Room Door": + { + "x":[15], + "y":[12], + "type":"door" + }, + "Jas's Room Door": + { + "x":[6], + "y":[13], + "type":"door" + }, + "Shane's Room Door": + { + "x":[21], + "y":[13], + "type":"door" + }, + "Marnie's Barn Door": + { + "x":[30], + "y":[13], + "type":"door" + }, + "Fridge": + { + "x":[28], + "y":[14], + "type":"decoration" + }, + "Oven": + { + "x":[24], + "y":[14], + "type":"decoration" + }, + "Mega Station": + { + "x":[22], + "y":[5], + "type":"decoration" + }, + "Shane's Radio": + { + "x":[24], + "y":[4], + "type":"decoration" + }, + "Marnie's Dresser": + { + "x":[16], + "y":[4], + "type":"decoration" + }, + "Marnie's Drawer": + { + "x":[17], + "y":[4], + "type":"decoration" + }, + "Jack in the Box": + { + "x":[8], + "y":[5], + "type":"decoration" + }, + "Futan Bear": + { + "x":[2,3], + "y":[4], + "type":"decoration" + }, + "Colouring Book": + { + "x":[5], + "y":[4], + "type":"decoration" + }, + "Paint Set": + { + "x":[5], + "y":[7], + "type":"decoration" + }, + "Jas's Alarm Clock": + { + "x":[8], + "y":[8], + "type":"decoration" + }, + "Jas's Radio": + { + "x":[4], + "y":[9], + "type":"decoration" + }, + "Arts And Craft": + { + "x":[7], + "y":[6], + "type":"decoration" + }, + "Doll House": + { + "x":[6,7], + "y":[4], + "type":"decoration" + } } } \ No newline at end of file From 110989ebcccc23a6a2532447b4b503a07ac456fa Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 23:10:50 +0530 Subject: [PATCH 099/232] Added saloon tiles --- stardew-access/static-tiles.json | 65 +++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 806db3d..052ac89 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1825,7 +1825,7 @@ "y":[11], "type":"door" }, - "Living Room Door": + "Living Area Door": { "x":[14], "y":[16], @@ -2143,5 +2143,68 @@ "y":[4], "type":"decoration" } + }, + "saloon": + { + "Exit": + { + "x":[14], + "y":[24], + "type":"door" + }, + "Counter": + { + "x":[14], + "y":[19], + "type":"interactable" + }, + "Journey of the Prairie King Arcade": + { + "x":[33], + "y":[17], + "type":"interactable" + }, + "Junimo Kart Arcade": + { + "x":[35], + "y":[17], + "type":"interactable" + }, + "Joja Vending Machine": + { + "x":[37,38], + "y":[17], + "type":"interactable" + }, + "Jukebox": + { + "x":[1,2], + "y":[17], + "type":"interactable" + }, + "Gus's Room Door": + { + "x":[20], + "y":[9], + "type":"door" + }, + "Dining Room Door": + { + "x":[11], + "y":[9], + "type":"door" + }, + "Living Area Door": + { + "x":[4], + "y":[16], + "type":"door" + }, + "Gus's Radio": + { + "x":[16], + "y":[6], + "type":"decoration" + } } } \ No newline at end of file From b287021db7c513988de675d12a3ae012938d95a1 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 23:26:41 +0530 Subject: [PATCH 100/232] Added science house tiles --- stardew-access/static-tiles.json | 132 +++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 052ac89..51dd4e5 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -1704,6 +1704,12 @@ "x":[9], "y":[0], "type":"door" + }, + "Science House Secondary Door": + { + "x":[8], + "y":[20], + "type":"door" } }, "undergroundmine77377": @@ -2206,5 +2212,131 @@ "y":[6], "type":"decoration" } + }, + "sciencehouse": + { + "Exit": + { + "x":[6], + "y":[24], + "type":"door" + }, + "Secondary Exit": + { + "x":[3], + "y":[8], + "type":"door" + }, + "Counter": + { + "x":[8], + "y":[19], + "type":"interactable" + }, + "Sebastian's Room Entrance": + { + "x":[12], + "y":[21], + "type":"door" + }, + "Beaker Set": + { + "x":[17], + "y":[17], + "type":"decoration" + }, + "Microscope": + { + "x":[19], + "y":[17], + "type":"decoration" + }, + "Stereo Microscope": + { + "x":[23], + "y":[20], + "type":"decoration" + }, + "Robin and Demetrius's Room Entrance": + { + "x":[13], + "y":[10], + "type":"door" + }, + "Maru's Room Entrance": + { + "x":[7], + "y":[10], + "type":"door" + }, + "Bookshelf": + { + "x":[16,17], + "y":[4], + "type":"decoration" + }, + "Maru's Device": + { + "x":[6], + "y":[6], + "type":"decoration" + }, + "Poster": + { + "x":[6], + "y":[3], + "type":"decoration" + }, + "Computer": + { + "x":[9], + "y":[4], + "type":"decoration" + }, + "Fridge": + { + "x":[27], + "y":[8], + "type":"decoration" + }, + "Oven": + { + "x":[30], + "y":[10], + "type":"decoration" + } + }, + "sebastianroom": + { + "Exit": + { + "x":[1], + "y":[1], + "type":"door" + }, + "Room Door": + { + "x":[1], + "y":[3], + "type":"door" + }, + "Sebastian's Radio": + { + "x":[3], + "y":[4], + "type":"decoration" + }, + "Graphic Novel": + { + "x":[10], + "y":[6], + "type":"decoration" + }, + "Computer": + { + "x":[7], + "y":[4], + "type":"decoration" + } } } \ No newline at end of file From 1aa569ab84dc9ae4bf4d10b5036ff28d9aa2b056 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 23:31:17 +0530 Subject: [PATCH 101/232] Added sam house tiles --- stardew-access/static-tiles.json | 51 ++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 51dd4e5..3c36ba6 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -2338,5 +2338,56 @@ "y":[4], "type":"decoration" } + }, + "samhouse": + { + "Exit": + { + "x":[4], + "y":[23], + "type":"door" + }, + "Radio": + { + "x":[6], + "y":[12], + "type":"decoration" + }, + "Vincent's Room Door": + { + "x":[16], + "y":[18], + "type":"door" + }, + "Sam's Room Door": + { + "x":[12], + "y":[14], + "type":"door" + }, + "Jodi's Room Door": + { + "x":[17], + "y":[6], + "type":"door" + }, + "Sam's Drawer": + { + "x":[7,8], + "y":[12], + "type":"decoration" + }, + "Bookshelf": + { + "x":[18,19], + "y":[12], + "type":"decoration" + }, + "Fridge": + { + "x":[7], + "y":[4], + "type":"decoration" + } } } \ No newline at end of file From 297eca3adcc250563f1ec69f831142c7f7eba0ff Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 2 May 2022 23:43:43 +0530 Subject: [PATCH 102/232] Added haley house tiles --- stardew-access/static-tiles.json | 81 ++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 3c36ba6..93c4714 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -2389,5 +2389,86 @@ "y":[4], "type":"decoration" } + }, + "haleyhouse": + { + "Exit": + { + "x":[2], + "y":[24], + "type":"door" + }, + "Sewing Machine": + { + "x":[12,13], + "y":[23], + "type":"interactable" + }, + "Dye Pots": + { + "x":[17], + "y":[25], + "type":"interactable" + }, + "Emily's Room Door": + { + "x":[16], + "y":[12], + "type":"door" + }, + "Emily's Computer": + { + "x":[22], + "y":[6], + "type":"decoration" + }, + "Emily's Pet Parrot": + { + "x":[14], + "y":[4], + "type":"decoration" + }, + "Magazine": + { + "x":[4], + "y":[22], + "type":"decoration" + }, + "Globe": + { + "x":[8], + "y":[15], + "type":"decoration" + }, + "Fridge": + { + "x":[21], + "y":[15], + "type":"decoration" + }, + "Haley's Room Door": + { + "x":[5], + "y":[13], + "type":"door" + }, + "Futan Bear": + { + "x":[8], + "y":[4], + "type":"decoration" + }, + "Diary": + { + "x":[9], + "y":[5], + "type":"decoration" + }, + "Haley's Camera": + { + "x":[1], + "y":[9], + "type":"decoration" + } } } \ No newline at end of file From 14506b1e11a0d4a86dfa012eca217f1e4a0a4751 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Wed, 4 May 2022 23:29:15 +0530 Subject: [PATCH 103/232] Added Josh House tiles --- stardew-access/ModEntry.cs | 1 - stardew-access/stardew-access.csproj | 1 + stardew-access/static-tiles.json | 81 ++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 2fb2634..59dbb2f 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -7,7 +7,6 @@ using stardew_access.Patches; using stardew_access.ScreenReader; using Microsoft.Xna.Framework; using StardewValley.Menus; -using Newtonsoft.Json.Linq; namespace stardew_access { diff --git a/stardew-access/stardew-access.csproj b/stardew-access/stardew-access.csproj index b3ab566..c7ca1ff 100644 --- a/stardew-access/stardew-access.csproj +++ b/stardew-access/stardew-access.csproj @@ -1,6 +1,7 @@  + /home/shoaib/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/Stardew Valley/ net5.0 stardew_access enable diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 93c4714..0db0336 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -2470,5 +2470,86 @@ "y":[9], "type":"decoration" } + }, + "joshhouse": + { + "Exit": + { + "x":[9], + "y":[24], + "type":"door" + }, + "TV": + { + "x":[15,16], + "y":[20], + "type":"decoration" + }, + "Bookshelf": + { + "x":[17,18], + "y":[16], + "type":"decoration" + }, + "Fridge": + { + "x":[5], + "y":[16], + "type":"decoration" + }, + "Evelyn and George's Room Door": + { + "x":[5], + "y":[9], + "type":"door" + }, + "Alex's Room Door": + { + "x":[10], + "y":[10], + "type":"door" + }, + "Radio": + { + "x":[3], + "y":[4], + "type":"door" + }, + "Magazine": + { + "x":[11], + "y":[4], + "type":"door" + }, + "Alex's Bookshelf": + { + "x":[12,13], + "y":[4], + "type":"decoration" + }, + "Alex's Drawer": + { + "x":[17,18], + "y":[4], + "type":"decoration" + }, + "Dumbbell": + { + "x":[14], + "y":[4], + "type":"door" + }, + "Gridball": + { + "x":[23], + "y":[45], + "type":"door" + }, + "Gridball Helmet": + { + "x":[23], + "y":[6], + "type":"door" + } } } \ No newline at end of file From 4cc4bba62e1e84c818fe5aa7ffc7d12d6c49b7c6 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Wed, 4 May 2022 23:33:05 +0530 Subject: [PATCH 104/232] Added trailer tiles --- stardew-access/static-tiles.json | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/stardew-access/static-tiles.json b/stardew-access/static-tiles.json index 0db0336..b204e8b 100644 --- a/stardew-access/static-tiles.json +++ b/stardew-access/static-tiles.json @@ -2551,5 +2551,38 @@ "y":[6], "type":"door" } + }, + "trailer": + { + "Exit": + { + "x":[12], + "y":[9], + "type":"door" + }, + "Penny's Room Door": + { + "x":[6], + "y":[7], + "type":"door" + }, + "Bookshelf": + { + "x":[5,6], + "y":[4], + "type":"decoration" + }, + "Book": + { + "x":[2], + "y":[4], + "type":"decoration" + }, + "Magazine": + { + "x":[1], + "y":[9], + "type":"decoration" + } } } \ No newline at end of file From 8355f056840598c97e9ff7118c17ce37780f4c2a Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 5 May 2022 00:01:56 +0530 Subject: [PATCH 105/232] Added event tiles --- stardew-access/Features/TileInfo.cs | 53 +++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 27fc00f..fd0862f 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -323,6 +323,56 @@ namespace stardew_access.Features } } } + else if (Game1.currentLocation.currentEvent != null) + { + string event_name = Game1.currentLocation.currentEvent.FestivalName; + if (event_name == "Egg Festival" && x == 21 && y == 55) + { + return (CATEGORY.Interactables, "Egg Festival Shop"); + } + else if (event_name == "Flower Dance" && x == 28 && y == 37) + { + return (CATEGORY.Interactables, "Flower Dance Shop"); + } + else if (event_name == "Luau" && x == 35 && y == 13) + { + return (CATEGORY.Interactables, "Soup Pot"); + } + else if (event_name == "Spirit's Eve" && x == 25 && y == 49) + { + return (CATEGORY.Interactables, "Spirit's Eve Shop"); + } + else if (event_name == "Stardew Valley Fair") + { + if (x == 16 && y == 52) + return (CATEGORY.Interactables, "Stardew Valley Fair Shop"); + else if (x == 23 && y == 62) + return (CATEGORY.Interactables, "Slingshot Game"); + else if (x == 34 && y == 65) + return (CATEGORY.Interactables, "Purchase Star Tokens"); + else if (x == 33 && y == 70) + return (CATEGORY.Interactables, "The Wheel"); + else if (x == 23 && y == 70) + return (CATEGORY.Interactables, "Fishing Challenge"); + else if (x == 47 && y == 87) + return (CATEGORY.Interactables, "Fortune Teller"); + else if (x == 38 && y == 59) + return (CATEGORY.Interactables, "Grange Display"); + else if (x == 30 && y == 56) + return (CATEGORY.Interactables, "Strength Game"); + else if (x == 26 && y == 33) + return (CATEGORY.Interactables, "Free Burgers"); + } + else if (event_name == "Festival of Ice" && x == 55 && y == 31) + { + return (CATEGORY.Interactables, "Travelling Cart"); + } + else if (event_name == "Feast of the Winter Star" && x == 18 && y == 61) + { + return (CATEGORY.Interactables, "Feast of the Winter Star Shop"); + } + + } else if (Game1.currentLocation is Town) { if (SpecialOrder.IsSpecialOrdersBoardUnlocked() && x == 62 && y == 93) @@ -420,6 +470,9 @@ namespace stardew_access.Features if (isHarvestable) toReturn = "Harvestable " + toReturn; + + if (dirt.crop.dead.Value) + toReturn = "Dead " + toReturn; } else if (dirt.crop != null && dirt.crop.forageCrop.Value) { From f9fbbc418146b6ae58ad2c3e403c5837419f1c9d Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 5 May 2022 00:08:17 +0530 Subject: [PATCH 106/232] Moved static-tiles.json to assets folder --- stardew-access/Features/StaticTiles.cs | 2 +- stardew-access/{ => assets}/static-tiles.json | 0 stardew-access/manifest.json | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename stardew-access/{ => assets}/static-tiles.json (100%) diff --git a/stardew-access/Features/StaticTiles.cs b/stardew-access/Features/StaticTiles.cs index cd2d654..2d0a42f 100644 --- a/stardew-access/Features/StaticTiles.cs +++ b/stardew-access/Features/StaticTiles.cs @@ -12,7 +12,7 @@ namespace stardew_access.Features if (MainClass.ModHelper == null) return; - using (StreamReader file = new StreamReader(Path.Combine(MainClass.ModHelper.DirectoryPath, "static-tiles.json"))) + using (StreamReader file = new StreamReader(Path.Combine(MainClass.ModHelper.DirectoryPath, "assets", "static-tiles.json"))) { string json = file.ReadToEnd(); data = JObject.Parse(json); diff --git a/stardew-access/static-tiles.json b/stardew-access/assets/static-tiles.json similarity index 100% rename from stardew-access/static-tiles.json rename to stardew-access/assets/static-tiles.json diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index 4f0403b..91acaaf 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.1.8", + "Version": "1.2.0", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From bd29fae9ca2f1d3b612d561cf161a3745ff44dcc Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Fri, 6 May 2022 19:47:11 -0400 Subject: [PATCH 107/232] Initial refactor of mouse handling. --- stardew-access/Features/MouseHandler.cs | 50 +++++++++++++++++++++++++ stardew-access/Features/Other.cs | 26 ------------- stardew-access/ModEntry.cs | 14 ++++++- 3 files changed, 63 insertions(+), 27 deletions(-) create mode 100644 stardew-access/Features/MouseHandler.cs diff --git a/stardew-access/Features/MouseHandler.cs b/stardew-access/Features/MouseHandler.cs new file mode 100644 index 0000000..88b09c8 --- /dev/null +++ b/stardew-access/Features/MouseHandler.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using StardewValley; + +namespace stardew_access.Features +{ + public class MouseHandler + { + + public Vector2 ViewingOffset { get; set; } = Vector2.Zero; + + public Vector2 PlayerFacingVector + { + get + { +switch (Game1.player.FacingDirection) + { + case 0: + return new Vector2(0, - Game1.tileSize); + case 1: + return new Vector2(Game1.tileSize, 0); + case 2: + return new Vector2(0, Game1.tileSize); + case 3: + return new Vector2(-Game1.tileSize, 0); + default: + return Vector2.Zero; + } + } + } + + public Vector2 PlayerPosition + { + get + { + int x = Game1.player.GetBoundingBox().Center.X - Game1.viewport.X; + int y = Game1.player.GetBoundingBox().Center.Y - Game1.viewport.Y; + return new Vector2(x, y); + } + } + + public void SnapMouseToPlayer() + { + Vector2 snapPosition = this.PlayerPosition + this.PlayerFacingVector + this.ViewingOffset; + if (Utility.isOnScreen(snapPosition, 0)) + Game1.setMousePosition((int)snapPosition.X, (int)snapPosition.Y); + } + } +} diff --git a/stardew-access/Features/Other.cs b/stardew-access/Features/Other.cs index a21698f..826c01d 100644 --- a/stardew-access/Features/Other.cs +++ b/stardew-access/Features/Other.cs @@ -41,32 +41,6 @@ namespace stardew_access.Features MainClass.ScreenReader.Say($"{currentLocation.Name} Entered", true); } - public static void SnapMouseToPlayer() - { - int x = Game1.player.GetBoundingBox().Center.X - Game1.viewport.X; - int y = Game1.player.GetBoundingBox().Center.Y - Game1.viewport.Y; - - int offset = 64; - - switch (Game1.player.FacingDirection) - { - case 0: - y -= offset; - break; - case 1: - x += offset; - break; - case 2: - y += offset; - break; - case 3: - x -= offset; - break; - } - - Game1.setMousePosition(x, y); - } - public static void narrateHudMessages() { try diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 59dbb2f..7718b08 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -20,6 +20,7 @@ namespace stardew_access private static StaticTiles? sTiles; private static IScreenReader? screenReader; private static IModHelper? modHelper; + private static MouseHandler? mouse; internal static ModConfig Config { get => config; set => config = value; } public static IModHelper? ModHelper { get => modHelper; } @@ -62,6 +63,17 @@ namespace stardew_access set => screenReader = value; } + + public static MouseHandler Mouse + { +get + { + if (mouse == null) + mouse = new MouseHandler(); + return mouse; + } + } + #endregion /********* @@ -131,7 +143,7 @@ namespace stardew_access Other.narrateCurrentLocation(); if (Config.SnapMouse) - Other.SnapMouseToPlayer(); + Mouse.SnapMouseToPlayer(); if (!ReadTile.isReadingTile && Config.ReadTile) { From 8dd96d84b3c634acac4b453a87eed86958ef5883 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Sun, 8 May 2022 01:47:32 -0400 Subject: [PATCH 108/232] Refactoring mouse handling into a separate class. --- stardew-access/Features/MouseHandler.cs | 19 +++++++++++++------ stardew-access/ModEntry.cs | 3 +-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/stardew-access/Features/MouseHandler.cs b/stardew-access/Features/MouseHandler.cs index 88b09c8..f0bccd7 100644 --- a/stardew-access/Features/MouseHandler.cs +++ b/stardew-access/Features/MouseHandler.cs @@ -8,9 +8,9 @@ namespace stardew_access.Features public class MouseHandler { - public Vector2 ViewingOffset { get; set; } = Vector2.Zero; + private Vector2 ViewingOffset = Vector2.Zero; - public Vector2 PlayerFacingVector + private Vector2 PlayerFacingVector { get { @@ -30,7 +30,7 @@ switch (Game1.player.FacingDirection) } } - public Vector2 PlayerPosition + private Vector2 PlayerPosition { get { @@ -40,11 +40,18 @@ switch (Game1.player.FacingDirection) } } - public void SnapMouseToPlayer() + private void SnapMouseToPlayer() { Vector2 snapPosition = this.PlayerPosition + this.PlayerFacingVector + this.ViewingOffset; - if (Utility.isOnScreen(snapPosition, 0)) - Game1.setMousePosition((int)snapPosition.X, (int)snapPosition.Y); + Point snapPoint = new Point((int)snapPosition.X, (int)snapPosition.Y); + if (Utility.isOnScreen(snapPoint, 0)) + Game1.setMousePosition(snapPoint.X, snapPoint.Y); + } + +public void update() + { + if (MainClass.Config.SnapMouse) + this.SnapMouseToPlayer(); } } } diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 7718b08..28c19fe 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -142,8 +142,7 @@ get // Narrate current location's name Other.narrateCurrentLocation(); - if (Config.SnapMouse) - Mouse.SnapMouseToPlayer(); + Mouse.update(); if (!ReadTile.isReadingTile && Config.ReadTile) { From 797e0ab13658ccc3f1fbeb877c2c5262e9074d43 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Sun, 8 May 2022 01:55:38 -0400 Subject: [PATCH 109/232] Add cursor keys to mod config. --- stardew-access/ModConfig.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index 98c19df..388d7fd 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -26,6 +26,16 @@ namespace stardew_access public KeybindList ReadTileKey { get; set; } = KeybindList.Parse("J"); public KeybindList ReadStandingTileKey { get; set; } = KeybindList.Parse("LeftAlt + J"); + //Tile viewer keys + public KeybindList TileCursorUpKey { get; set; } = KeybindList.Parse("Up"); + public KeybindList TileCursorRightKey { get; set; } = KeybindList.Parse("Right"); + public KeybindList TileCursorDownKey { get; set; } = KeybindList.Parse("Down"); + public KeybindList TileCursorLeftKey { get; set; } = KeybindList.Parse("Left"); + public KeybindList TileCursorPreciseUpKey { get; set; } = KeybindList.Parse("LeftShift + Up"); + public KeybindList TileCursorPreciseRightKey { get; set; } = KeybindList.Parse("LeftShift + Right"); + public KeybindList TileCursorPreciseDownKey { get; set; } = KeybindList.Parse("LeftShift + Down"); + public KeybindList TileCursorPreciseLeftKey { get; set; } = KeybindList.Parse("LeftShift + Left"); + #endregion // TODO Add the exclusion and focus list too From 0b318b29ad1647d32ffd0f156a7b193eee621142 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Sun, 8 May 2022 18:46:37 -0400 Subject: [PATCH 110/232] MouseHandler refactoring. --- stardew-access/Features/MouseHandler.cs | 27 +++++++++++++++++++++++++ stardew-access/Features/Radar.cs | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/stardew-access/Features/MouseHandler.cs b/stardew-access/Features/MouseHandler.cs index f0bccd7..fb3f618 100644 --- a/stardew-access/Features/MouseHandler.cs +++ b/stardew-access/Features/MouseHandler.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using Microsoft.Xna.Framework; +using xTile; using StardewValley; + namespace stardew_access.Features { public class MouseHandler @@ -40,6 +42,23 @@ switch (Game1.player.FacingDirection) } } + private static (int, int) GetMapTileDimensions() + { + Map map = Game1.currentLocation.map; + return (map.Layers[0].LayerWidth, map.Layers[0].LayerHeight); + } + + public bool MoveTileView(Vector2 delta) + { + Vector2 dest = this.PlayerPosition + this.PlayerFacingVector + this.ViewingOffset + delta; + if (Utility.isOnScreen(dest, 0)) + { + this.ViewingOffset += delta; + return true; + } + return false; + } + private void SnapMouseToPlayer() { Vector2 snapPosition = this.PlayerPosition + this.PlayerFacingVector + this.ViewingOffset; @@ -53,5 +72,13 @@ public void update() if (MainClass.Config.SnapMouse) this.SnapMouseToPlayer(); } + + private static bool IsTileOnMap(Vector2 tile) + { + (int width, int height) dimensions = GetMapTileDimensions(); + if (tile.X < 0 || tile.X >= dimensions.width) return false; + if (tile.Y < 0 || tile.Y >= dimensions.height) return false; + return true; } } +} diff --git a/stardew-access/Features/Radar.cs b/stardew-access/Features/Radar.cs index 86b5e46..5253de9 100644 --- a/stardew-access/Features/Radar.cs +++ b/stardew-access/Features/Radar.cs @@ -1,4 +1,4 @@ -using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework; using StardewValley; using StardewValley.Objects; From 43c3dbb0d857a73be801e8932d4038dbe757215b Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Sun, 8 May 2022 19:22:28 -0400 Subject: [PATCH 111/232] Added diggable golden walnut locations to ginger Island tileInfo. --- stardew-access/Features/TileInfo.cs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index fd0862f..8e075b9 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -430,17 +430,24 @@ namespace stardew_access.Features else if (x == 8 && y == 9) return (((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatAnchor")) ? CATEGORY.Interactables : CATEGORY.Decor), ((!Game1.MasterPlayer.hasOrWillReceiveMail("willyBoatAnchor")) ? "Repair " : "") + "Boat Anchor"); } - else if (Game1.currentLocation is IslandWest islandWest) + else if (Game1.currentLocation is IslandLocation islandLocation) { - if (islandWest.shippingBinPosition.X == x && islandWest.shippingBinPosition.Y == y) - return (CATEGORY.Interactables, "Shipping Bin"); + var nutTracker = Game1.player.team.collectedNutTracker; + if (islandLocation.IsBuriedNutLocation(new Point(x, y)) && !nutTracker.ContainsKey("Buried_" + islandLocation.Name + "_" + x + "_" + y)) + { + return (CATEGORY.Interactables, "Diggable spot"); + } + else if (Game1.currentLocation is IslandWest islandWest) + { + if (islandWest.shippingBinPosition.X == x && islandWest.shippingBinPosition.Y == y) + return (CATEGORY.Interactables, "Shipping Bin"); + } + else if (Game1.currentLocation is IslandNorth islandNorth) + { + if (islandNorth.traderActivated.Value && x == 36 && y == 71) + return (CATEGORY.Interactables, "Island Trader"); + } } - else if (Game1.currentLocation is IslandNorth islandNorth) - { - if (islandNorth.traderActivated.Value && x == 36 && y == 71) - return (CATEGORY.Interactables, "Island Trader"); - } - return (null, null); } From 72f1835929880ea955693eb6bf0d1af1a6fda528 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Sun, 8 May 2022 19:31:04 -0400 Subject: [PATCH 112/232] Brief refactor for readability. --- stardew-access/Features/TileInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 8e075b9..bf1c848 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -433,7 +433,7 @@ namespace stardew_access.Features else if (Game1.currentLocation is IslandLocation islandLocation) { var nutTracker = Game1.player.team.collectedNutTracker; - if (islandLocation.IsBuriedNutLocation(new Point(x, y)) && !nutTracker.ContainsKey("Buried_" + islandLocation.Name + "_" + x + "_" + y)) + if (islandLocation.IsBuriedNutLocation(new Point(x, y)) && !nutTracker.ContainsKey($"Buried_{islandLocation.Name}_{x}_{y}")) { return (CATEGORY.Interactables, "Diggable spot"); } From 415231c65d01dee11d6bf79426674732c8de22cb Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 9 May 2022 23:29:21 +0530 Subject: [PATCH 113/232] Added farm and farm cave tiles --- stardew-access/assets/static-tiles.json | 42 +++++++++++++++++++++++++ stardew-access/manifest.json | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index b204e8b..ec96ea2 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -1,4 +1,46 @@ { + "farm": + { + "Bus Stop Entrance": + { + "x":[79], + "y":[15,16,17,18], + "type":"door" + }, + "Backwoods Entrance": + { + "x":[40,41], + "y":[0], + "type":"door" + }, + "Cindersap Forest Entrance": + { + "x":[40,41], + "y":[64], + "type":"door" + }, + "Farm Cave Entrance": + { + "x":[34], + "y":[7], + "type":"door" + }, + "Grandpa's Shrine": + { + "x":[8], + "y":[7], + "type":"interactable" + } + }, + "farmcave": + { + "Exit": + { + "x":[8], + "y":[11], + "type":"door" + } + }, "busstop": { "Ticket Machine": diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index 91acaaf..eb54070 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.2.0", + "Version": "1.2.1", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From eebabe8fe601a6db88daf5100a7e6898de30d045 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 10 May 2022 00:02:15 +0530 Subject: [PATCH 114/232] Added statue of perfection & endless fortune --- stardew-access/Features/TileInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index bf1c848..944434c 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -9,7 +9,7 @@ namespace stardew_access.Features { public class TileInfo { - public static string[] trackable_machines = { "bee house", "cask", "press", "keg", "machine", "maker", "preserves jar", "bone mill", "kiln", "crystalarium", "furnace", "geode crusher", "tapper", "lightning rod", "incubator", "wood chipper", "worm bin", "loom" }; + public static string[] trackable_machines = { "bee house", "cask", "press", "keg", "machine", "maker", "preserves jar", "bone mill", "kiln", "crystalarium", "furnace", "geode crusher", "tapper", "lightning rod", "incubator", "wood chipper", "worm bin", "loom", "statue of endless fortune", "statue of perfection" }; ///Returns the name of the object at tile alongwith it's category's name public static (string? name, string? categoryName) getNameWithCategoryNameAtTile(Vector2 tile) From 730beeffaa01d108c88d0d170b8d398e9a199afa Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 10 May 2022 00:21:37 +0530 Subject: [PATCH 115/232] removed birdie from static tiles (as she was already detectable) --- stardew-access/assets/static-tiles.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index ec96ea2..05fe015 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -1351,12 +1351,6 @@ "y":[22], "type":"door" }, - "Birdie": - { - "x":[18], - "y":[58], - "type":"npc" - }, "Hole 1": { "x":[37], From bc6ec49619de41d315555fbe311aebc13953732f Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Mon, 9 May 2022 23:53:29 -0400 Subject: [PATCH 116/232] Initial implementation of cursor movement and mouse snapping to cursor location. --- stardew-access/Features/MouseHandler.cs | 116 +++++++++++++++++++----- 1 file changed, 91 insertions(+), 25 deletions(-) diff --git a/stardew-access/Features/MouseHandler.cs b/stardew-access/Features/MouseHandler.cs index fb3f618..5b615f6 100644 --- a/stardew-access/Features/MouseHandler.cs +++ b/stardew-access/Features/MouseHandler.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using Microsoft.Xna.Framework; using xTile; using StardewValley; +using StardewValley.Menus; +using stardew_access.Features; namespace stardew_access.Features @@ -11,15 +13,18 @@ namespace stardew_access.Features { private Vector2 ViewingOffset = Vector2.Zero; + private Vector2 relativeOffsetLockPosition = Vector2.Zero; + private Boolean relativeOffsetLock = false; + private Vector2 prevPlayerPosition = Vector2.Zero, prevFacing = Vector2.Zero; private Vector2 PlayerFacingVector { get { -switch (Game1.player.FacingDirection) + switch (Game1.player.FacingDirection) { case 0: - return new Vector2(0, - Game1.tileSize); + return new Vector2(0, -Game1.tileSize); case 1: return new Vector2(Game1.tileSize, 0); case 2: @@ -36,24 +41,55 @@ switch (Game1.player.FacingDirection) { get { - int x = Game1.player.GetBoundingBox().Center.X - Game1.viewport.X; - int y = Game1.player.GetBoundingBox().Center.Y - Game1.viewport.Y; + int x = Game1.player.GetBoundingBox().Center.X; + int y = Game1.player.GetBoundingBox().Center.Y; return new Vector2(x, y); } } - private static (int, int) GetMapTileDimensions() + private Vector2 getTileCursorPosition() { - Map map = Game1.currentLocation.map; - return (map.Layers[0].LayerWidth, map.Layers[0].LayerHeight); + Vector2 target = this.PlayerPosition; + if (this.relativeOffsetLock) + { + target += this.relativeOffsetLockPosition; + } + else + { + target += this.PlayerFacingVector + this.ViewingOffset; + } + return target; } - - public bool MoveTileView(Vector2 delta) + + private void cursorMoveInput(Vector2 delta, Boolean precise = false) { - Vector2 dest = this.PlayerPosition + this.PlayerFacingVector + this.ViewingOffset + delta; + if (!tryMoveTileView(delta)) return; + Vector2 position = this.getTileCursorPosition(); + String ?name = TileInfo.getNameAtTile(position / Game1.tileSize); + if (name == null) + { + name = "empty tile"; + } + if (precise) + { + MainClass.ScreenReader.Say($"{position.X}, {position.Y}", true); + } + else + { + MainClass.ScreenReader.Say($"{name}, {(int)(position.X / Game1.tileSize)}, {(int)(position.Y / Game1.tileSize)}", true); + } + } + + private bool tryMoveTileView(Vector2 delta) + { + Vector2 dest = this.getTileCursorPosition() + delta; if (Utility.isOnScreen(dest, 0)) { - this.ViewingOffset += delta; + if (this.relativeOffsetLock) + this.relativeOffsetLockPosition += delta; + else + this.ViewingOffset += delta; + return true; } return false; @@ -61,24 +97,54 @@ switch (Game1.player.FacingDirection) private void SnapMouseToPlayer() { - Vector2 snapPosition = this.PlayerPosition + this.PlayerFacingVector + this.ViewingOffset; - Point snapPoint = new Point((int)snapPosition.X, (int)snapPosition.Y); - if (Utility.isOnScreen(snapPoint, 0)) - Game1.setMousePosition(snapPoint.X, snapPoint.Y); + Vector2 cursorPosition = this.getTileCursorPosition(); + if (allowMouseSnap(cursorPosition)) + Game1.setMousePosition((int)cursorPosition.X - Game1.viewport.X, (int)cursorPosition.Y - Game1.viewport.Y); } -public void update() + public void update() { + if (this.prevFacing != this.PlayerFacingVector || this.prevPlayerPosition != this.PlayerPosition) + { + this.ViewingOffset = Vector2.Zero; + } + this.prevFacing = this.PlayerFacingVector; + this.prevPlayerPosition = this.PlayerPosition; if (MainClass.Config.SnapMouse) - this.SnapMouseToPlayer(); + this.SnapMouseToPlayer(); + if (MainClass.Config.TileCursorUpKey.JustPressed()) + { + this.cursorMoveInput(new Vector2(0, -Game1.tileSize)); + } + else if (MainClass.Config.TileCursorRightKey.JustPressed()) + { + this.cursorMoveInput(new Vector2(Game1.tileSize, 0)); + } + else if (MainClass.Config.TileCursorDownKey.JustPressed()) + { + this.cursorMoveInput(new Vector2(0, Game1.tileSize)); + } + else if (MainClass.Config.TileCursorLeftKey.JustPressed()) + { + this.cursorMoveInput(new Vector2(-Game1.tileSize, 0)); + } + } + + + private static bool allowMouseSnap(Vector2 point) + { + if (!Utility.isOnScreen(point, 0)) return false; + + //prevent mousing over the toolbar or any other UI component with the tile cursor + foreach (IClickableMenu menu in Game1.onScreenMenus) + { + if (menu.allClickableComponents == null) continue; + foreach (ClickableComponent component in menu.allClickableComponents) + { + if (component.containsPoint((int)point.X, (int)point.Y)) return false; + } + } + return true; } - - private static bool IsTileOnMap(Vector2 tile) - { - (int width, int height) dimensions = GetMapTileDimensions(); - if (tile.X < 0 || tile.X >= dimensions.width) return false; - if (tile.Y < 0 || tile.Y >= dimensions.height) return false; - return true; } } -} From 838273b3ce1449cc52bcae9d35e0233281ab4c0f Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Tue, 10 May 2022 00:05:35 -0400 Subject: [PATCH 117/232] Read blocked or empty tiles if no object is on a tile. --- stardew-access/Features/MouseHandler.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/stardew-access/Features/MouseHandler.cs b/stardew-access/Features/MouseHandler.cs index 5b615f6..b93c96b 100644 --- a/stardew-access/Features/MouseHandler.cs +++ b/stardew-access/Features/MouseHandler.cs @@ -65,10 +65,17 @@ namespace stardew_access.Features { if (!tryMoveTileView(delta)) return; Vector2 position = this.getTileCursorPosition(); - String ?name = TileInfo.getNameAtTile(position / Game1.tileSize); + Vector2 tile = position / Game1.tileSize; + String ?name = TileInfo.getNameAtTile(tile); if (name == null) { - name = "empty tile"; + if (TileInfo.isCollidingAtTile((int)tile.X, (int)tile.Y)) + { + name = "blocked"; + } else + { + name = "empty"; + } } if (precise) { From 672f9a1f007539168490f50a3cd239b56ac8d5b0 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 10 May 2022 19:39:22 +0530 Subject: [PATCH 118/232] Added twig to debris category --- stardew-access/Features/TileInfo.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 944434c..797e00f 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -667,13 +667,14 @@ namespace stardew_access.Features // Get object names based on index (string? name, CATEGORY category) correctNameAndCategory = getCorrectNameAndCategoryFromIndex(index); + if (correctNameAndCategory.name != null) toReturn = correctNameAndCategory; - - if (toReturn.name.ToLower().Equals("stone")) // Fix for `Busy stone` + else if (obj.name.ToLower().Equals("stone")) toReturn.category = CATEGORY.Debris; - - if (obj is Chest) + else if (obj.name.ToLower().Equals("twig")) + toReturn.category = CATEGORY.Debris; + else if (obj is Chest) { Chest chest = (Chest)obj; toReturn = (chest.DisplayName, CATEGORY.Chests); From cedf2dcc0319bb8dae2b8551c3fe556816190fad Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 10 May 2022 19:48:04 +0530 Subject: [PATCH 119/232] Added area narration when switching to other area --- .gitignore | 1 + stardew-access/Patches/BundleMenuPatches.cs | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 82c0f04..2c855eb 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ .vscode/* .git-old/ +bin/ # User-specific files *.rsuser diff --git a/stardew-access/Patches/BundleMenuPatches.cs b/stardew-access/Patches/BundleMenuPatches.cs index d9495e1..a1bf47c 100644 --- a/stardew-access/Patches/BundleMenuPatches.cs +++ b/stardew-access/Patches/BundleMenuPatches.cs @@ -7,6 +7,7 @@ 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; @@ -80,6 +81,8 @@ namespace stardew_access.Patches isUsingCustomButtons = false; string areaName = __instance.scrambledText ? CommunityCenter.getAreaEnglishDisplayNameFromNumber(___whichArea) : CommunityCenter.getAreaDisplayNameFromNumber(___whichArea); + string reward = __instance.getRewardNameForArea(___whichArea); + if (__instance.scrambledText) { string toSpeak = "Scrambled Text"; @@ -90,6 +93,15 @@ namespace stardew_access.Patches } return; } + + if (currentJunimoArea != areaName) + { + currentJunimoArea = areaName; + MainClass.DebugLog(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)) @@ -175,7 +187,6 @@ namespace stardew_access.Patches MainClass.ScreenReader.Say("Purchase Button", true); } } - string reward = __instance.getRewardNameForArea(___whichArea); } catch (Exception e) { From 6acfd313e8493d9e0208a06f17db96db8b5d38e5 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 10 May 2022 20:05:02 +0530 Subject: [PATCH 120/232] Added quartz, etc. to mine item category --- stardew-access/Features/TileInfo.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 797e00f..df206ab 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -562,11 +562,6 @@ namespace stardew_access.Features category = CATEGORY.Trees; toReturn = getTree((Tree)terrain.Get()); } - else if (terrain.Get() is Quartz) - { - category = CATEGORY.MineItems; - toReturn = "Quartz"; - } return (toReturn, category); } @@ -674,6 +669,12 @@ namespace stardew_access.Features toReturn.category = CATEGORY.Debris; else if (obj.name.ToLower().Equals("twig")) toReturn.category = CATEGORY.Debris; + else if (obj.name.ToLower().Contains("quartz")) + toReturn.category = CATEGORY.MineItems; + else if (obj.name.ToLower().Contains("earth crystal")) + toReturn.category = CATEGORY.MineItems; + else if (obj.name.ToLower().Contains("frozen tear")) + toReturn.category = CATEGORY.MineItems; else if (obj is Chest) { Chest chest = (Chest)obj; From 7688bc0dd5dd1dd850b258da6ceeacfbf003450f Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 10 May 2022 20:47:21 +0530 Subject: [PATCH 121/232] Fixed giant crop narration --- stardew-access/Features/TileInfo.cs | 33 ++++++++++------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index df206ab..a63381c 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -92,6 +92,11 @@ namespace stardew_access.Features toReturn = obj.name; category = obj.category; } + else if (resourceClump != null) + { + toReturn = resourceClump; + category = CATEGORY.ResourceClumps; + } else if (terrainFeature.ContainsKey(tile)) { (string? name, CATEGORY category) tf = getTerrainFeatureAtTile(terrainFeature[tile]); @@ -108,11 +113,6 @@ namespace stardew_access.Features toReturn = bush; category = CATEGORY.Bush; } - else if (resourceClump != null) - { - toReturn = resourceClump; - category = CATEGORY.ResourceClumps; - } else if (door != null) { toReturn = door; @@ -503,23 +503,6 @@ namespace stardew_access.Features toReturn = "Fertilized " + toReturn; } } - else if (terrain.Get() is GiantCrop) - { - category = CATEGORY.Crops; - int whichCrop = ((GiantCrop)terrain.Get()).which.Value; - switch (whichCrop) - { - case 0: - toReturn = "Cauliflower"; - break; - case 1: - toReturn = "Melon"; - break; - case 2: - toReturn = "Pumpkin"; - break; - } - } else if (terrain.Get() is CosmeticPlant) { category = CATEGORY.Furnitures; @@ -970,6 +953,12 @@ namespace stardew_access.Features return "Mine Rock"; case 672: return "Boulder"; + case 190: + return "Giant Cauliflower"; + case 254: + return "Giant Melon"; + case 276: + return "Giant Pumpkin"; default: return "Unknown"; } From 78a466a01023c55d2884cf941e825627a527190b Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 10 May 2022 20:47:40 +0530 Subject: [PATCH 122/232] Added category to manual read tile key --- stardew-access/Features/ReadTile.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 54dcb92..40ad4c5 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -19,8 +19,9 @@ namespace stardew_access.Features try { Vector2 tile; - int x, y; + #region Get Tile + int x, y; if (!playersPosition) { // Grab tile @@ -31,9 +32,9 @@ namespace stardew_access.Features // Player's standing tile tile = CurrentPlayer.getPosition(); } - #endregion x = (int)tile.X; y = (int)tile.Y; + #endregion if (Context.IsPlayerFree) { @@ -45,15 +46,15 @@ namespace stardew_access.Features bool isColliding = TileInfo.isCollidingAtTile(x, y); - string? toSpeak = TileInfo.getNameAtTile(tile); + (string? name, string? category) info = TileInfo.getNameWithCategoryNameAtTile(tile); #region Narrate toSpeak - if (toSpeak != null) + if (info.name != null) if (MainClass.ScreenReader != null) if (manuallyTriggered) - MainClass.ScreenReader.Say(toSpeak, true); + MainClass.ScreenReader.Say($"{info.name}, Category: {info.category}", true); else - MainClass.ScreenReader.SayWithTileQuery(toSpeak, x, y, true); + MainClass.ScreenReader.SayWithTileQuery(info.name, x, y, true); #endregion #region Play colliding sound effect From c49dec4f29f75fdb449f7fafc17efcef5ae87dda Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Tue, 10 May 2022 20:55:51 +0530 Subject: [PATCH 123/232] Beta 1.2.2 --- stardew-access/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index eb54070..f702e9f 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.2.1", + "Version": "1.2.2", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From 332dd858a85dff9372fc3d88877fc8b647520bac Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Tue, 10 May 2022 14:21:17 -0400 Subject: [PATCH 124/232] Fixed bug with tile cursor not passing exact integer tile values causing terrain features like crops to not be read out. --- stardew-access/Features/MouseHandler.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/stardew-access/Features/MouseHandler.cs b/stardew-access/Features/MouseHandler.cs index b93c96b..ecdcf7d 100644 --- a/stardew-access/Features/MouseHandler.cs +++ b/stardew-access/Features/MouseHandler.cs @@ -63,9 +63,9 @@ namespace stardew_access.Features private void cursorMoveInput(Vector2 delta, Boolean precise = false) { - if (!tryMoveTileView(delta)) return; + if (!tryMoveTileView(delta)) return; Vector2 position = this.getTileCursorPosition(); - Vector2 tile = position / Game1.tileSize; + Vector2 tile = new Vector2((float)Math.Floor(position.X / Game1.tileSize), (float)Math.Floor(position.Y / Game1.tileSize)); String ?name = TileInfo.getNameAtTile(tile); if (name == null) { @@ -79,7 +79,7 @@ namespace stardew_access.Features } if (precise) { - MainClass.ScreenReader.Say($"{position.X}, {position.Y}", true); + MainClass.ScreenReader.Say($"{name}, {position.X}, {position.Y}", true); } else { @@ -111,6 +111,7 @@ namespace stardew_access.Features public void update() { + //Reset the viewing cursor to the player when they turn or move. This will not reset the locked offset relative cursor position. if (this.prevFacing != this.PlayerFacingVector || this.prevPlayerPosition != this.PlayerPosition) { this.ViewingOffset = Vector2.Zero; @@ -119,6 +120,7 @@ namespace stardew_access.Features this.prevPlayerPosition = this.PlayerPosition; if (MainClass.Config.SnapMouse) this.SnapMouseToPlayer(); + if (MainClass.Config.TileCursorUpKey.JustPressed()) { this.cursorMoveInput(new Vector2(0, -Game1.tileSize)); @@ -137,7 +139,6 @@ namespace stardew_access.Features } } - private static bool allowMouseSnap(Vector2 point) { if (!Utility.isOnScreen(point, 0)) return false; From a8bcaf6cef30fe5832f7be838d9c560459c68aa9 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Tue, 10 May 2022 14:37:43 -0400 Subject: [PATCH 125/232] Refactored keyboard tile cursor input to be triggered at the same time as the rest of keyboard input; fix bug in reading UI components with tile cursor. --- stardew-access/Features/MouseHandler.cs | 43 ++++++++++++------------- stardew-access/ModEntry.cs | 3 ++ 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/stardew-access/Features/MouseHandler.cs b/stardew-access/Features/MouseHandler.cs index ecdcf7d..678b8c4 100644 --- a/stardew-access/Features/MouseHandler.cs +++ b/stardew-access/Features/MouseHandler.cs @@ -61,6 +61,26 @@ namespace stardew_access.Features return target; } + public void HandleInput() + { + if (MainClass.Config.TileCursorUpKey.JustPressed()) + { + this.cursorMoveInput(new Vector2(0, -Game1.tileSize)); + } + else if (MainClass.Config.TileCursorRightKey.JustPressed()) + { + this.cursorMoveInput(new Vector2(Game1.tileSize, 0)); + } + else if (MainClass.Config.TileCursorDownKey.JustPressed()) + { + this.cursorMoveInput(new Vector2(0, Game1.tileSize)); + } + else if (MainClass.Config.TileCursorLeftKey.JustPressed()) + { + this.cursorMoveInput(new Vector2(-Game1.tileSize, 0)); + } + } + private void cursorMoveInput(Vector2 delta, Boolean precise = false) { if (!tryMoveTileView(delta)) return; @@ -120,23 +140,6 @@ namespace stardew_access.Features this.prevPlayerPosition = this.PlayerPosition; if (MainClass.Config.SnapMouse) this.SnapMouseToPlayer(); - - if (MainClass.Config.TileCursorUpKey.JustPressed()) - { - this.cursorMoveInput(new Vector2(0, -Game1.tileSize)); - } - else if (MainClass.Config.TileCursorRightKey.JustPressed()) - { - this.cursorMoveInput(new Vector2(Game1.tileSize, 0)); - } - else if (MainClass.Config.TileCursorDownKey.JustPressed()) - { - this.cursorMoveInput(new Vector2(0, Game1.tileSize)); - } - else if (MainClass.Config.TileCursorLeftKey.JustPressed()) - { - this.cursorMoveInput(new Vector2(-Game1.tileSize, 0)); - } } private static bool allowMouseSnap(Vector2 point) @@ -146,11 +149,7 @@ namespace stardew_access.Features //prevent mousing over the toolbar or any other UI component with the tile cursor foreach (IClickableMenu menu in Game1.onScreenMenus) { - if (menu.allClickableComponents == null) continue; - foreach (ClickableComponent component in menu.allClickableComponents) - { - if (component.containsPoint((int)point.X, (int)point.Y)) return false; - } + if (menu.isWithinBounds((int)point.X - Game1.viewport.X, (int)point.Y - Game1.viewport.Y)) return false; } return true; } diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 28c19fe..2dd3899 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -267,6 +267,9 @@ get ReadTile.run(manuallyTriggered: true); return; } + + // Tile viewing cursor keys + Mouse.HandleInput(); } public static void ErrorLog(string message) From c8bf29b6d09c3064120c4617acaf97428632dbc0 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Tue, 10 May 2022 15:09:54 -0400 Subject: [PATCH 126/232] Added precise tile cursor movement (defaults to 8 pixels per key press). --- stardew-access/Features/MouseHandler.cs | 18 +++++++++++++++++- stardew-access/ModConfig.cs | 3 ++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/stardew-access/Features/MouseHandler.cs b/stardew-access/Features/MouseHandler.cs index 678b8c4..33af128 100644 --- a/stardew-access/Features/MouseHandler.cs +++ b/stardew-access/Features/MouseHandler.cs @@ -63,7 +63,23 @@ namespace stardew_access.Features public void HandleInput() { - if (MainClass.Config.TileCursorUpKey.JustPressed()) + if (MainClass.Config.TileCursorPreciseUpKey.JustPressed()) + { + this.cursorMoveInput(new Vector2(0, -MainClass.Config.TileCursorPreciseMovementDistance), true); + } + else if (MainClass.Config.TileCursorPreciseRightKey.JustPressed()) + { + this.cursorMoveInput(new Vector2(MainClass.Config.TileCursorPreciseMovementDistance, 0), true); + } + else if (MainClass.Config.TileCursorPreciseDownKey.JustPressed()) + { + this.cursorMoveInput(new Vector2(0, MainClass.Config.TileCursorPreciseMovementDistance), true); + } + else if (MainClass.Config.TileCursorPreciseLeftKey.JustPressed()) + { + this.cursorMoveInput(new Vector2(-MainClass.Config.TileCursorPreciseMovementDistance, 0), true); + } + else if (MainClass.Config.TileCursorUpKey.JustPressed()) { this.cursorMoveInput(new Vector2(0, -Game1.tileSize)); } diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index 388d7fd..445fa1b 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -25,6 +25,7 @@ namespace stardew_access public KeybindList TimeNSeasonKey { get; set; } = KeybindList.Parse("Q"); public KeybindList ReadTileKey { get; set; } = KeybindList.Parse("J"); public KeybindList ReadStandingTileKey { get; set; } = KeybindList.Parse("LeftAlt + J"); + public int TileCursorPreciseMovementDistance { get; set; } = 8; //Tile viewer keys public KeybindList TileCursorUpKey { get; set; } = KeybindList.Parse("Up"); @@ -35,7 +36,7 @@ namespace stardew_access public KeybindList TileCursorPreciseRightKey { get; set; } = KeybindList.Parse("LeftShift + Right"); public KeybindList TileCursorPreciseDownKey { get; set; } = KeybindList.Parse("LeftShift + Down"); public KeybindList TileCursorPreciseLeftKey { get; set; } = KeybindList.Parse("LeftShift + Left"); - + public KeybindList ToggleRelativeCursorLockKey { get; set; } = KeybindList.Parse("L"); #endregion // TODO Add the exclusion and focus list too From 955ffac65bbb7cbd5d91e1efcd49df8bfc89dbac Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Tue, 10 May 2022 15:22:32 -0400 Subject: [PATCH 127/232] Implementation of relative offset lock (keep mouse in position relative to you as yo umove). --- stardew-access/Features/MouseHandler.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/stardew-access/Features/MouseHandler.cs b/stardew-access/Features/MouseHandler.cs index 33af128..f909ab9 100644 --- a/stardew-access/Features/MouseHandler.cs +++ b/stardew-access/Features/MouseHandler.cs @@ -63,7 +63,18 @@ namespace stardew_access.Features public void HandleInput() { - if (MainClass.Config.TileCursorPreciseUpKey.JustPressed()) + if (MainClass.Config.ToggleRelativeCursorLockKey.JustPressed()) + { + this.relativeOffsetLock = !this.relativeOffsetLock; + if (this.relativeOffsetLock) + { + this.relativeOffsetLockPosition = this.PlayerFacingVector + this.ViewingOffset; + } else { + this.relativeOffsetLockPosition = Vector2.Zero; + } + MainClass.ScreenReader.Say("Relative cursor lock " + (this.relativeOffsetLock ? "enabled" : "disabled") + ".", true); + } + else if (MainClass.Config.TileCursorPreciseUpKey.JustPressed()) { this.cursorMoveInput(new Vector2(0, -MainClass.Config.TileCursorPreciseMovementDistance), true); } From 7a1b768bdbed437bbafaf0691cdf89b6ab971464 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Tue, 10 May 2022 16:31:54 -0400 Subject: [PATCH 128/232] Added option to allow tile cursor to view the entire map regardless if it is visible or not. --- stardew-access/Features/MouseHandler.cs | 13 ++++++++++--- stardew-access/ModConfig.cs | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/stardew-access/Features/MouseHandler.cs b/stardew-access/Features/MouseHandler.cs index f909ab9..a84f52e 100644 --- a/stardew-access/Features/MouseHandler.cs +++ b/stardew-access/Features/MouseHandler.cs @@ -6,7 +6,6 @@ using StardewValley; using StardewValley.Menus; using stardew_access.Features; - namespace stardew_access.Features { public class MouseHandler @@ -137,13 +136,13 @@ namespace stardew_access.Features private bool tryMoveTileView(Vector2 delta) { Vector2 dest = this.getTileCursorPosition() + delta; - if (Utility.isOnScreen(dest, 0)) + if (!isPositionOnMap(dest)) return false; + if ((MainClass.Config.LimitTileCursorToScreen && Utility.isOnScreen(dest, 0)) || !MainClass.Config.LimitTileCursorToScreen) { if (this.relativeOffsetLock) this.relativeOffsetLockPosition += delta; else this.ViewingOffset += delta; - return true; } return false; @@ -180,5 +179,13 @@ namespace stardew_access.Features } return true; } + + private static bool isPositionOnMap(Vector2 position) + { + Map map = Game1.currentLocation.map; + if (position.X < 0 || position.X > map.Layers[0].DisplayWidth) return false; + if (position.Y < 0 || position.Y > map.Layers[0].DisplayHeight) return false; + return true; + } } } diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index 445fa1b..479c25d 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -25,6 +25,7 @@ namespace stardew_access public KeybindList TimeNSeasonKey { get; set; } = KeybindList.Parse("Q"); public KeybindList ReadTileKey { get; set; } = KeybindList.Parse("J"); public KeybindList ReadStandingTileKey { get; set; } = KeybindList.Parse("LeftAlt + J"); + public bool LimitTileCursorToScreen { get; set; } = false; public int TileCursorPreciseMovementDistance { get; set; } = 8; //Tile viewer keys From 8446049c55c348ccce4d83b9927d8f758b455c6b Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Tue, 10 May 2022 16:36:23 -0400 Subject: [PATCH 129/232] Rename MouseHandler to TileViewer for accuracy --- .../Features/{MouseHandler.cs => TileViewer.cs} | 2 +- stardew-access/ModEntry.cs | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) rename stardew-access/Features/{MouseHandler.cs => TileViewer.cs} (99%) diff --git a/stardew-access/Features/MouseHandler.cs b/stardew-access/Features/TileViewer.cs similarity index 99% rename from stardew-access/Features/MouseHandler.cs rename to stardew-access/Features/TileViewer.cs index a84f52e..4d06f8f 100644 --- a/stardew-access/Features/MouseHandler.cs +++ b/stardew-access/Features/TileViewer.cs @@ -8,7 +8,7 @@ using stardew_access.Features; namespace stardew_access.Features { - public class MouseHandler + public class TileViewer { private Vector2 ViewingOffset = Vector2.Zero; diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 2dd3899..d46348e 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -20,7 +20,7 @@ namespace stardew_access private static StaticTiles? sTiles; private static IScreenReader? screenReader; private static IModHelper? modHelper; - private static MouseHandler? mouse; + private static TileViewer? tileViewer; internal static ModConfig Config { get => config; set => config = value; } public static IModHelper? ModHelper { get => modHelper; } @@ -64,13 +64,13 @@ namespace stardew_access set => screenReader = value; } - public static MouseHandler Mouse + public static TileViewer TileViewer { get { - if (mouse == null) - mouse = new MouseHandler(); - return mouse; + if (tileViewer == null) + tileViewer = new TileViewer(); + return tileViewer; } } @@ -142,7 +142,8 @@ get // Narrate current location's name Other.narrateCurrentLocation(); - Mouse.update(); + //handle TileCursor update logic + TileViewer.update(); if (!ReadTile.isReadingTile && Config.ReadTile) { @@ -269,7 +270,7 @@ get } // Tile viewing cursor keys - Mouse.HandleInput(); + TileViewer.HandleInput(); } public static void ErrorLog(string message) From 55b9255dc84467af0c9433cb6e4253afd4660869 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Tue, 10 May 2022 16:41:07 -0400 Subject: [PATCH 130/232] Formatting and code cleanup; added documentation and some clarifying comments. --- stardew-access/Features/TileViewer.cs | 59 ++++++++++++++++++++++----- stardew-access/ModEntry.cs | 2 +- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/stardew-access/Features/TileViewer.cs b/stardew-access/Features/TileViewer.cs index 4d06f8f..2b07fde 100644 --- a/stardew-access/Features/TileViewer.cs +++ b/stardew-access/Features/TileViewer.cs @@ -8,9 +8,14 @@ using stardew_access.Features; namespace stardew_access.Features { + + /// + /// Allows browsing of the map and snapping mouse to tiles with the arrow keys + /// public class TileViewer { + //None of these positions take viewport into account; other functions are responsible later private Vector2 ViewingOffset = Vector2.Zero; private Vector2 relativeOffsetLockPosition = Vector2.Zero; private Boolean relativeOffsetLock = false; @@ -46,7 +51,11 @@ namespace stardew_access.Features } } - private Vector2 getTileCursorPosition() + /// + /// Return the position of the tile cursor in pixels from the upper-left corner of the map. + /// + /// Vector2 + public Vector2 GetTileCursorPosition() { Vector2 target = this.PlayerPosition; if (this.relativeOffsetLock) @@ -60,6 +69,19 @@ namespace stardew_access.Features return target; } + /// + /// Return the tile at the position of the tile cursor. + /// + /// Vector2 + public Vector2 GetViewingTile() + { + Vector2 position = this.GetTileCursorPosition(); + return new Vector2((int)position.X / Game1.tileSize, (int)position.Y / Game1.tileSize); + } + + /// + /// Handle keyboard input related to the tile viewer. + /// public void HandleInput() { if (MainClass.Config.ToggleRelativeCursorLockKey.JustPressed()) @@ -68,12 +90,14 @@ namespace stardew_access.Features if (this.relativeOffsetLock) { this.relativeOffsetLockPosition = this.PlayerFacingVector + this.ViewingOffset; - } else { + } + else + { this.relativeOffsetLockPosition = Vector2.Zero; } MainClass.ScreenReader.Say("Relative cursor lock " + (this.relativeOffsetLock ? "enabled" : "disabled") + ".", true); } - else if (MainClass.Config.TileCursorPreciseUpKey.JustPressed()) + else if (MainClass.Config.TileCursorPreciseUpKey.JustPressed()) { this.cursorMoveInput(new Vector2(0, -MainClass.Config.TileCursorPreciseMovementDistance), true); } @@ -109,16 +133,18 @@ namespace stardew_access.Features private void cursorMoveInput(Vector2 delta, Boolean precise = false) { - if (!tryMoveTileView(delta)) return; - Vector2 position = this.getTileCursorPosition(); - Vector2 tile = new Vector2((float)Math.Floor(position.X / Game1.tileSize), (float)Math.Floor(position.Y / Game1.tileSize)); - String ?name = TileInfo.getNameAtTile(tile); + if (!tryMoveTileView(delta)) return; + Vector2 position = this.GetTileCursorPosition(); + Vector2 tile = this.GetViewingTile(); + String? name = TileInfo.getNameAtTile(tile); if (name == null) { + // Report if a tile is empty or blocked if there is nothing on it if (TileInfo.isCollidingAtTile((int)tile.X, (int)tile.Y)) { name = "blocked"; - } else + } + else { name = "empty"; } @@ -135,14 +161,18 @@ namespace stardew_access.Features private bool tryMoveTileView(Vector2 delta) { - Vector2 dest = this.getTileCursorPosition() + delta; + Vector2 dest = this.GetTileCursorPosition() + delta; if (!isPositionOnMap(dest)) return false; if ((MainClass.Config.LimitTileCursorToScreen && Utility.isOnScreen(dest, 0)) || !MainClass.Config.LimitTileCursorToScreen) { if (this.relativeOffsetLock) + { this.relativeOffsetLockPosition += delta; + } else + { this.ViewingOffset += delta; + } return true; } return false; @@ -150,11 +180,15 @@ namespace stardew_access.Features private void SnapMouseToPlayer() { - Vector2 cursorPosition = this.getTileCursorPosition(); - if (allowMouseSnap(cursorPosition)) + Vector2 cursorPosition = this.GetTileCursorPosition(); + if (allowMouseSnap(cursorPosition)) + // Must account for viewport here Game1.setMousePosition((int)cursorPosition.X - Game1.viewport.X, (int)cursorPosition.Y - Game1.viewport.Y); } + /// + /// Handle tile viewer logic. + /// public void update() { //Reset the viewing cursor to the player when they turn or move. This will not reset the locked offset relative cursor position. @@ -170,11 +204,13 @@ namespace stardew_access.Features private static bool allowMouseSnap(Vector2 point) { + // Utility.isOnScreen treats a vector as a pixel position, not a tile position if (!Utility.isOnScreen(point, 0)) return false; //prevent mousing over the toolbar or any other UI component with the tile cursor foreach (IClickableMenu menu in Game1.onScreenMenus) { + //must account for viewport here if (menu.isWithinBounds((int)point.X - Game1.viewport.X, (int)point.Y - Game1.viewport.Y)) return false; } return true; @@ -182,6 +218,7 @@ namespace stardew_access.Features private static bool isPositionOnMap(Vector2 position) { + //position does not take viewport into account since the entire map needs to be checked. Map map = Game1.currentLocation.map; if (position.X < 0 || position.X > map.Layers[0].DisplayWidth) return false; if (position.Y < 0 || position.Y > map.Layers[0].DisplayHeight) return false; diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index d46348e..28776c4 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -66,7 +66,7 @@ namespace stardew_access public static TileViewer TileViewer { -get + get { if (tileViewer == null) tileViewer = new TileViewer(); From eb2524337784ecb40f7d4ae82483b2539f7e5c73 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Wed, 11 May 2022 13:14:46 +0530 Subject: [PATCH 131/232] Code documentation --- stardew-access/Features/CurrentPlayer.cs | 264 ++++++++++++++--------- stardew-access/Features/Radar.cs | 2 +- stardew-access/Features/ReadTile.cs | 4 +- stardew-access/ModEntry.cs | 10 +- stardew-access/Patches/MenuPatches.cs | 2 +- 5 files changed, 166 insertions(+), 116 deletions(-) diff --git a/stardew-access/Features/CurrentPlayer.cs b/stardew-access/Features/CurrentPlayer.cs index 3f29382..7a2f525 100644 --- a/stardew-access/Features/CurrentPlayer.cs +++ b/stardew-access/Features/CurrentPlayer.cs @@ -6,121 +6,171 @@ namespace stardew_access.Features internal class CurrentPlayer { - public static int getHealth() + /// + /// Returns the percentage health remaining of player. + /// + public static int Health { - if (Game1.player == null) - return 0; - - int maxHealth = Game1.player.maxHealth; - int currentHealth = Game1.player.health; - - int healthPercentage = (int)(currentHealth * 100) / maxHealth; - return healthPercentage; - } - - public static int getStamina() - { - if (Game1.player == null) - return 0; - - int maxStamina = Game1.player.maxStamina.Value; - int currentStamine = (int)Game1.player.stamina; - - int staminaPercentage = (int)(currentStamine * 100) / maxStamina; - - return staminaPercentage; - } - - public static Vector2 getPosition() - { - if (Game1.player == null) - return Vector2.Zero; - - return Game1.player.getTileLocation(); - } - - public static int getPositionX() - { - if (Game1.player == null) - return 0; - - return (int)getPosition().X; - } - - public static int getPositionY() - { - if (Game1.player == null) - return 0; - - return (int)getPosition().Y; - } - - public static string getTimeOfDay() - { - int timeOfDay = Game1.timeOfDay; - - int minutes = timeOfDay % 100; - int hours = timeOfDay / 100; - string amOrpm = "A M"; - if (hours >= 12) + get { - amOrpm = "P M"; - if (hours > 12) - hours -= 12; + if (Game1.player == null) + return 0; + + int maxHealth = Game1.player.maxHealth; + int currentHealth = Game1.player.health; + + int healthPercentage = (int)(currentHealth * 100) / maxHealth; + return healthPercentage; } - - return $"{hours}:{minutes} {amOrpm}"; } - public static string getSeason() + /// + /// Returns the percentage stamine/energy remaining of player. + /// + public static int Stamina { - return Game1.CurrentSeasonDisplayName; - } - - public static int getDate() - { - return Game1.dayOfMonth; - } - - public static string getDay() - { - return Game1.Date.DayOfWeek.ToString(); - } - - public static int getMoney() - { - if (Game1.player == null) - return -1; - - return Game1.player.Money; - } - - public static Vector2 getNextTile() - { - int x = Game1.player.GetBoundingBox().Center.X; - int y = Game1.player.GetBoundingBox().Center.Y; - - int offset = 64; - - switch (Game1.player.FacingDirection) + get { - case 0: - y -= offset; - break; - case 1: - x += offset; - break; - case 2: - y += offset; - break; - case 3: - x -= offset; - break; - } + if (Game1.player == null) + return 0; - x /= Game1.tileSize; - y /= Game1.tileSize; - return new Vector2(x, y); + int maxStamina = Game1.player.maxStamina.Value; + int currentStamine = (int)Game1.player.stamina; + + int staminaPercentage = (int)(currentStamine * 100) / maxStamina; + + return staminaPercentage; + } + } + + /// + /// Returns the tile location of the player + /// + public static Vector2 Position + { + get + { + if (Game1.player == null) + return Vector2.Zero; + + return Game1.player.getTileLocation(); + } + } + + /// + /// Returns the X coordinate of the player + /// + public static int PositionX + { + get + { + if (Game1.player == null) + return 0; + + return (int)Position.X; + } + } + + /// + /// Returns the Y coordinate of the player + /// + public static int PositionY + { + get + { + if (Game1.player == null) + return 0; + + return (int)Position.Y; + } + } + + /// + /// Returns the time in the 12 hours format + /// + public static string TimeOfDay + { + get + { + int timeOfDay = Game1.timeOfDay; + + int minutes = timeOfDay % 100; + int hours = timeOfDay / 100; + string amOrpm = "A M"; + if (hours >= 12) + { + amOrpm = "P M"; + if (hours > 12) + hours -= 12; + } + + return $"{hours}:{minutes} {amOrpm}"; + } + } + + /// + /// Returns the current season + /// + public static string Season => Game1.CurrentSeasonDisplayName; + + /// + /// Returns the current date of month + /// + public static int Date => Game1.dayOfMonth; + + /// + /// Returns the current day of week + /// + /// + public static string Day => Game1.Date.DayOfWeek.ToString(); + + /// + /// Returns the amount of money the player has currently + /// + public static int Money + { + get + { + if (Game1.player == null) + return -1; + + return Game1.player.Money; + } + } + + /// + /// Returns the tile position of the tile the player is facing + /// + /// + public static Vector2 FacingTile + { + get + { + int x = Game1.player.GetBoundingBox().Center.X; + int y = Game1.player.GetBoundingBox().Center.Y; + + int offset = 64; + + switch (Game1.player.FacingDirection) + { + case 0: + y -= offset; + break; + case 1: + x += offset; + break; + case 2: + y += offset; + break; + case 3: + x -= offset; + break; + } + + x /= Game1.tileSize; + y /= Game1.tileSize; + return new Vector2(x, y); + } } } } diff --git a/stardew-access/Features/Radar.cs b/stardew-access/Features/Radar.cs index 5253de9..2c3c050 100644 --- a/stardew-access/Features/Radar.cs +++ b/stardew-access/Features/Radar.cs @@ -262,7 +262,7 @@ namespace stardew_access.Features #region Check whether to skip the object or not // Skip if player is directly looking at the tile - if (CurrentPlayer.getNextTile().Equals(position)) + if (CurrentPlayer.FacingTile.Equals(position)) return; if (!radarFocus) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 40ad4c5..5017cb7 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -25,12 +25,12 @@ namespace stardew_access.Features if (!playersPosition) { // Grab tile - tile = CurrentPlayer.getNextTile(); + tile = CurrentPlayer.FacingTile; } else { // Player's standing tile - tile = CurrentPlayer.getPosition(); + tile = CurrentPlayer.Position; } x = (int)tile.X; y = (int)tile.Y; diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 28776c4..c79e472 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -220,11 +220,11 @@ namespace stardew_access string toSpeak; if (Config.VerboseCoordinates) { - toSpeak = $"X: {CurrentPlayer.getPositionX()}, Y: {CurrentPlayer.getPositionY()}"; + toSpeak = $"X: {CurrentPlayer.PositionX}, Y: {CurrentPlayer.PositionY}"; } else { - toSpeak = $"{CurrentPlayer.getPositionX()}, {CurrentPlayer.getPositionY()}"; + toSpeak = $"{CurrentPlayer.PositionX}, {CurrentPlayer.PositionY}"; } MainClass.ScreenReader.Say(toSpeak, true); @@ -234,7 +234,7 @@ namespace stardew_access // Narrate health and stamina if (Config.HealthNStaminaKey.JustPressed()) { - string toSpeak = $"Health is {CurrentPlayer.getHealth()} and Stamina is {CurrentPlayer.getStamina()}"; + string toSpeak = $"Health is {CurrentPlayer.Health} and Stamina is {CurrentPlayer.Stamina}"; MainClass.ScreenReader.Say(toSpeak, true); return; } @@ -242,7 +242,7 @@ namespace stardew_access // Narrate money at hand if (Config.MoneyKey.JustPressed()) { - string toSpeak = $"You have {CurrentPlayer.getMoney()}g"; + string toSpeak = $"You have {CurrentPlayer.Money}g"; MainClass.ScreenReader.Say(toSpeak, true); return; } @@ -250,7 +250,7 @@ namespace stardew_access // Narrate time and season if (Config.TimeNSeasonKey.JustPressed()) { - string toSpeak = $"Time is {CurrentPlayer.getTimeOfDay()} and it is {CurrentPlayer.getDay()} {CurrentPlayer.getDate()} of {CurrentPlayer.getSeason()}"; + string toSpeak = $"Time is {CurrentPlayer.TimeOfDay} and it is {CurrentPlayer.Day} {CurrentPlayer.Date} of {CurrentPlayer.Season}"; MainClass.ScreenReader.Say(toSpeak, true); return; } diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index d8c0ce8..7fffaef 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -436,7 +436,7 @@ namespace stardew_access.Patches if (cueName == "grassyStep" || cueName == "sandyStep" || cueName == "snowyStep" || cueName == "stoneStep" || cueName == "thudStep" || cueName == "woodyStep") { - Vector2 nextTile = CurrentPlayer.getNextTile(); + Vector2 nextTile = CurrentPlayer.FacingTile; if (TileInfo.isCollidingAtTile((int)nextTile.X, (int)nextTile.Y)) { if (prevTile != nextTile) From 04ba44d0456edf0cc0ea766e247de06590b45562 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Wed, 11 May 2022 13:29:12 +0530 Subject: [PATCH 132/232] Added warnings.cs --- stardew-access/Features/Warnings.cs | 47 +++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 stardew-access/Features/Warnings.cs diff --git a/stardew-access/Features/Warnings.cs b/stardew-access/Features/Warnings.cs new file mode 100644 index 0000000..91c20e4 --- /dev/null +++ b/stardew-access/Features/Warnings.cs @@ -0,0 +1,47 @@ +namespace stardew_access.Features +{ + public class Warnings + { + public void update() + { + this.checkForHealth(); + this.checkForStamina(); + } + + public void checkForStamina() + { + int stamina = CurrentPlayer.Stamina; + + if (stamina <= 50) + { + // 50% stamina warning + } + else if (stamina <= 25) + { + // 25% stamina warning + } + else if (stamina <= 10) + { + // 10% stamina warning + } + } + + public void checkForHealth() + { + int health = CurrentPlayer.Health; + + if (health <= 50) + { + // 50% health warning + } + else if (health <= 25) + { + // 25% health warning + } + else if (health <= 10) + { + // 10% health warning + } + } + } +} \ No newline at end of file From d8dbfe78d4d3e0f58b31fdda867403993df26866 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Wed, 11 May 2022 13:47:19 +0530 Subject: [PATCH 133/232] Added i18n folder for other language support and logic for narrating warnings --- stardew-access/Features/Warnings.cs | 51 +++++++++++-------- .../Patches/BuildingNAnimalMenuPatches.cs | 1 - stardew-access/i18n/default.json | 3 ++ 3 files changed, 32 insertions(+), 23 deletions(-) create mode 100644 stardew-access/i18n/default.json diff --git a/stardew-access/Features/Warnings.cs b/stardew-access/Features/Warnings.cs index 91c20e4..7f3eb74 100644 --- a/stardew-access/Features/Warnings.cs +++ b/stardew-access/Features/Warnings.cs @@ -2,6 +2,15 @@ namespace stardew_access.Features { public class Warnings { + private int prevStamina; + private int prevHealth; + + public Warnings() + { + prevStamina = 100; + prevHealth = 100; + } + public void update() { this.checkForHealth(); @@ -10,38 +19,36 @@ namespace stardew_access.Features public void checkForStamina() { - int stamina = CurrentPlayer.Stamina; + if (MainClass.ModHelper == null) + return; - if (stamina <= 50) + int stamina = CurrentPlayer.Stamina; + string toSpeak = MainClass.ModHelper.Translation.Get("warnings.label", new { type = "stamina", value = stamina }); + + if ((stamina <= 50 && prevStamina > 50) || (stamina <= 25 && prevStamina > 25) || (stamina <= 10 && prevStamina > 10)) { - // 50% stamina warning - } - else if (stamina <= 25) - { - // 25% stamina warning - } - else if (stamina <= 10) - { - // 10% stamina warning + MainClass.DebugLog(toSpeak); + MainClass.ScreenReader.Say(toSpeak, true); } + + prevStamina = stamina; } public void checkForHealth() { - int health = CurrentPlayer.Health; + if (MainClass.ModHelper == null) + return; - if (health <= 50) + int health = CurrentPlayer.Health; + string toSpeak = MainClass.ModHelper.Translation.Get("warnings.label", new { type = "health", value = health }); + + if ((health <= 50 && prevHealth > 50) || (health <= 25 && prevHealth > 25) || (health <= 10 && prevHealth > 10)) { - // 50% health warning - } - else if (health <= 25) - { - // 25% health warning - } - else if (health <= 10) - { - // 10% health warning + MainClass.DebugLog(toSpeak); + MainClass.ScreenReader.Say(toSpeak, true); } + + prevHealth = health; } } } \ No newline at end of file diff --git a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs index b172e95..e314dc9 100644 --- a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs +++ b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs @@ -1,5 +1,4 @@ using Microsoft.Xna.Framework; -using StardewModdingAPI; using StardewValley; using StardewValley.Buildings; using StardewValley.Locations; diff --git a/stardew-access/i18n/default.json b/stardew-access/i18n/default.json new file mode 100644 index 0000000..94c0aaa --- /dev/null +++ b/stardew-access/i18n/default.json @@ -0,0 +1,3 @@ +{ + "warnings.label": "Warning! {{type}} is at {{value}} percent!" +} \ No newline at end of file From 2d7465321712f3e4640b75311c666f5faedde7e3 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 12 May 2022 15:28:09 +0530 Subject: [PATCH 134/232] Read tile pauses when narrating a warning message --- stardew-access/Features/ReadTile.cs | 37 +++++++++++++++++++++--- stardew-access/Features/Warnings.cs | 6 ++-- stardew-access/ModEntry.cs | 44 +++++++++++++++++++++-------- 3 files changed, 69 insertions(+), 18 deletions(-) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 5017cb7..a7a5912 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -6,15 +6,44 @@ namespace stardew_access.Features { public class ReadTile { - public static bool isReadingTile = false; - public static Vector2 prevTile; + private bool isBusy; // To pause execution of run method between fixed intervals + private int delay; // Length of each interval (in ms) + private bool shouldPause; // To pause the execution + private Vector2 prevTile; public ReadTile() { - isReadingTile = false; + isBusy = false; + delay = 100; } - public static void run(bool manuallyTriggered = false, bool playersPosition = false) + public void update() + { + if (this.isBusy) + return; + + if (this.shouldPause) + return; + + if (!MainClass.Config.ReadTile) + return; + + this.isBusy = true; + this.run(); + Task.Delay(delay).ContinueWith(_ => { this.isBusy = false; }); + } + + /// + /// Pauses the read tile for the provided time. + /// + /// The amount of time we want to pause the execution (in ms).
Default is 2500 (2.5s). + public void pause(int time = 2500) + { + this.shouldPause = true; + Task.Delay(time).ContinueWith(_ => { this.shouldPause = false; }); + } + + public void run(bool manuallyTriggered = false, bool playersPosition = false) { try { diff --git a/stardew-access/Features/Warnings.cs b/stardew-access/Features/Warnings.cs index 7f3eb74..da54da7 100644 --- a/stardew-access/Features/Warnings.cs +++ b/stardew-access/Features/Warnings.cs @@ -27,8 +27,9 @@ namespace stardew_access.Features if ((stamina <= 50 && prevStamina > 50) || (stamina <= 25 && prevStamina > 25) || (stamina <= 10 && prevStamina > 10)) { - MainClass.DebugLog(toSpeak); MainClass.ScreenReader.Say(toSpeak, true); + // Pause the read tile feature to prevent interruption in warning message + MainClass.ReadTileFeature.pause(); } prevStamina = stamina; @@ -44,8 +45,9 @@ namespace stardew_access.Features if ((health <= 50 && prevHealth > 50) || (health <= 25 && prevHealth > 25) || (health <= 10 && prevHealth > 10)) { - MainClass.DebugLog(toSpeak); MainClass.ScreenReader.Say(toSpeak, true); + // Pause the read tile feature to prevent interruption in warning message + MainClass.ReadTileFeature.pause(); } prevHealth = health; diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index c79e472..768a365 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -21,6 +21,8 @@ namespace stardew_access private static IScreenReader? screenReader; private static IModHelper? modHelper; private static TileViewer? tileViewer; + private static Warnings? warnings; + private static ReadTile? readTile; internal static ModConfig Config { get => config; set => config = value; } public static IModHelper? ModHelper { get => modHelper; } @@ -64,7 +66,7 @@ namespace stardew_access set => screenReader = value; } - public static TileViewer TileViewer + public static TileViewer TileViewerFeature { get { @@ -74,6 +76,26 @@ namespace stardew_access } } + public static ReadTile ReadTileFeature + { + get + { + if (readTile == null) + readTile = new ReadTile(); + return readTile; + } + } + + public static Warnings WarningsFeature + { + get + { + if (warnings == null) + warnings = new Warnings(); + + return warnings; + } + } #endregion /********* @@ -120,7 +142,8 @@ namespace stardew_access public void OnExit(object? sender, EventArgs? e) { - // Don't if this ever gets called or not but, just in case if it does. + // This closes the connection with the screen reader, important for linux + // Don't know if this ever gets called or not but, just in case if it does. if (ScreenReader != null) ScreenReader.CloseScreenReader(); } @@ -143,14 +166,11 @@ namespace stardew_access Other.narrateCurrentLocation(); //handle TileCursor update logic - TileViewer.update(); + TileViewerFeature.update(); - if (!ReadTile.isReadingTile && Config.ReadTile) - { - ReadTile.isReadingTile = true; - ReadTile.run(); - Task.Delay(100).ContinueWith(_ => { ReadTile.isReadingTile = false; }); - } + WarningsFeature.update(); + + ReadTileFeature.update(); if (!RadarFeature.isRunning && Config.Radar) { @@ -258,19 +278,19 @@ namespace stardew_access // Manual read tile at player's position if (Config.ReadStandingTileKey.JustPressed()) { - ReadTile.run(manuallyTriggered: true, playersPosition: true); + ReadTileFeature.run(manuallyTriggered: true, playersPosition: true); return; } // Manual read tile at looking tile if (Config.ReadTileKey.JustPressed()) { - ReadTile.run(manuallyTriggered: true); + ReadTileFeature.run(manuallyTriggered: true); return; } // Tile viewing cursor keys - TileViewer.HandleInput(); + TileViewerFeature.HandleInput(); } public static void ErrorLog(string message) From 92d5ae22d98e48d85e24bd46a1fd7a88629982fa Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 12 May 2022 15:56:05 +0530 Subject: [PATCH 135/232] Added other languages to warning message --- stardew-access/Features/Warnings.cs | 4 ++-- stardew-access/i18n/de.json | 4 ++++ stardew-access/i18n/default.json | 3 ++- stardew-access/i18n/es.json | 4 ++++ stardew-access/i18n/fr.json | 4 ++++ stardew-access/i18n/hu.json | 4 ++++ stardew-access/i18n/it.json | 4 ++++ stardew-access/i18n/ja.json | 4 ++++ stardew-access/i18n/ko.json | 4 ++++ stardew-access/i18n/pt.json | 4 ++++ stardew-access/i18n/ru.json | 4 ++++ stardew-access/i18n/tr.json | 4 ++++ stardew-access/i18n/zh.json | 4 ++++ 13 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 stardew-access/i18n/de.json create mode 100644 stardew-access/i18n/es.json create mode 100644 stardew-access/i18n/fr.json create mode 100644 stardew-access/i18n/hu.json create mode 100644 stardew-access/i18n/it.json create mode 100644 stardew-access/i18n/ja.json create mode 100644 stardew-access/i18n/ko.json create mode 100644 stardew-access/i18n/pt.json create mode 100644 stardew-access/i18n/ru.json create mode 100644 stardew-access/i18n/tr.json create mode 100644 stardew-access/i18n/zh.json diff --git a/stardew-access/Features/Warnings.cs b/stardew-access/Features/Warnings.cs index da54da7..eb6b3e7 100644 --- a/stardew-access/Features/Warnings.cs +++ b/stardew-access/Features/Warnings.cs @@ -23,7 +23,7 @@ namespace stardew_access.Features return; int stamina = CurrentPlayer.Stamina; - string toSpeak = MainClass.ModHelper.Translation.Get("warnings.label", new { type = "stamina", value = stamina }); + string toSpeak = MainClass.ModHelper.Translation.Get("warnings.stamina", new { value = stamina }); if ((stamina <= 50 && prevStamina > 50) || (stamina <= 25 && prevStamina > 25) || (stamina <= 10 && prevStamina > 10)) { @@ -41,7 +41,7 @@ namespace stardew_access.Features return; int health = CurrentPlayer.Health; - string toSpeak = MainClass.ModHelper.Translation.Get("warnings.label", new { type = "health", value = health }); + string toSpeak = MainClass.ModHelper.Translation.Get("warnings.health", new { value = health }); if ((health <= 50 && prevHealth > 50) || (health <= 25 && prevHealth > 25) || (health <= 10 && prevHealth > 10)) { diff --git a/stardew-access/i18n/de.json b/stardew-access/i18n/de.json new file mode 100644 index 0000000..0130f37 --- /dev/null +++ b/stardew-access/i18n/de.json @@ -0,0 +1,4 @@ +{ + "warnings.health": "Warnung! Die Gesundheit liegt bei {{value}} Prozent!", + "warnings.stamina": "Warnung! Ausdauer beträgt ar {{value}} Prozent!" +} \ No newline at end of file diff --git a/stardew-access/i18n/default.json b/stardew-access/i18n/default.json index 94c0aaa..a12e4dc 100644 --- a/stardew-access/i18n/default.json +++ b/stardew-access/i18n/default.json @@ -1,3 +1,4 @@ { - "warnings.label": "Warning! {{type}} is at {{value}} percent!" + "warnings.health": "Warning! Health is at {{value}} percent!", + "warnings.stamina": "Warning! Stamina is at {{value}} percent!" } \ No newline at end of file diff --git a/stardew-access/i18n/es.json b/stardew-access/i18n/es.json new file mode 100644 index 0000000..fb318d2 --- /dev/null +++ b/stardew-access/i18n/es.json @@ -0,0 +1,4 @@ +{ + "warnings.health": "¡Advertencia! ¡La salud está al {{value}} por ciento!", + "warnings.stamina": "¡Advertencia! ¡La resistencia es un {{value}} por ciento!" +} \ No newline at end of file diff --git a/stardew-access/i18n/fr.json b/stardew-access/i18n/fr.json new file mode 100644 index 0000000..b2ac16f --- /dev/null +++ b/stardew-access/i18n/fr.json @@ -0,0 +1,4 @@ +{ + "warnings.health": "Avertissement! La santé est à {{value}} pour cent!", + "warnings.stamina": "Avertissement! L'endurance est à {{value}} pour cent!" +} \ No newline at end of file diff --git a/stardew-access/i18n/hu.json b/stardew-access/i18n/hu.json new file mode 100644 index 0000000..ba71d1f --- /dev/null +++ b/stardew-access/i18n/hu.json @@ -0,0 +1,4 @@ +{ + "warnings.health": "Figyelem! Az egészségi állapot {{érték}} százalék!", + "warnings.stamina": "Figyelem! Az állóképesség {{value}} százalék!" +} \ No newline at end of file diff --git a/stardew-access/i18n/it.json b/stardew-access/i18n/it.json new file mode 100644 index 0000000..89209c9 --- /dev/null +++ b/stardew-access/i18n/it.json @@ -0,0 +1,4 @@ +{ + "warnings.health": "Avvertimento! La salute è al {{value}} percento!", + "warnings.stamina": "Avvertimento! La resistenza è al {{value}} percento!" +} \ No newline at end of file diff --git a/stardew-access/i18n/ja.json b/stardew-access/i18n/ja.json new file mode 100644 index 0000000..9b9a3ad --- /dev/null +++ b/stardew-access/i18n/ja.json @@ -0,0 +1,4 @@ +{ + "warnings.health": "警告!健康状態は{{value}}パーセントです!", + "warnings.stamina": "警告!スタミナは{{value}}パーセントです!" +} \ No newline at end of file diff --git a/stardew-access/i18n/ko.json b/stardew-access/i18n/ko.json new file mode 100644 index 0000000..044302c --- /dev/null +++ b/stardew-access/i18n/ko.json @@ -0,0 +1,4 @@ +{ + "warnings.health": "경고! 건강은 {{value}}퍼센트입니다!", + "warnings.stamina": "경고! 체력은 {{value}}퍼센트입니다!" +} \ No newline at end of file diff --git a/stardew-access/i18n/pt.json b/stardew-access/i18n/pt.json new file mode 100644 index 0000000..7ea4784 --- /dev/null +++ b/stardew-access/i18n/pt.json @@ -0,0 +1,4 @@ +{ + "warnings.health": "Aviso! A saúde está em {{value}} por cento!", + "warnings.stamina": "Aviso! A resistência está em {{value}} por cento!" +} \ No newline at end of file diff --git a/stardew-access/i18n/ru.json b/stardew-access/i18n/ru.json new file mode 100644 index 0000000..3ff98dc --- /dev/null +++ b/stardew-access/i18n/ru.json @@ -0,0 +1,4 @@ +{ + "warnings.health": "Предупреждение! Здоровье составляет {{value}} процентов!", + "warnings.stamina": "Предупреждение! Выносливость составляет {{value}} процентов!" +} \ No newline at end of file diff --git a/stardew-access/i18n/tr.json b/stardew-access/i18n/tr.json new file mode 100644 index 0000000..d1d86fb --- /dev/null +++ b/stardew-access/i18n/tr.json @@ -0,0 +1,4 @@ +{ + "warnings.health": "Uyarı! Sağlık yüzde {{değer}}!", + "warnings.stamina": "uyarı! Dayanıklılık yüzde {{değer}}!" +} \ No newline at end of file diff --git a/stardew-access/i18n/zh.json b/stardew-access/i18n/zh.json new file mode 100644 index 0000000..76ccb50 --- /dev/null +++ b/stardew-access/i18n/zh.json @@ -0,0 +1,4 @@ +{ + "warnings.health": "警告!健康状况为 {{value}} 百分!", + "warnings.stamina": "警告!耐力为 {{value}} 百分!" +} \ No newline at end of file From ebc68f0ffa37aa58864b3301911a4c7b98630f59 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 12 May 2022 16:00:20 +0530 Subject: [PATCH 136/232] Added pause option to config It now pauses in hud messages --- stardew-access/Features/Other.cs | 2 ++ stardew-access/Features/ReadTile.cs | 3 +++ stardew-access/ModConfig.cs | 1 + 3 files changed, 6 insertions(+) diff --git a/stardew-access/Features/Other.cs b/stardew-access/Features/Other.cs index 826c01d..6a7b9ff 100644 --- a/stardew-access/Features/Other.cs +++ b/stardew-access/Features/Other.cs @@ -63,6 +63,8 @@ namespace stardew_access.Features MainClass.hudMessageQueryKey = searchQuery; MainClass.ScreenReader.Say(toSpeak, true); + // Pause to avoid interruption + MainClass.ReadTileFeature.pause(); } } } diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index a7a5912..3c76011 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -39,6 +39,9 @@ namespace stardew_access.Features /// The amount of time we want to pause the execution (in ms).
Default is 2500 (2.5s). public void pause(int time = 2500) { + if (!MainClass.Config.ReadTileAllowPausing) + return; + this.shouldPause = true; Task.Delay(time).ContinueWith(_ => { this.shouldPause = false; }); } diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index 479c25d..40f41fb 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -6,6 +6,7 @@ namespace stardew_access { public Boolean VerboseCoordinates { get; set; } = true; public Boolean ReadTile { get; set; } = true; + public Boolean ReadTileAllowPausing { get; set; } = true; public Boolean SnapMouse { get; set; } = true; public Boolean Radar { get; set; } = false; public Boolean RadarStereoSound { get; set; } = true; From f8a6cab24e08eba53a9b88b57abe5dd488ccb90c Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 12 May 2022 21:47:01 +0530 Subject: [PATCH 137/232] Added missing entrances --- stardew-access/assets/static-tiles.json | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 05fe015..8d54578 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -66,6 +66,12 @@ "x":[34], "y":[23], "type":"door" + }, + "Backwoods Entrance": + { + "x":[0], + "y":[6,7,8,9], + "type":"door" } }, "town": @@ -125,6 +131,15 @@ "type":"door" } }, + "shed": + { + "Exit": + { + "x":[6], + "y":[13], + "type":"door" + } + }, "coop": { "Hay Hopper": @@ -1788,6 +1803,27 @@ "x":[14], "y":[39], "type":"door" + }, + "Bus stop Entrance": + { + "x":[49], + "y":[28,29,30,31,32], + "type":"door" + }, + "Tunnel Entrance": + { + "x":[23], + "y":[29,30,31], + "type":"door" + } + }, + "tunnel": + { + "Exit": + { + "x":[39], + "y":[7,8,9,10,11], + "type":"door" } }, "movietheater": From 92b7230a4dfec08c5a6fcc910806b564cd84aa92 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 12 May 2022 22:54:33 +0530 Subject: [PATCH 138/232] Fix for stone types --- stardew-access/Features/TileInfo.cs | 72 +++++++++++++++-------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index a63381c..716d9c8 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -644,7 +644,7 @@ namespace stardew_access.Features toReturn.name = obj.DisplayName; // Get object names based on index - (string? name, CATEGORY category) correctNameAndCategory = getCorrectNameAndCategoryFromIndex(index); + (string? name, CATEGORY category) correctNameAndCategory = getCorrectNameAndCategoryFromIndex(index, obj.Name); if (correctNameAndCategory.name != null) toReturn = correctNameAndCategory; @@ -716,7 +716,7 @@ namespace stardew_access.Features return MachineState.Waiting; } - private static (string? name, CATEGORY category) getCorrectNameAndCategoryFromIndex(int index) + private static (string? name, CATEGORY category) getCorrectNameAndCategoryFromIndex(int index, string objName) { switch (index) { @@ -746,32 +746,6 @@ namespace stardew_access.Features case 320: case 321: return ("Ice crystal", CATEGORY.Debris); - case 75: - return ("Geode", CATEGORY.MineItems); - case 32: - case 34: - case 36: - case 38: - case 40: - case 42: - case 48: - case 50: - case 52: - case 54: - case 56: - case 58: - return ("Coloured stone", CATEGORY.Debris); - case 668: - case 670: - case 845: - case 846: - case 847: - return ("Mine stone", CATEGORY.MineItems); - case 818: - return ("Clay stone", CATEGORY.Debris); - case 816: - case 817: - return ("Fossil stone", CATEGORY.Debris); case 118: case 120: case 122: @@ -784,7 +758,7 @@ namespace stardew_access.Features return ("Item box", CATEGORY.MineItems); } - if (Game1.currentLocation is Mine or MineShaft) + if (objName.ToLower().Contains("stone")) { switch (index) { @@ -792,15 +766,47 @@ namespace stardew_access.Features return ("Frozen geode", CATEGORY.MineItems); case 77: return ("Magma geode", CATEGORY.MineItems); + case 75: + return ("Geode", CATEGORY.MineItems); + case 819: + return ("Omni geode node", CATEGORY.MineItems); + case 32: + case 34: + case 36: + case 38: + case 40: + case 42: + case 48: + case 50: + case 52: + case 54: + case 56: + case 58: + return ("Coloured stone", CATEGORY.Debris); + case 668: + case 670: + case 845: + case 846: + case 847: + return ("Mine stone", CATEGORY.MineItems); + case 818: + return ("Clay stone", CATEGORY.Debris); + case 816: + case 817: + return ("Fossil stone", CATEGORY.Debris); + case 25: + return ("Mussel Node", CATEGORY.MineItems); + case 95: + return ("Radioactive Node", CATEGORY.MineItems); + case 843: + case 844: + return ("Cinder shard node", CATEGORY.MineItems); case 8: case 66: return ("Amethyst node", CATEGORY.MineItems); case 14: case 62: return ("Aquamarine node", CATEGORY.MineItems); - case 843: - case 844: - return ("Cinder shard node", CATEGORY.MineItems); case 2: case 72: return ("Diamond node", CATEGORY.MineItems); @@ -822,8 +828,6 @@ namespace stardew_access.Features case 10: case 68: return ("Topaz node", CATEGORY.MineItems); - case 819: - return ("Omni geode node", CATEGORY.MineItems); case 751: case 849: return ("Copper node", CATEGORY.MineItems); From 6c04ba7bf4a715a720886b7c7526cc21184c89f9 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 12 May 2022 22:58:42 +0530 Subject: [PATCH 139/232] Removed pause from hud message as it was pausing too much --- stardew-access/Features/Other.cs | 2 -- stardew-access/Features/ReadTile.cs | 3 --- stardew-access/ModConfig.cs | 1 - 3 files changed, 6 deletions(-) diff --git a/stardew-access/Features/Other.cs b/stardew-access/Features/Other.cs index 6a7b9ff..826c01d 100644 --- a/stardew-access/Features/Other.cs +++ b/stardew-access/Features/Other.cs @@ -63,8 +63,6 @@ namespace stardew_access.Features MainClass.hudMessageQueryKey = searchQuery; MainClass.ScreenReader.Say(toSpeak, true); - // Pause to avoid interruption - MainClass.ReadTileFeature.pause(); } } } diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 3c76011..a7a5912 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -39,9 +39,6 @@ namespace stardew_access.Features /// The amount of time we want to pause the execution (in ms).
Default is 2500 (2.5s). public void pause(int time = 2500) { - if (!MainClass.Config.ReadTileAllowPausing) - return; - this.shouldPause = true; Task.Delay(time).ContinueWith(_ => { this.shouldPause = false; }); } diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index 40f41fb..479c25d 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -6,7 +6,6 @@ namespace stardew_access { public Boolean VerboseCoordinates { get; set; } = true; public Boolean ReadTile { get; set; } = true; - public Boolean ReadTileAllowPausing { get; set; } = true; public Boolean SnapMouse { get; set; } = true; public Boolean Radar { get; set; } = false; public Boolean RadarStereoSound { get; set; } = true; From d74ca682eb5c44dac5f1ca474f95b6a803ab60e5 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Thu, 12 May 2022 23:22:59 +0530 Subject: [PATCH 140/232] Added time warning --- stardew-access/Features/Warnings.cs | 21 +++++++++++++++++++++ stardew-access/i18n/de.json | 3 ++- stardew-access/i18n/default.json | 3 ++- stardew-access/i18n/es.json | 3 ++- stardew-access/i18n/fr.json | 3 ++- stardew-access/i18n/hu.json | 3 ++- stardew-access/i18n/it.json | 3 ++- stardew-access/i18n/ja.json | 3 ++- stardew-access/i18n/ko.json | 3 ++- stardew-access/i18n/pt.json | 3 ++- stardew-access/i18n/ru.json | 3 ++- stardew-access/i18n/tr.json | 3 ++- stardew-access/i18n/zh.json | 3 ++- 13 files changed, 45 insertions(+), 12 deletions(-) diff --git a/stardew-access/Features/Warnings.cs b/stardew-access/Features/Warnings.cs index eb6b3e7..c88d158 100644 --- a/stardew-access/Features/Warnings.cs +++ b/stardew-access/Features/Warnings.cs @@ -4,17 +4,38 @@ namespace stardew_access.Features { private int prevStamina; private int prevHealth; + private int prevHour; public Warnings() { prevStamina = 100; prevHealth = 100; + prevHour = 6; } public void update() { this.checkForHealth(); this.checkForStamina(); + this.checkForTimeOfDay(); + } + + private void checkForTimeOfDay() + { + if (MainClass.ModHelper == null) + return; + + int hours = StardewValley.Game1.timeOfDay / 100; + string toSpeak = MainClass.ModHelper.Translation.Get("warnings.time", new { value = CurrentPlayer.TimeOfDay }); + + if (hours < 1 && prevHour > 2 || hours >= 1 && prevHour < 1) + { + MainClass.ScreenReader.Say(toSpeak, true); + // Pause the read tile feature to prevent interruption in warning message + MainClass.ReadTileFeature.pause(); + } + + prevHour = hours; } public void checkForStamina() diff --git a/stardew-access/i18n/de.json b/stardew-access/i18n/de.json index 0130f37..bdf53da 100644 --- a/stardew-access/i18n/de.json +++ b/stardew-access/i18n/de.json @@ -1,4 +1,5 @@ { "warnings.health": "Warnung! Die Gesundheit liegt bei {{value}} Prozent!", - "warnings.stamina": "Warnung! Ausdauer beträgt ar {{value}} Prozent!" + "warnings.stamina": "Warnung! Ausdauer beträgt ar {{value}} Prozent!", + "warnings.time": "Warnung! Zeit ist {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/default.json b/stardew-access/i18n/default.json index a12e4dc..071bf62 100644 --- a/stardew-access/i18n/default.json +++ b/stardew-access/i18n/default.json @@ -1,4 +1,5 @@ { "warnings.health": "Warning! Health is at {{value}} percent!", - "warnings.stamina": "Warning! Stamina is at {{value}} percent!" + "warnings.stamina": "Warning! Stamina is at {{value}} percent!", + "warnings.time": "Warning! Time is {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/es.json b/stardew-access/i18n/es.json index fb318d2..d833a17 100644 --- a/stardew-access/i18n/es.json +++ b/stardew-access/i18n/es.json @@ -1,4 +1,5 @@ { "warnings.health": "¡Advertencia! ¡La salud está al {{value}} por ciento!", - "warnings.stamina": "¡Advertencia! ¡La resistencia es un {{value}} por ciento!" + "warnings.stamina": "¡Advertencia! ¡La resistencia es un {{value}} por ciento!", + "warnings.time": "¡Advertencia! El tiempo es {{valor}}" } \ No newline at end of file diff --git a/stardew-access/i18n/fr.json b/stardew-access/i18n/fr.json index b2ac16f..102cff8 100644 --- a/stardew-access/i18n/fr.json +++ b/stardew-access/i18n/fr.json @@ -1,4 +1,5 @@ { "warnings.health": "Avertissement! La santé est à {{value}} pour cent!", - "warnings.stamina": "Avertissement! L'endurance est à {{value}} pour cent!" + "warnings.stamina": "Avertissement! L'endurance est à {{value}} pour cent!", + "warnings.time": "Avertissement! Le temps est de {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/hu.json b/stardew-access/i18n/hu.json index ba71d1f..a62e032 100644 --- a/stardew-access/i18n/hu.json +++ b/stardew-access/i18n/hu.json @@ -1,4 +1,5 @@ { "warnings.health": "Figyelem! Az egészségi állapot {{érték}} százalék!", - "warnings.stamina": "Figyelem! Az állóképesség {{value}} százalék!" + "warnings.stamina": "Figyelem! Az állóképesség {{value}} százalék!", + "warnings.time": "Figyelem! Az idő {{érték}}" } \ No newline at end of file diff --git a/stardew-access/i18n/it.json b/stardew-access/i18n/it.json index 89209c9..b756cf8 100644 --- a/stardew-access/i18n/it.json +++ b/stardew-access/i18n/it.json @@ -1,4 +1,5 @@ { "warnings.health": "Avvertimento! La salute è al {{value}} percento!", - "warnings.stamina": "Avvertimento! La resistenza è al {{value}} percento!" + "warnings.stamina": "Avvertimento! La resistenza è al {{value}} percento!", + "warnings.time": "Avvertimento! L'ora è {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/ja.json b/stardew-access/i18n/ja.json index 9b9a3ad..112a9c2 100644 --- a/stardew-access/i18n/ja.json +++ b/stardew-access/i18n/ja.json @@ -1,4 +1,5 @@ { "warnings.health": "警告!健康状態は{{value}}パーセントです!", - "warnings.stamina": "警告!スタミナは{{value}}パーセントです!" + "warnings.stamina": "警告!スタミナは{{value}}パーセントです!", + "warnings.time": "警告!時間は{{value}}です" } \ No newline at end of file diff --git a/stardew-access/i18n/ko.json b/stardew-access/i18n/ko.json index 044302c..1ddc247 100644 --- a/stardew-access/i18n/ko.json +++ b/stardew-access/i18n/ko.json @@ -1,4 +1,5 @@ { "warnings.health": "경고! 건강은 {{value}}퍼센트입니다!", - "warnings.stamina": "경고! 체력은 {{value}}퍼센트입니다!" + "warnings.stamina": "경고! 체력은 {{value}}퍼센트입니다!", + "warnings.time": "경고! 시간은 {{value}}입니다" } \ No newline at end of file diff --git a/stardew-access/i18n/pt.json b/stardew-access/i18n/pt.json index 7ea4784..ecb88f7 100644 --- a/stardew-access/i18n/pt.json +++ b/stardew-access/i18n/pt.json @@ -1,4 +1,5 @@ { "warnings.health": "Aviso! A saúde está em {{value}} por cento!", - "warnings.stamina": "Aviso! A resistência está em {{value}} por cento!" + "warnings.stamina": "Aviso! A resistência está em {{value}} por cento!", + "warnings.time": "Aviso! O tempo é {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/ru.json b/stardew-access/i18n/ru.json index 3ff98dc..2c03e06 100644 --- a/stardew-access/i18n/ru.json +++ b/stardew-access/i18n/ru.json @@ -1,4 +1,5 @@ { "warnings.health": "Предупреждение! Здоровье составляет {{value}} процентов!", - "warnings.stamina": "Предупреждение! Выносливость составляет {{value}} процентов!" + "warnings.stamina": "Предупреждение! Выносливость составляет {{value}} процентов!", + "warnings.time": "Предупреждение! Время {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/tr.json b/stardew-access/i18n/tr.json index d1d86fb..72f2597 100644 --- a/stardew-access/i18n/tr.json +++ b/stardew-access/i18n/tr.json @@ -1,4 +1,5 @@ { "warnings.health": "Uyarı! Sağlık yüzde {{değer}}!", - "warnings.stamina": "uyarı! Dayanıklılık yüzde {{değer}}!" + "warnings.stamina": "uyarı! Dayanıklılık yüzde {{değer}}!", + "warnings.time": "Uyarı! Zaman {{değer}}" } \ No newline at end of file diff --git a/stardew-access/i18n/zh.json b/stardew-access/i18n/zh.json index 76ccb50..2eccfdc 100644 --- a/stardew-access/i18n/zh.json +++ b/stardew-access/i18n/zh.json @@ -1,4 +1,5 @@ { "warnings.health": "警告!健康状况为 {{value}} 百分!", - "warnings.stamina": "警告!耐力为 {{value}} 百分!" + "warnings.stamina": "警告!耐力为 {{value}} 百分!", + "warnings.time": "警告!时间是 {{value}}" } \ No newline at end of file From 569311c61e68f2e1b08926d3d093c1709746bf12 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Thu, 12 May 2022 20:04:33 -0400 Subject: [PATCH 141/232] Fix incorrect AM and PM time announcement and :00 formatting. --- stardew-access/Features/CurrentPlayer.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/stardew-access/Features/CurrentPlayer.cs b/stardew-access/Features/CurrentPlayer.cs index 7a2f525..5331576 100644 --- a/stardew-access/Features/CurrentPlayer.cs +++ b/stardew-access/Features/CurrentPlayer.cs @@ -96,15 +96,10 @@ namespace stardew_access.Features int minutes = timeOfDay % 100; int hours = timeOfDay / 100; - string amOrpm = "A M"; - if (hours >= 12) - { - amOrpm = "P M"; - if (hours > 12) - hours -= 12; - } - - return $"{hours}:{minutes} {amOrpm}"; + string amOrpm = hours / 12 == 1 ? "PM" : "AM"; + hours = hours % 12; + if (hours == 0) hours = 12; + return $"{hours}:{minutes:00} {amOrpm}"; } } From 153b1207301a95a91e4ffc59f6f94e8d09f2142e Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Thu, 12 May 2022 20:05:01 -0400 Subject: [PATCH 142/232] Added old mariner to npcs category when present. --- stardew-access/Features/TileInfo.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 716d9c8..665a836 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -47,16 +47,15 @@ namespace stardew_access.Features (string? name, CATEGORY category) staticTile = MainClass.STiles.getStaticTileInfoAtWithCategory(x, y); string? bush = getBushAtTile(x, y, lessInfo); - if (Game1.currentLocation.isCharacterAtTile(tile) != null) + if (Game1.currentLocation.isCharacterAtTile(tile) is NPC npc) { - NPC npc = Game1.currentLocation.isCharacterAtTile(tile); - toReturn = npc.displayName; + toReturn = npc.displayName; if (npc.isVillager() || npc.CanSocialize) category = CATEGORY.Farmers; else category = CATEGORY.NPCs; } - else if (farmAnimal != null) + else if (farmAnimal != null) { toReturn = farmAnimal; category = CATEGORY.FarmAnimals; @@ -408,7 +407,11 @@ namespace stardew_access.Features } else if (Game1.currentLocation is Beach beach) { - if (x == 58 && y == 13) + if (MainClass.ModHelper.Reflection.GetField(beach, "oldMariner").GetValue() is NPC mariner && mariner.getTileLocation() == new Vector2(x, y)) + { + return (CATEGORY.NPCs, "Old Mariner"); + } + else if (x == 58 && y == 13) { if (!beach.bridgeFixed.Value) return (CATEGORY.Interactables, "Repair Bridge"); From f4a47bf64a69f8b20db4da4506d1fe1afe65a860 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Thu, 12 May 2022 20:33:50 -0400 Subject: [PATCH 143/232] Added ore panning spot to interactables category. --- stardew-access/Features/TileInfo.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 665a836..5c13689 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -49,13 +49,13 @@ namespace stardew_access.Features if (Game1.currentLocation.isCharacterAtTile(tile) is NPC npc) { - toReturn = npc.displayName; + toReturn = npc.displayName; if (npc.isVillager() || npc.CanSocialize) category = CATEGORY.Farmers; else category = CATEGORY.NPCs; } - else if (farmAnimal != null) + else if (farmAnimal != null) { toReturn = farmAnimal; category = CATEGORY.FarmAnimals; @@ -300,7 +300,11 @@ namespace stardew_access.Features ///
name: This is the name of the tile. Default to null if the tile tile has nothing on it. public static (CATEGORY? category, string? name) getDynamicTilesInfo(int x, int y, bool lessInfo = false) { - if (Game1.currentLocation is Farm farm) + if (Game1.currentLocation.orePanPoint != Point.Zero && Game1.currentLocation.orePanPoint == new Point(x, y)) + { + return (CATEGORY.Interactables, "panning spot"); + } + else if (Game1.currentLocation is Farm farm) { if (farm.GetMainMailboxPosition().X == x && farm.GetMainMailboxPosition().Y == y) return (CATEGORY.Interactables, "Mail box"); @@ -407,7 +411,7 @@ namespace stardew_access.Features } else if (Game1.currentLocation is Beach beach) { - if (MainClass.ModHelper.Reflection.GetField(beach, "oldMariner").GetValue() is NPC mariner && mariner.getTileLocation() == new Vector2(x, y)) + if (MainClass.ModHelper.Reflection.GetField(beach, "oldMariner").GetValue() is NPC mariner && mariner.getTileLocation() == new Vector2(x, y)) { return (CATEGORY.NPCs, "Old Mariner"); } From e4187a636abfbaebc03b64149cf42601967cb0e5 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Sun, 15 May 2022 00:22:36 -0400 Subject: [PATCH 144/232] Ginger Island gem birds added to TileInfo npcs category. --- stardew-access/Features/TileInfo.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 5c13689..2a8ecfc 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -444,6 +444,10 @@ namespace stardew_access.Features { return (CATEGORY.Interactables, "Diggable spot"); } + else if (islandLocation.locationGemBird.Value is IslandGemBird bird && ((int)bird.position.X / Game1.tileSize) == x && ((int)bird.position.Y / Game1.tileSize) == y) + { + return (CATEGORY.NPCs, GetGemBirdName(bird)); + } else if (Game1.currentLocation is IslandWest islandWest) { if (islandWest.shippingBinPosition.X == x && islandWest.shippingBinPosition.Y == y) @@ -852,6 +856,19 @@ namespace stardew_access.Features } #endregion + public static String GetGemBirdName(IslandGemBird bird) + { + return bird.itemIndex.Value switch + { + 60 => "Emerald Gem Bird", + 62 => "Aquamarine Gem Bird", + 64 => "Ruby Gem Bird", + 66 => "Amethyst Gem Bird", + 68 => "Topaz", + _ => "Gem Bird", + }; + } + public static bool isMineDownLadderAtTile(int x, int y) { try From 9e0c2dfab7538bd8eb6f0ca44fc089b3ce712020 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Sun, 15 May 2022 01:43:39 -0400 Subject: [PATCH 145/232] Fix incorrectly named Topaz Gem Bird. --- stardew-access/Features/TileInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 2a8ecfc..70c1ece 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -864,7 +864,7 @@ namespace stardew_access.Features 62 => "Aquamarine Gem Bird", 64 => "Ruby Gem Bird", 66 => "Amethyst Gem Bird", - 68 => "Topaz", + 68 => "Topaz Gem Bird", _ => "Gem Bird", }; } From c8b274347e1af772832bb9214d6e23a3bd481fe8 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 15 May 2022 11:30:28 +0530 Subject: [PATCH 146/232] Added auto walk feature to tile viewer --- stardew-access/Features/TileViewer.cs | 26 ++++++++++++++++++++++++++ stardew-access/ModConfig.cs | 1 + 2 files changed, 27 insertions(+) diff --git a/stardew-access/Features/TileViewer.cs b/stardew-access/Features/TileViewer.cs index 2b07fde..60acdef 100644 --- a/stardew-access/Features/TileViewer.cs +++ b/stardew-access/Features/TileViewer.cs @@ -20,6 +20,8 @@ namespace stardew_access.Features private Vector2 relativeOffsetLockPosition = Vector2.Zero; private Boolean relativeOffsetLock = false; private Vector2 prevPlayerPosition = Vector2.Zero, prevFacing = Vector2.Zero; + private Boolean isAutoWalking = false; + private Vector2 finalTile = Vector2.Zero; private Vector2 PlayerFacingVector { @@ -129,6 +131,22 @@ namespace stardew_access.Features { this.cursorMoveInput(new Vector2(-Game1.tileSize, 0)); } + else if (MainClass.Config.AutoWalkToTile.JustPressed() && StardewModdingAPI.Context.IsPlayerFree) + { + PathFindController controller = new PathFindController(Game1.player, Game1.currentLocation, this.GetViewingTile().ToPoint(), Game1.player.FacingDirection); + controller.allowPlayerPathingInEvent = true; + if (controller.pathToEndPoint != null && controller.pathToEndPoint.Count > 0) + { + Game1.player.controller = controller; + this.isAutoWalking = true; + this.finalTile = this.GetViewingTile(); + MainClass.ScreenReader.Say($"Moving to {this.finalTile.X}x {this.finalTile.Y}y", true); + } + else + { + MainClass.ScreenReader.Say($"Cannot move to {this.finalTile.X}x {this.finalTile.Y}y", true); + } + } } private void cursorMoveInput(Vector2 delta, Boolean precise = false) @@ -200,6 +218,14 @@ namespace stardew_access.Features this.prevPlayerPosition = this.PlayerPosition; if (MainClass.Config.SnapMouse) this.SnapMouseToPlayer(); + + if (this.isAutoWalking && this.finalTile != Vector2.Zero && this.finalTile == CurrentPlayer.Position) + { + MainClass.ScreenReader.Say("Reached destination", true); + this.finalTile = Vector2.Zero; + this.isAutoWalking = false; + } + } private static bool allowMouseSnap(Vector2 point) diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index 479c25d..7b22ddc 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -38,6 +38,7 @@ namespace stardew_access public KeybindList TileCursorPreciseDownKey { get; set; } = KeybindList.Parse("LeftShift + Down"); public KeybindList TileCursorPreciseLeftKey { get; set; } = KeybindList.Parse("LeftShift + Left"); public KeybindList ToggleRelativeCursorLockKey { get; set; } = KeybindList.Parse("L"); + public KeybindList AutoWalkToTile { get; set; } = KeybindList.Parse("LeftControl + Enter"); #endregion // TODO Add the exclusion and focus list too From cb49832b5afc71693726a611746e2ddb8ea13bdd Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 15 May 2022 11:47:48 +0530 Subject: [PATCH 147/232] Player stops auto walking if WASD is pressed --- stardew-access/Features/TileViewer.cs | 57 ++++++++++++++++++--------- stardew-access/ModEntry.cs | 10 +++++ 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/stardew-access/Features/TileViewer.cs b/stardew-access/Features/TileViewer.cs index 60acdef..c63e0cd 100644 --- a/stardew-access/Features/TileViewer.cs +++ b/stardew-access/Features/TileViewer.cs @@ -20,9 +20,10 @@ namespace stardew_access.Features private Vector2 relativeOffsetLockPosition = Vector2.Zero; private Boolean relativeOffsetLock = false; private Vector2 prevPlayerPosition = Vector2.Zero, prevFacing = Vector2.Zero; - private Boolean isAutoWalking = false; private Vector2 finalTile = Vector2.Zero; + public Boolean isAutoWalking = false; + private Vector2 PlayerFacingVector { get @@ -133,22 +134,40 @@ namespace stardew_access.Features } else if (MainClass.Config.AutoWalkToTile.JustPressed() && StardewModdingAPI.Context.IsPlayerFree) { - PathFindController controller = new PathFindController(Game1.player, Game1.currentLocation, this.GetViewingTile().ToPoint(), Game1.player.FacingDirection); - controller.allowPlayerPathingInEvent = true; - if (controller.pathToEndPoint != null && controller.pathToEndPoint.Count > 0) - { - Game1.player.controller = controller; - this.isAutoWalking = true; - this.finalTile = this.GetViewingTile(); - MainClass.ScreenReader.Say($"Moving to {this.finalTile.X}x {this.finalTile.Y}y", true); - } - else - { - MainClass.ScreenReader.Say($"Cannot move to {this.finalTile.X}x {this.finalTile.Y}y", true); - } + this.startAutoWalking(); } } + private void startAutoWalking() + { + PathFindController controller = new PathFindController(Game1.player, Game1.currentLocation, this.GetViewingTile().ToPoint(), Game1.player.FacingDirection); + controller.allowPlayerPathingInEvent = true; + if (controller.pathToEndPoint != null && controller.pathToEndPoint.Count > 0) + { + Game1.player.controller = controller; + this.isAutoWalking = true; + this.finalTile = this.GetViewingTile(); + MainClass.ScreenReader.Say($"Moving to {this.finalTile.X}x {this.finalTile.Y}y", true); + } + else + { + MainClass.ScreenReader.Say($"Cannot move to {this.finalTile.X}x {this.finalTile.Y}y", true); + } + } + + /// + /// Stop the auto walk controller and reset variables + /// + /// Narrates a message if set to true. + public void stopAutoWalking(bool wasForced = false) + { + this.finalTile = Vector2.Zero; + this.isAutoWalking = false; + Game1.player.controller = null; + if (wasForced) + MainClass.ScreenReader.Say("Stopped moving", true); + } + private void cursorMoveInput(Vector2 delta, Boolean precise = false) { if (!tryMoveTileView(delta)) return; @@ -219,11 +238,13 @@ namespace stardew_access.Features if (MainClass.Config.SnapMouse) this.SnapMouseToPlayer(); - if (this.isAutoWalking && this.finalTile != Vector2.Zero && this.finalTile == CurrentPlayer.Position) + if (this.isAutoWalking) { - MainClass.ScreenReader.Say("Reached destination", true); - this.finalTile = Vector2.Zero; - this.isAutoWalking = false; + if (this.finalTile != Vector2.Zero && this.finalTile == CurrentPlayer.Position) + { + MainClass.ScreenReader.Say("Reached destination", true); + this.stopAutoWalking(); + } } } diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 768a365..4579a36 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -226,6 +226,16 @@ namespace stardew_access if (!Context.IsPlayerFree) return; + // Stops the auto walk controller if any movement key(WASD) is pressed + if (TileViewerFeature.isAutoWalking && + (e.Button.Equals(SButtonExtensions.ToSButton(Game1.options.moveUpButton[0])) + || e.Button.Equals(SButtonExtensions.ToSButton(Game1.options.moveDownButton[0])) + || e.Button.Equals(SButtonExtensions.ToSButton(Game1.options.moveLeftButton[0])) + || e.Button.Equals(SButtonExtensions.ToSButton(Game1.options.moveRightButton[0])))) + { + TileViewerFeature.stopAutoWalking(wasForced: true); + } + // Narrate Current Location if (Config.LocationKey.JustPressed()) { From 715601f6941a193f629496ad391c61e50a97ec7d Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 15 May 2022 12:17:41 +0530 Subject: [PATCH 148/232] Added footstep sounds to auto walk --- stardew-access/Features/TileViewer.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stardew-access/Features/TileViewer.cs b/stardew-access/Features/TileViewer.cs index c63e0cd..64c56e4 100644 --- a/stardew-access/Features/TileViewer.cs +++ b/stardew-access/Features/TileViewer.cs @@ -21,6 +21,7 @@ namespace stardew_access.Features private Boolean relativeOffsetLock = false; private Vector2 prevPlayerPosition = Vector2.Zero, prevFacing = Vector2.Zero; private Vector2 finalTile = Vector2.Zero; + private Vector2 prevTile = Vector2.Zero; public Boolean isAutoWalking = false; @@ -240,6 +241,12 @@ namespace stardew_access.Features if (this.isAutoWalking) { + if (Vector2.Distance(this.prevTile, CurrentPlayer.Position) >= 2f) + { + prevTile = CurrentPlayer.Position; + Game1.player.checkForFootstep(); + } + if (this.finalTile != Vector2.Zero && this.finalTile == CurrentPlayer.Position) { MainClass.ScreenReader.Say("Reached destination", true); From 373a39fa3384b948cfba3fd9cc3a59d2c22a45f9 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 15 May 2022 12:25:27 +0530 Subject: [PATCH 149/232] Added pet's water bowl --- stardew-access/assets/static-tiles.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 8d54578..599bff2 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -30,6 +30,12 @@ "x":[8], "y":[7], "type":"interactable" + }, + "Water bowl": + { + "x":[54], + "y":[7], + "type":"interactable" } }, "farmcave": From 4dcef714fa77b85bab35f49393edc5c24b5ba560 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 15 May 2022 12:30:52 +0530 Subject: [PATCH 150/232] Fixed warning --- stardew-access/Features/TileInfo.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 70c1ece..4c161f9 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -411,6 +411,9 @@ namespace stardew_access.Features } else if (Game1.currentLocation is Beach beach) { + if (MainClass.ModHelper == null) + return (null, null); + if (MainClass.ModHelper.Reflection.GetField(beach, "oldMariner").GetValue() is NPC mariner && mariner.getTileLocation() == new Vector2(x, y)) { return (CATEGORY.NPCs, "Old Mariner"); From f9dd84efc8ad29d95036ce982aeb668a6f81c9ea Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 15 May 2022 13:28:20 +0530 Subject: [PATCH 151/232] Patched grandpa story and intro mini game --- stardew-access/HarmonyPatches.cs | 13 +++ stardew-access/Patches/MenuPatches.cs | 3 - stardew-access/Patches/MiniGamesPatches.cs | 127 +++++++++++++++++++++ 3 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 stardew-access/Patches/MiniGamesPatches.cs diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index ae60975..1250a4a 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -5,6 +5,7 @@ using Microsoft.Xna.Framework.Input; using stardew_access.Patches; using StardewValley; using StardewValley.Menus; +using StardewValley.Minigames; namespace stardew_access { @@ -253,6 +254,18 @@ namespace stardew_access ); #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)) + ); + + harmony.Patch( + original: AccessTools.Method(typeof(GrandpaStory), nameof(GrandpaStory.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(MiniGamesPatches), nameof(MiniGamesPatches.GrandpaStoryPatch)) + ); + #endregion + harmony.Patch( original: AccessTools.Method(typeof(Game1), nameof(Game1.playSound)), prefix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.PlaySoundPatch)) diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 7fffaef..e1abd64 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -1,12 +1,9 @@ using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Input; using stardew_access.Features; using StardewModdingAPI; using StardewValley; using StardewValley.Buildings; -using StardewValley.Locations; using StardewValley.Menus; -using StardewValley.Objects; namespace stardew_access.Patches { diff --git a/stardew-access/Patches/MiniGamesPatches.cs b/stardew-access/Patches/MiniGamesPatches.cs new file mode 100644 index 0000000..938b98b --- /dev/null +++ b/stardew-access/Patches/MiniGamesPatches.cs @@ -0,0 +1,127 @@ +using Microsoft.Xna.Framework; +using StardewValley; +using StardewValley.Minigames; + +namespace stardew_access.Patches +{ + public class MiniGamesPatches + { + public static string grandpaStoryQuery = " "; + + internal static void IntroPatch(Intro __instance, int ___currentState) + { + try + { + MainClass.DebugLog(___currentState + "\t intro"); + if (___currentState == 3) + { + string text = "Travelling to Stardew Valley bus stop"; + if (grandpaStoryQuery != text) + { + grandpaStoryQuery = text; + MainClass.ScreenReader.Say(text, true); + return; + } + } + if (___currentState == 4) + { + string text = "Stardew valley 0.5 miles away"; + if (grandpaStoryQuery != text) + { + grandpaStoryQuery = text; + MainClass.ScreenReader.Say(text, true); + 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) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + + // TODO add scene 0 explaination + // if(___scene == 0) + // { + // + // } + + if (___drawGrandpa) + { + if (___grandpaSpeech.Count > 0 && ___grandpaSpeechTimer > 3000) + { + string text = ___grandpaSpeech.Peek(); + if (grandpaStoryQuery != text) + { + grandpaStoryQuery = text; + MainClass.ScreenReader.Say(text, true); + return; + } + } + } + if (___scene == 3) + { + string text = Game1.content.LoadString("Strings\\StringsFromCSFiles:GrandpaStory.cs.12059"); + if (grandpaStoryQuery != text) + { + grandpaStoryQuery = text; + MainClass.ScreenReader.Say(text, true); + return; + } + } + + // TODO add scene 4 & 5 explaination + // if(___scene == 4) + // { + // + // } + // if(___scene == 5) + // { + // + // } + + if (___scene == 6) + { + if (___grandpaSpeechTimer > 3000) + { + if (clickableGrandpaLetterRect(___parallaxPan, ___grandpaSpeechTimer).Contains(x, y)) + { + string text = "Left click to open grandpa's letter"; + if (grandpaStoryQuery != text) + { + grandpaStoryQuery = text; + MainClass.ScreenReader.Say(text, true); + return; + } + } + else if (___letterView == null) + { + Point pos = clickableGrandpaLetterRect(___parallaxPan, ___grandpaSpeechTimer).Center; + Game1.setMousePositionRaw((int)((float)pos.X * Game1.options.zoomLevel), (int)((float)pos.Y * Game1.options.zoomLevel)); + return; + } + } + } + if (___letterView != null) + { + DialoguePatches.NarrateLetterContent(___letterView); + } + } + catch (System.Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static Rectangle clickableGrandpaLetterRect(int ___parallaxPan, int ___grandpaSpeechTimer) + { + 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 From 7f81d7277115bdb63d533eaaf1b242c13c36b941 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 15 May 2022 15:00:08 +0530 Subject: [PATCH 152/232] added more scenes to grandpa story --- stardew-access/ModEntry.cs | 31 +++++++ stardew-access/Patches/MiniGamesPatches.cs | 97 ++++++++++------------ stardew-access/i18n/default.json | 7 +- 3 files changed, 82 insertions(+), 53 deletions(-) diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 4579a36..8412b81 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -194,6 +194,7 @@ namespace stardew_access bool isLeftAltPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftAlt); + #region Simulate left and right clicks if (Game1.activeClickableMenu != null) { bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift); @@ -223,6 +224,36 @@ namespace stardew_access #endregion } + if (Game1.currentMinigame != null) + { + bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift); + bool isLeftControlPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl); + bool isCustomizingChrachter = Game1.activeClickableMenu is CharacterCustomization || (TitleMenu.subMenu != null && TitleMenu.subMenu is CharacterCustomization); + + #region Mouse Click Simulation + // Main Keybinds + if (isLeftControlPressed && Config.LeftClickMainKey.JustPressed()) + { + Game1.currentMinigame.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); + } + if (isLeftShiftPressed && Config.RightClickMainKey.JustPressed()) + { + Game1.currentMinigame.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true)); + } + + // Alternate Keybinds + if (Config.LeftClickAlternateKey.JustPressed()) + { + Game1.currentMinigame.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); + } + if (Config.RightClickAlternateKey.JustPressed()) + { + Game1.currentMinigame.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true)); + } + #endregion + } + #endregion + if (!Context.IsPlayerFree) return; diff --git a/stardew-access/Patches/MiniGamesPatches.cs b/stardew-access/Patches/MiniGamesPatches.cs index 938b98b..ec3985d 100644 --- a/stardew-access/Patches/MiniGamesPatches.cs +++ b/stardew-access/Patches/MiniGamesPatches.cs @@ -7,31 +7,27 @@ namespace stardew_access.Patches public class MiniGamesPatches { public static string grandpaStoryQuery = " "; + public static string introQuery = " "; internal static void IntroPatch(Intro __instance, int ___currentState) { try { - MainClass.DebugLog(___currentState + "\t intro"); + string toSpeak = " "; if (___currentState == 3) { - string text = "Travelling to Stardew Valley bus stop"; - if (grandpaStoryQuery != text) - { - grandpaStoryQuery = text; - MainClass.ScreenReader.Say(text, true); - return; - } + toSpeak = "Travelling to Stardew Valley bus stop"; } if (___currentState == 4) { - string text = "Stardew valley 0.5 miles away"; - if (grandpaStoryQuery != text) - { - grandpaStoryQuery = text; - MainClass.ScreenReader.Say(text, true); - return; - } + toSpeak = "Stardew valley 0.5 miles away"; + } + + if (toSpeak != " " && introQuery != toSpeak) + { + introQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, false); + return; } } catch (System.Exception e) @@ -45,46 +41,42 @@ namespace stardew_access.Patches try { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + string toSpeak = " "; + MainClass.DebugLog("" + ___scene); - // TODO add scene 0 explaination - // if(___scene == 0) - // { - // - // } + if (___letterView != null) + { + DialoguePatches.NarrateLetterContent(___letterView); + } + + if (MainClass.ModHelper == null) + return; + + if (___scene == 0) + { + toSpeak = MainClass.ModHelper.Translation.Get("grandpastory.scene0"); + } if (___drawGrandpa) { if (___grandpaSpeech.Count > 0 && ___grandpaSpeechTimer > 3000) { - string text = ___grandpaSpeech.Peek(); - if (grandpaStoryQuery != text) - { - grandpaStoryQuery = text; - MainClass.ScreenReader.Say(text, true); - return; - } + toSpeak = ___grandpaSpeech.Peek(); } } if (___scene == 3) { - string text = Game1.content.LoadString("Strings\\StringsFromCSFiles:GrandpaStory.cs.12059"); - if (grandpaStoryQuery != text) - { - grandpaStoryQuery = text; - MainClass.ScreenReader.Say(text, true); - return; - } + toSpeak = Game1.content.LoadString("Strings\\StringsFromCSFiles:GrandpaStory.cs.12059"); } - // TODO add scene 4 & 5 explaination - // if(___scene == 4) - // { - // - // } - // if(___scene == 5) - // { - // - // } + if (___scene == 4) + { + toSpeak = MainClass.ModHelper.Translation.Get("grandpastory.scene4"); + } + if (___scene == 5) + { + toSpeak = MainClass.ModHelper.Translation.Get("grandpastory.scene5"); + } if (___scene == 6) { @@ -92,13 +84,7 @@ namespace stardew_access.Patches { if (clickableGrandpaLetterRect(___parallaxPan, ___grandpaSpeechTimer).Contains(x, y)) { - string text = "Left click to open grandpa's letter"; - if (grandpaStoryQuery != text) - { - grandpaStoryQuery = text; - MainClass.ScreenReader.Say(text, true); - return; - } + toSpeak = MainClass.ModHelper.Translation.Get("grandpastory.letteropen"); } else if (___letterView == null) { @@ -107,10 +93,16 @@ namespace stardew_access.Patches return; } } + else + { + toSpeak = MainClass.ModHelper.Translation.Get("grandpastory.scene6"); + } } - if (___letterView != null) + + if (toSpeak != " " && grandpaStoryQuery != toSpeak) { - DialoguePatches.NarrateLetterContent(___letterView); + grandpaStoryQuery = toSpeak; + MainClass.ScreenReader.Say(toSpeak, false); } } catch (System.Exception e) @@ -119,6 +111,7 @@ namespace stardew_access.Patches } } + // This method is taken from the game's source code private static Rectangle clickableGrandpaLetterRect(int ___parallaxPan, int ___grandpaSpeechTimer) { 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); diff --git a/stardew-access/i18n/default.json b/stardew-access/i18n/default.json index 071bf62..893d9b2 100644 --- a/stardew-access/i18n/default.json +++ b/stardew-access/i18n/default.json @@ -1,5 +1,10 @@ { "warnings.health": "Warning! Health is at {{value}} percent!", "warnings.stamina": "Warning! Stamina is at {{value}} percent!", - "warnings.time": "Warning! Time is {{value}}" + "warnings.time": "Warning! Time is {{value}}", + "grandpastory.scene0":"Grandpa, on his deathbed.", + "grandpastory.scene4":"Employees working in JoJa corp.", + "grandpastory.scene5":"Employees in their cubicals, some of them look exhausted including yourself.", + "grandpastory.scene6":"You reach your desk finding grandpa's letter.", + "grandpastory.letteropen":"Left click to open grandpa's letter" } \ No newline at end of file From af50fd114c5a0c60c8796da202a3d9ab6d6e7ae9 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 15 May 2022 15:03:04 +0530 Subject: [PATCH 153/232] Cleaned the code a bit --- stardew-access/Patches/MiniGamesPatches.cs | 23 +++++++++++----------- stardew-access/i18n/default.json | 4 +++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/stardew-access/Patches/MiniGamesPatches.cs b/stardew-access/Patches/MiniGamesPatches.cs index ec3985d..1020aeb 100644 --- a/stardew-access/Patches/MiniGamesPatches.cs +++ b/stardew-access/Patches/MiniGamesPatches.cs @@ -13,14 +13,18 @@ namespace stardew_access.Patches { try { + if (MainClass.ModHelper == null) + return; + string toSpeak = " "; + if (___currentState == 3) { - toSpeak = "Travelling to Stardew Valley bus stop"; + toSpeak = MainClass.ModHelper.Translation.Get("intro.scene3"); } - if (___currentState == 4) + else if (___currentState == 4) { - toSpeak = "Stardew valley 0.5 miles away"; + toSpeak = MainClass.ModHelper.Translation.Get("intro.scene4"); } if (toSpeak != " " && introQuery != toSpeak) @@ -56,29 +60,26 @@ namespace stardew_access.Patches { toSpeak = MainClass.ModHelper.Translation.Get("grandpastory.scene0"); } - - if (___drawGrandpa) + else if (___drawGrandpa) { if (___grandpaSpeech.Count > 0 && ___grandpaSpeechTimer > 3000) { toSpeak = ___grandpaSpeech.Peek(); } } - if (___scene == 3) + else if (___scene == 3) { toSpeak = Game1.content.LoadString("Strings\\StringsFromCSFiles:GrandpaStory.cs.12059"); } - - if (___scene == 4) + else if (___scene == 4) { toSpeak = MainClass.ModHelper.Translation.Get("grandpastory.scene4"); } - if (___scene == 5) + else if (___scene == 5) { toSpeak = MainClass.ModHelper.Translation.Get("grandpastory.scene5"); } - - if (___scene == 6) + else if (___scene == 6) { if (___grandpaSpeechTimer > 3000) { diff --git a/stardew-access/i18n/default.json b/stardew-access/i18n/default.json index 893d9b2..cc93de8 100644 --- a/stardew-access/i18n/default.json +++ b/stardew-access/i18n/default.json @@ -6,5 +6,7 @@ "grandpastory.scene4":"Employees working in JoJa corp.", "grandpastory.scene5":"Employees in their cubicals, some of them look exhausted including yourself.", "grandpastory.scene6":"You reach your desk finding grandpa's letter.", - "grandpastory.letteropen":"Left click to open grandpa's letter" + "grandpastory.letteropen":"Left click to open grandpa's letter", + "intro.scene3":"Travelling to Stardew Valley bus stop", + "intro.scene4":"Stardew valley 0.5 miles away" } \ No newline at end of file From 80aa0bf1069bfed21d194990f73fffdd6abdae27 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 15 May 2022 15:22:50 +0530 Subject: [PATCH 154/232] Added translations to other languages --- stardew-access/i18n/de.json | 9 ++++++++- stardew-access/i18n/default.json | 2 +- stardew-access/i18n/es.json | 9 ++++++++- stardew-access/i18n/fr.json | 9 ++++++++- stardew-access/i18n/hu.json | 9 ++++++++- stardew-access/i18n/it.json | 9 ++++++++- stardew-access/i18n/ja.json | 9 ++++++++- stardew-access/i18n/ko.json | 9 ++++++++- stardew-access/i18n/pt.json | 9 ++++++++- stardew-access/i18n/ru.json | 9 ++++++++- stardew-access/i18n/tr.json | 9 ++++++++- stardew-access/i18n/zh.json | 9 ++++++++- 12 files changed, 89 insertions(+), 12 deletions(-) diff --git a/stardew-access/i18n/de.json b/stardew-access/i18n/de.json index bdf53da..8c66154 100644 --- a/stardew-access/i18n/de.json +++ b/stardew-access/i18n/de.json @@ -1,5 +1,12 @@ { "warnings.health": "Warnung! Die Gesundheit liegt bei {{value}} Prozent!", "warnings.stamina": "Warnung! Ausdauer beträgt ar {{value}} Prozent!", - "warnings.time": "Warnung! Zeit ist {{value}}" + "warnings.time": "Warnung! Zeit ist {{value}}", + "grandpastory.scene0":"Opa, auf seinem Sterbebett.", + "grandpastory.scene4":"Mitarbeiter der JoJa corp.", + "grandpastory.scene5":"Mitarbeiter in ihren Kabinen, einige von ihnen sehen erschöpft aus, Sie selbst eingeschlossen.", + "grandpastory.scene6":"Du erreichst deinen Schreibtisch und findest Opas Brief.", + "grandpastory.letteropen":"Linksklick, um Opas Brief zu öffnen", + "intro.scene3":"Fahrt zur Bushaltestelle Stardew Valley", + "intro.scene4":"Stardew Valley 0.5 Meilen entfernt" } \ No newline at end of file diff --git a/stardew-access/i18n/default.json b/stardew-access/i18n/default.json index cc93de8..cf1b780 100644 --- a/stardew-access/i18n/default.json +++ b/stardew-access/i18n/default.json @@ -4,7 +4,7 @@ "warnings.time": "Warning! Time is {{value}}", "grandpastory.scene0":"Grandpa, on his deathbed.", "grandpastory.scene4":"Employees working in JoJa corp.", - "grandpastory.scene5":"Employees in their cubicals, some of them look exhausted including yourself.", + "grandpastory.scene5":"Employees in their cubicles, some of them look exhausted including yourself.", "grandpastory.scene6":"You reach your desk finding grandpa's letter.", "grandpastory.letteropen":"Left click to open grandpa's letter", "intro.scene3":"Travelling to Stardew Valley bus stop", diff --git a/stardew-access/i18n/es.json b/stardew-access/i18n/es.json index d833a17..2c0a7fb 100644 --- a/stardew-access/i18n/es.json +++ b/stardew-access/i18n/es.json @@ -1,5 +1,12 @@ { "warnings.health": "¡Advertencia! ¡La salud está al {{value}} por ciento!", "warnings.stamina": "¡Advertencia! ¡La resistencia es un {{value}} por ciento!", - "warnings.time": "¡Advertencia! El tiempo es {{valor}}" + "warnings.time": "¡Advertencia! El tiempo es {{valor}}", + "grandpastory.scene0":"Abuelo, en su lecho de muerte.", + "grandpastory.scene4":"Empleados que trabajan en JoJa corp.", + "grandpastory.scene5":"Empleados en sus cubículos, algunos de ellos parecen exhaustos, incluido usted.", + "grandpastory.scene6":"Llegas a tu escritorio y encuentras la carta del abuelo.", + "grandpastory.letteropen":"Haz clic izquierdo para abrir la carta del abuelo.", + "intro.scene3":"Viajando a la parada de autobús de Stardew Valley", + "intro.scene4":"Valle de Stardew a 0.5 millas de distancia" } \ No newline at end of file diff --git a/stardew-access/i18n/fr.json b/stardew-access/i18n/fr.json index 102cff8..ad36680 100644 --- a/stardew-access/i18n/fr.json +++ b/stardew-access/i18n/fr.json @@ -1,5 +1,12 @@ { "warnings.health": "Avertissement! La santé est à {{value}} pour cent!", "warnings.stamina": "Avertissement! L'endurance est à {{value}} pour cent!", - "warnings.time": "Avertissement! Le temps est de {{value}}" + "warnings.time": "Avertissement! Le temps est de {{value}}", + "grandpastory.scene0":"Grand-père, sur son lit de mort.", + "grandpastory.scene4":"Les employés travaillant chez JoJa corp.", + "grandpastory.scene5":"Employés dans leurs cabines, certains ont l'air épuisés, y compris vous-même.", + "grandpastory.scene6":"Vous atteignez votre bureau en trouvant la lettre de grand-père.", + "grandpastory.letteropen":"Clic gauche pour ouvrir la lettre de grand-père", + "intro.scene3":"Se rendre à l'arrêt de bus Stardew Valley", + "intro.scene4":"Vallée de Stardew à 0.5 miles" } \ No newline at end of file diff --git a/stardew-access/i18n/hu.json b/stardew-access/i18n/hu.json index a62e032..8421e95 100644 --- a/stardew-access/i18n/hu.json +++ b/stardew-access/i18n/hu.json @@ -1,5 +1,12 @@ { "warnings.health": "Figyelem! Az egészségi állapot {{érték}} százalék!", "warnings.stamina": "Figyelem! Az állóképesség {{value}} százalék!", - "warnings.time": "Figyelem! Az idő {{érték}}" + "warnings.time": "Figyelem! Az idő {{érték}}", + "grandpastory.scene0":"Nagypapa, a halálos ágyán.", + "grandpastory.scene4":"A JoJa corp.-nál dolgozó alkalmazottak", + "grandpastory.scene5":"Alkalmazottak a fülkéiben, néhányuk kimerültnek tűnik, beleértve Önt is.", + "grandpastory.scene6":"Az asztalodhoz érve megtalálod a nagypapa levelét.", + "grandpastory.letteropen":"Kattintson a bal gombbal a nagypapa levelének megnyitásához", + "intro.scene3":"Utazás a Stardew Valley buszmegállóhoz", + "intro.scene4":"Stardew-völgy 0.5 mérföldre van" } \ No newline at end of file diff --git a/stardew-access/i18n/it.json b/stardew-access/i18n/it.json index b756cf8..1b54896 100644 --- a/stardew-access/i18n/it.json +++ b/stardew-access/i18n/it.json @@ -1,5 +1,12 @@ { "warnings.health": "Avvertimento! La salute è al {{value}} percento!", "warnings.stamina": "Avvertimento! La resistenza è al {{value}} percento!", - "warnings.time": "Avvertimento! L'ora è {{value}}" + "warnings.time": "Avvertimento! L'ora è {{value}}", + "grandpastory.scene0":"Il nonno, sul letto di morte.", + "grandpastory.scene4":"Dipendenti che lavorano in JoJa corp.", + "grandpastory.scene5":"Impiegati nei loro cubicoli, alcuni di loro sembrano esausti, compreso te.", + "grandpastory.scene6":"Raggiungi la tua scrivania e trovi la lettera del nonno.", + "grandpastory.letteropen":"Fare clic con il tasto sinistro per aprire la lettera del nonno", + "intro.scene3":"In viaggio verso la fermata dell'autobus di Stardew Valley", + "intro.scene4":"Stardew Valley 0.5 miglia di distanza" } \ No newline at end of file diff --git a/stardew-access/i18n/ja.json b/stardew-access/i18n/ja.json index 112a9c2..c667345 100644 --- a/stardew-access/i18n/ja.json +++ b/stardew-access/i18n/ja.json @@ -1,5 +1,12 @@ { "warnings.health": "警告!健康状態は{{value}}パーセントです!", "warnings.stamina": "警告!スタミナは{{value}}パーセントです!", - "warnings.time": "警告!時間は{{value}}です" + "warnings.time": "警告!時間は{{value}}です", + "grandpastory.scene0":"おじいちゃん、彼の死の床に。", + "grandpastory.scene4":"JoJacorpで働く従業員。", + "grandpastory.scene5":"彼らのキュービクルの従業員、彼らの何人かはあなた自身を含めて疲れ果てているように見えます。", + "grandpastory.scene6":"おじいちゃんの手紙を見つけて机に着きます。", + "grandpastory.letteropen":"左クリックしておじいちゃんの手紙を開く", + "intro.scene3":"スターデューバレーバス停への移動", + "intro.scene4":"0.5マイル離れたスターデューバレー" } \ No newline at end of file diff --git a/stardew-access/i18n/ko.json b/stardew-access/i18n/ko.json index 1ddc247..2261eb5 100644 --- a/stardew-access/i18n/ko.json +++ b/stardew-access/i18n/ko.json @@ -1,5 +1,12 @@ { "warnings.health": "경고! 건강은 {{value}}퍼센트입니다!", "warnings.stamina": "경고! 체력은 {{value}}퍼센트입니다!", - "warnings.time": "경고! 시간은 {{value}}입니다" + "warnings.time": "경고! 시간은 {{value}}입니다", + "grandpastory.scene0":"임종을 앞둔 할아버지.", + "grandpastory.scene4":"(주)조자에서 근무하는 직원들", + "grandpastory.scene5":"칸막이에 있는 직원들, 당신을 포함하여 몇몇은 지쳐 보입니다.", + "grandpastory.scene6":"책상에 다가가 할아버지의 편지를 찾습니다.", + "grandpastory.letteropen":"할아버지의 편지를 열려면 왼쪽 클릭", + "intro.scene3":"스타듀밸리 버스정류장으로 이동", + "intro.scene4":"스타듀 밸리에서 0.8km 떨어짐" } \ No newline at end of file diff --git a/stardew-access/i18n/pt.json b/stardew-access/i18n/pt.json index ecb88f7..edd1ed5 100644 --- a/stardew-access/i18n/pt.json +++ b/stardew-access/i18n/pt.json @@ -1,5 +1,12 @@ { "warnings.health": "Aviso! A saúde está em {{value}} por cento!", "warnings.stamina": "Aviso! A resistência está em {{value}} por cento!", - "warnings.time": "Aviso! O tempo é {{value}}" + "warnings.time": "Aviso! O tempo é {{value}}", + "grandpastory.scene0":"Vovô, em seu leito de morte.", + "grandpastory.scene4":"Funcionários que trabalham na JoJa corp.", + "grandpastory.scene5":"Funcionários em seus cubículos, alguns deles parecem exaustos, incluindo você.", + "grandpastory.scene6":"Você chega à sua mesa encontrando a carta do vovô.", + "grandpastory.letteropen":"Clique com o botão esquerdo para abrir a carta do vovô", + "intro.scene3":"Viajar para o ponto de ônibus Stardew Valley", + "intro.scene4":"Vale Stardew a 0.5 km de distância" } \ No newline at end of file diff --git a/stardew-access/i18n/ru.json b/stardew-access/i18n/ru.json index 2c03e06..61b2023 100644 --- a/stardew-access/i18n/ru.json +++ b/stardew-access/i18n/ru.json @@ -1,5 +1,12 @@ { "warnings.health": "Предупреждение! Здоровье составляет {{value}} процентов!", "warnings.stamina": "Предупреждение! Выносливость составляет {{value}} процентов!", - "warnings.time": "Предупреждение! Время {{value}}" + "warnings.time": "Предупреждение! Время {{value}}", + "grandpastory.scene0":"Дедушка на смертном одре.", + "grandpastory.scene4":"Сотрудники, работающие в JoJa corp.", + "grandpastory.scene5":"Сотрудники в своих кабинетах, некоторые из них выглядят измученными, в том числе и вы.", + "grandpastory.scene6":"Вы подходите к своему столу и находите дедушкино письмо.", + "grandpastory.letteropen":"Щелкните левой кнопкой мыши, чтобы открыть письмо дедушки", + "intro.scene3":"Поездка на автобусную остановку Stardew Valley", + "intro.scene4":"Долина Стардью: 0.8 км" } \ No newline at end of file diff --git a/stardew-access/i18n/tr.json b/stardew-access/i18n/tr.json index 72f2597..ebcdb31 100644 --- a/stardew-access/i18n/tr.json +++ b/stardew-access/i18n/tr.json @@ -1,5 +1,12 @@ { "warnings.health": "Uyarı! Sağlık yüzde {{değer}}!", "warnings.stamina": "uyarı! Dayanıklılık yüzde {{değer}}!", - "warnings.time": "Uyarı! Zaman {{değer}}" + "warnings.time": "Uyarı! Zaman {{değer}}", + "grandpastory.scene0":"Büyükbaba, ölüm döşeğinde.", + "grandpastory.scene4":"JoJa şirketinde çalışan çalışanlar", + "grandpastory.scene5":"Kabinlerinde çalışanlar, siz de dahil olmak üzere bazıları bitkin görünüyor.", + "grandpastory.scene6":"Dedenizin mektubunu bulmak için masanıza ulaşıyorsunuz.", + "grandpastory.letteropen":"Büyükbabanın mektubunu açmak için sol tıklayın", + "intro.scene3":"Stardew Valley otobüs durağına seyahat", + "intro.scene4":"Stardew vadisi 0.5 mil uzakta" } \ No newline at end of file diff --git a/stardew-access/i18n/zh.json b/stardew-access/i18n/zh.json index 2eccfdc..89a24e2 100644 --- a/stardew-access/i18n/zh.json +++ b/stardew-access/i18n/zh.json @@ -1,5 +1,12 @@ { "warnings.health": "警告!健康状况为 {{value}} 百分!", "warnings.stamina": "警告!耐力为 {{value}} 百分!", - "warnings.time": "警告!时间是 {{value}}" + "warnings.time": "警告!时间是 {{value}}", + "grandpastory.scene0":"爷爷,临终前。", + "grandpastory.scene4":"在 JoJa corp. 工作的员工", + "grandpastory.scene5":"员工在他们的隔间里,其中一些人看起来很累,包括你自己。", + "grandpastory.scene6":"你走到办公桌前,找到了爷爷的信。", + "grandpastory.letteropen":"左键打开爷爷的信", + "intro.scene3":"前往星露谷物语巴士站", + "intro.scene4":"星露谷物语 0.5 英里外" } \ No newline at end of file From 0942eec32e007610446e18cc2a4fb551ae505771 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 15 May 2022 15:26:34 +0530 Subject: [PATCH 155/232] beta 1.2.3 --- stardew-access/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index f702e9f..2258452 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.2.2", + "Version": "1.2.3", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From fcd6c8ecb35f2e3586eb6b11349fea91ab27a28b Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sun, 15 May 2022 15:30:31 +0530 Subject: [PATCH 156/232] Moved sounds to assets folder --- stardew-access/CustomSoundEffects.cs | 2 +- stardew-access/Features/TileInfo.cs | 2 +- stardew-access/assets/sounds/colliding.wav | Bin 0 -> 47950 bytes stardew-access/assets/sounds/drop_item.wav | Bin 0 -> 195816 bytes stardew-access/assets/sounds/npc_bottom.wav | Bin 0 -> 302050 bytes stardew-access/assets/sounds/npc_left.wav | Bin 0 -> 375478 bytes .../assets/sounds/npc_mono_bottom.wav | Bin 0 -> 4484 bytes stardew-access/assets/sounds/npc_mono_left.wav | Bin 0 -> 13364 bytes .../assets/sounds/npc_mono_right.wav | Bin 0 -> 7444 bytes stardew-access/assets/sounds/npc_mono_top.wav | Bin 0 -> 6704 bytes stardew-access/assets/sounds/npc_right.wav | Bin 0 -> 326524 bytes stardew-access/assets/sounds/npc_top.wav | Bin 0 -> 320404 bytes stardew-access/assets/sounds/obj_bottom.wav | Bin 0 -> 318490 bytes stardew-access/assets/sounds/obj_left.wav | Bin 0 -> 350998 bytes .../assets/sounds/obj_mono_bottom.wav | Bin 0 -> 6472 bytes stardew-access/assets/sounds/obj_mono_left.wav | Bin 0 -> 10404 bytes .../assets/sounds/obj_mono_right.wav | Bin 0 -> 3604 bytes stardew-access/assets/sounds/obj_mono_top.wav | Bin 0 -> 4484 bytes stardew-access/assets/sounds/obj_right.wav | Bin 0 -> 294772 bytes stardew-access/assets/sounds/obj_top.wav | Bin 0 -> 302050 bytes 20 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 stardew-access/assets/sounds/colliding.wav create mode 100644 stardew-access/assets/sounds/drop_item.wav create mode 100644 stardew-access/assets/sounds/npc_bottom.wav create mode 100644 stardew-access/assets/sounds/npc_left.wav create mode 100644 stardew-access/assets/sounds/npc_mono_bottom.wav create mode 100644 stardew-access/assets/sounds/npc_mono_left.wav create mode 100644 stardew-access/assets/sounds/npc_mono_right.wav create mode 100644 stardew-access/assets/sounds/npc_mono_top.wav create mode 100644 stardew-access/assets/sounds/npc_right.wav create mode 100644 stardew-access/assets/sounds/npc_top.wav create mode 100644 stardew-access/assets/sounds/obj_bottom.wav create mode 100644 stardew-access/assets/sounds/obj_left.wav create mode 100644 stardew-access/assets/sounds/obj_mono_bottom.wav create mode 100644 stardew-access/assets/sounds/obj_mono_left.wav create mode 100644 stardew-access/assets/sounds/obj_mono_right.wav create mode 100644 stardew-access/assets/sounds/obj_mono_top.wav create mode 100644 stardew-access/assets/sounds/obj_right.wav create mode 100644 stardew-access/assets/sounds/obj_top.wav diff --git a/stardew-access/CustomSoundEffects.cs b/stardew-access/CustomSoundEffects.cs index 11415cd..b5610e4 100644 --- a/stardew-access/CustomSoundEffects.cs +++ b/stardew-access/CustomSoundEffects.cs @@ -57,7 +57,7 @@ namespace stardew_access } SoundEffect effect; - string filePath = Path.Combine(MainClass.ModHelper.DirectoryPath, "sounds", $"{soundEffect.Key}.wav"); + string filePath = Path.Combine(MainClass.ModHelper.DirectoryPath, "assets", "sounds", $"{soundEffect.Key}.wav"); using (FileStream stream = new(filePath, FileMode.Open)) { effect = SoundEffect.FromStream(stream); diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 4c161f9..3e1605b 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -300,7 +300,7 @@ namespace stardew_access.Features ///
name: This is the name of the tile. Default to null if the tile tile has nothing on it. public static (CATEGORY? category, string? name) getDynamicTilesInfo(int x, int y, bool lessInfo = false) { - if (Game1.currentLocation.orePanPoint != Point.Zero && Game1.currentLocation.orePanPoint == new Point(x, y)) + if (Game1.currentLocation.orePanPoint.Value != Point.Zero && Game1.currentLocation.orePanPoint.Value == new Point(x, y)) { return (CATEGORY.Interactables, "panning spot"); } diff --git a/stardew-access/assets/sounds/colliding.wav b/stardew-access/assets/sounds/colliding.wav new file mode 100644 index 0000000000000000000000000000000000000000..fcc80a9d57d858d8e0bda592515db1ac7ac80753 GIT binary patch literal 47950 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 X_UBxWQ||%OU#`~G1Fjypeh>T~Mlq&l literal 0 HcmV?d00001 diff --git a/stardew-access/assets/sounds/drop_item.wav b/stardew-access/assets/sounds/drop_item.wav new file mode 100644 index 0000000000000000000000000000000000000000..0945bda9b8dc868d38ebed416305b2983f6f5e52 GIT binary patch literal 195816 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
Xe8B+M8TWSag)18xlT8Y#iKKYG5AX!b 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 literal 0 HcmV?d00001 diff --git a/stardew-access/assets/sounds/npc_bottom.wav b/stardew-access/assets/sounds/npc_bottom.wav new file mode 100644 index 0000000000000000000000000000000000000000..d610bf56aac87c5a797508f1ac18d52d14c6cdf4 GIT binary patch literal 302050 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

2v&@q!px2EBiZZ)@SNap1sTSaKBT7@!{vSWiSP zfiQXt3T3eQCOUr@?bpQSgK%$8yy`B#n~l3IAe*#Eb0cZ;Bm*~7tIH&;joxe`9o92{ zOB(u_nfcKyci!h5EiC6w^)zLa6lKITPD|(Ks+S{D>mg?5Emz)WZ_;Ij!2WBLy-fL} zE~-R|?S~=wZicHgh3;FU<1+$8fN7k|)v*b?&EL30K zbc$I$mSTh1!cb|%BGzTF)ZUs^s5^i(*m-yUtCH5%GM`AA;l`{cQtuLK^ou++r&|(; zK@6!KPlmVRl+QR}AwC_0g)-D+i>KS5pTE%Cbhsu3of`nT2a3N4W*eY33$Ww~jJY9} z#KL>i#l7?3(r?0GQ@G-g;P4kjI|_!60B)!5n+1Arl$}lk+kB-<04#6=mo}u&6{?cO6Le%HT3!)lL{v4}MM%1O)!>5sC)DK5vA5o{-pyCMV&+ z>*DuMFmyP;BhZapaC$HLWeYz)N8u%KqbVLg3H4i!mlUBLXK|}FK2wW(Ud5(5B(^`9 zXiu_J$f&L4bzkb8ManMIw7(>NIGZt+{=UUNhSBR2_@_#G@h11uV@?C5To>jNE_nwr z#Vcv-dG^s*PP@%AU1j%b=CfTs@tTz+%3tf)?`yKfJJ$b>ytbYdRmiDtSV*}nK4(U^ zWPeWU5U~nkXsswiK=$RL27=Si3iMFlKEJQ3Qgv*V^ar>cMB)fsaJFs|nl}m9Kt-%~WX*!8L8+VtZ)QLzvwaZX6<1^@3mR zgneePw}+r*4vhkZTSK7xC86tR_~ol`+ZiqxC$3!wN1heCq`(V3!Rk-&%?0p%1Ufec zPL4t=s-f#QWV#%+O~OMJ$od$r3Biv);|UsM-(WI&A5r)bKbf!#Br1&l{XhDShfPu0?&8@qReM5suQ@8qFrGJ4btIHWIjXdR_-!kd(N<1$Rj*cZg9drI z8()?p&l=7>XUktS`My@kxrXiDFKvit$8@9`SGFyRTgmj4#D-s|owqP+Upo9V9iUC` z&7u)G#HxU#c#)_5$aNVnjKVf)Sf>?fyWtmJXrGAsRl>O!(0P04=8h_h!4O@vb~-S9 z4A*=RbB@BKz2do*uyKU=(++0+P}lsz>1jd;12G!}1%`=qydlr)ysGzsdB@X|B1S?pndEH`1`d zeB~nQ(#Qt=M>n2f1Fh&#SC*tlYyQwnKgri9x~znltEbjOWMVq0@g$RN$m{{c;VxeO z2j6tVmv3X+ugKsKo)>~XF2SdJqjO`i-aWWZAN%>ik-yNj&aizsI$QuYo<;{(g3#?q z>;^u$p@K^B>JaoLTZ*y$;BRSLcZ@c3KdNh&;3A*??Mn+pZALvYp& zq5WPs>4s1g2>TZar#3*xhr$y-7}g+kSP6e{VXQywJxtWz0^hC{cSgWVIpVJK@O69e z?LMqt4lewLkrlwSC$b(2Ura?W62EFey2+R>Ms-v1$Jfwa ziCkyHGpPtyf^09e&Jm2%MJWYhP9fYgQgm7cKW7RL^x_Z6m=V z8)dyLZq-vh{371tiW9YBoTl<)o%qLCx$CF+&_)@>#6b&`7Y)JSV5MLQ)}|^22hjXR zdC~(o8VMgZfh{Y9x})IY1Hu0RXr3&#>A=&^#en6|ay!s{46mBQ8Iw`tJNV}|N{vH9 z#$k;~_)iA*_=Js(Nmd|fw~d_9pal=ejsw)83mx5&6)dC^Vp#ta>Z#AC{Gh`U`A-|x z-9);+kL4su^Pc^mv0BxGcitv1UchTBM?>`~l~-7(hUaq~J5~QeKFC3plg;0Z zQ7uT}8waQyqWB;kRoW(A-6Zey;HCFvm+^eraXF_akN1_Um25)~xwev3S4fwRv6HJL zVIix9d}}whb1y$sNui2$+eLo`v(JNQl9EQfB(K-fA$v%}C$fDQ*|U(me1{j*;G`JL zo$(ZV>|BB3+R)7ZP|OwN{RsN|p*jyZX9(*23k*|077q5@hchg}%rnrTPP7PtU*kpp z70_(HsD8x8zM}19I9U*0kAVwc30+4*-x9%K1YCDrAl7i%HR1C__&iUDb%9#t!d`DU zs9w+wfGeP|HX05$6*VuxHO^xEJ-B|iIOZe#l_v(sFk29n1JUfsz;_mUcoh8Dg`EC^ z@mG=lTsW-(mED5ty5NhZ=;l;BbU&K59mjn~PtW7wQ}OpITyPfK|G|bBCmWC^4-#ra z_)U`PMOOBv>mtazXlhLBvT64s{^io_#h3%OoUAvQFAreFc!J^fdmzNuLIOwBALbFm}_uJu(&u5I=hG?gW*jxarh>< zPE-8PAC7DhT$aOG^+Moc_@!ESum~F57t9vI&-aA+^Pz5q(BKZ^UkFDR!UfI3^kuNh zK-{_oUUm{m41B*|?2!epJQG*Fg(Vh1O9$;a2tGL?cO`fliXNs zBKgb}tWo5fQrR_c>Dy=aCr`Rz$n_;@(Inp0Uanir!#2ukfqddI`Aj5t&6Z1!@Wgwv zDuJJ?lzYeXwGZVv2l@PBbzumfkSZ@<&r|oy3*31xclqBa9?(r5+=2JIFD-h(cCV6- zA7urQC%LjOVZ4;k7{=aRrn|PWI#)XL2R-eEI;ejZ*d2qt#(;!{(5Y601K`m8Vh{qQ6U8lW zfR7|xRX1Bc5pJY`Cl`g;CxGLA;r>Bza-CqY4?OY}{6oQdZ^3jgnCB;qIRs4i2%FA> zqAP+LukQR+$o&nfEybGyU|gtpdl__mCqBLe+%*fR|Mrwwg4q9+@mbQK+b z37x;89}`eYFMRtln&gh(8sR;$*zXW_s>WM8k^loz97FQFNTmVYl1gk6slJj-GGe|C zbY(0X9YKF%CY90IK72HwzBxSJjP)nHhZ8$AQL6G~-?m8>+t}DEQdSguTqlh_!yahK zdvlrA2-&!jInR-;-?Ib$^1v3B7$Cn?vRmuqMG9s&Usl`Tzb$3G$84Co%;yq&eMKtU z${u)14R&mjhICew^}WjLAJhNbdE`m;c+SSGqXVz81}FN+n>F{NcG@gUBD=FGeNR+w z^v6vS@||pqB9>dpH+S+GVUwQZNia6A$1f!GEeTggA|G%3#Srx{!!QG0Zb8vA;nF*( zTmT0Xkya{5-HbxLfuVyAHBU5bc}bF;DTtWB6~7SbQBuNy6?^u+ukT zU@WwMEsTnU_g)C5k#NR4;b;u(`dfH<40>vbgU-Pgb1^y-9&iCk zPEBx#qZBaQ8vN;k{zZV5w#elxu<}DIondMca?gd+o}u5Ss80tRrlyJ};w7!ZJjtF%$r>`hj9xxRw%aiC_eA3&8*M`O=<$aO z>CH|2Mm(MLkh@pV#;#JkHkvh0@-}3L4oHS0S#z$G^&cBlEB#);^#4ijtJn=KIX{4T zcau$mS%tBjzn{hH%Z>-wSt_|kuoVr`*2Z1`!e z+eo`R@*cOS{RbAYm%iP~&N==v0DW^a7i?K0|VcRn#~Xyff1T0W--V$L!r4KZ#w#B z07q{}cXmLdD`?d>xcD>DosX{f!glx3V-IX+gJ~=tcMfkZ$JV;ULBy}O5tqS4=L7k$ zker@Q=SP#*IW)PL{MVZ`wvp=*EYX7YX<^n&sOc=8ca$2W@;Bu)Udf|>(+V~DtItlZ zlg3)Gh6L%DE&K19G{cdNDVE+lvEWk4a0+90q%?cxeM|a2j^(6FF{9YV(3&VOM`o|pOd6ho!G2y(o9MpecB6X%GrpiD&7f z7AySm3ha!L`%Fly(G~zYrlKJkATt~t^#dpTk)s8WXR0qdh#hT^Ux7HH2QrEf)u#SX z5Ajt49APQe--V%C;0|4BZ+h>v?J@ATx(SL80vJZ!7nWXV5|knd}9?Jts1EsKejmS1LG zW2E?PZ1Ow4{6BU%jGOmhC(L+pGaYo7DNCr*gXtflzrNC4?(~U2_3uh=eDK*N(eH6b&Q2z*24HEWcfm5A?ucyEe4dMP#(7l7u@i^#b zBHTR>wvQK9+ypi&gy}WlOQN9v2RwZyNCvRKp*YkQmMj$)uY^zY#j4}b*#yKrgDa!J zyspSe1@45`reuaaxqOTq3?)0X=#4_s zVH@>rCCU#pVj$h>tOl;>hwE(kA-bbGA5==W?B;z$>imYg3}HiuO0J&l&{ipP4?B`2 z%|FMczmtC7WMxR|T*~To3pY}9uBGY|B_Se$!N6Ea6 zbf_1p(xf4MNzDmT)`(9IC5hQM`yRfw8^86$Uz~BhfO_}BG zzGg_PR2*fBvTukjeUSSN@y8%!aaa5}8m)UN=1xb&f5bz+D84h8unpyp1Sg}B+KN|S zs{(=G=vAaj2HS6x9ImFIPxd5cn%Lyw7wVeF~U99 zqL@*5?rn5rHuluRFE`-ti?LA*UYw0@q~b3ezrKep+{pZTY@b4+m3UAqX)++uQ)$LX zvg8z9J)b;N(AFR_ekOBCCCe|cm(SH-*hNZ4OyVC0(yTCU=0a!X^ZeCR@r@Vlr)uCA zCettGQcgDQXeUi7rz2)b*|l`wA}PFylI2o3r^rt#?ZV1eN|pUt>LRIa1VhuL+;MET zh4j~!#d1E-hIv=rxz2MDRVAS)EQIm6@o1+yM7V1dxB z1#BK8q&xsK1_+z3f=>N~{3MXAuC-4D|K%4Fnad#LxX< z+8y!dY*^h3l!w6E!2oB#KcB(Ccku8`c&8KUcn3b3g3gRX9$~1d5TSB(ZYti`1^d?G znf|zBJ>li}bUWHMknBjNeZNY=*I^5OU~YG zcabC>VbSen*L&=}v+VbqnTN~K-Fd%T@>hF)^}T#{G4CbFRy+6uDwl-wh&K64IG_AN zHV))d&&mTl`NBo=EGxcWOCBb$)HKQE7JKX{1#D%(Z+Wkg%w{9M`-d`3zT-L#NMawi zQDDVpPoQqabQUL*|D*PIh}#3wVJ8{uK+-LVZ4M5v#d06KE(ALtLbLi~2L|&iQNwmP zY8!Gy;KE=uI~siW1`iAZTQcFOXX5P;SRF3fE`*6wMBQ=FSX&J41&_ZH)@eeQbm8b9 z@L#wP+W^L`6;hr9;w>zw2BzM^pc=4ywSen^cDTU)g8ED$N)Nhy7G_$*jeW(lK5*_T z@%2HtJx5Hr4S@z2{|6rP0tfn|h@0StCmP)yit*@kC>;G1d9=XWdieTcq&Ww#dWGta zs{7OM{x{gJ6w5tGxeZypfb=*|rXD7`+O*~_8M>OP&wbq}rKJYc(}-DGQ@DcV&Y^R% zSi%Ncq-4?2RC^rHxZOqY$7N;O#IWa&4j zMvhW}K;PO*`^ml~sKsx)N<)PIhI{g0rOiB5D;xj%mXOWQ| z$;W?q@K^j_5;nPm-KOBD$=L8O;=8c>Id%94PhEg=9Pv>DWMPRnJcVx!an@mYTt@13 z5@|-L3(S9w-n<0$#c29*(EB_}TLd)sBfSBjVFmivCLZ|@RaJ?0mS}&b__iAwc}lF% zL=}nRJ`DSv64e>%DObeH2$q$Ik2suHE7EpoYMYp#gTCv5^PSOUD`3|JK{v3i8=`@r zS9kO;6)f+AZhQh>Ly@gH40Awpx4{kGD5Dw{hoTNvs9!qDI)ZM$MpTIow8!;c_|X{L zy##Muh4=O$R>$zYP!dsw;Rh1YhUZMCYx|SHi*(!q;;7AhW68k{Eb<|7c+TQ9>GhF3 z&w*-1^H;lQ;T!%nn{pG$_7l~bC%x*(v_hmHE2eWsN_S?7dD7e^Y(klIE`W`CB%R&E zw5z3mhgifzDd#vVxGk+b!^WgZ-OsUgQPQz9O!k#79Ao_kOOwOdwXgi;I_7eWPjY2r zCh+?HthYLLtDuk8vuh>v2c@O^=;J85$%*=Pp-W}rdV+K+B5zH|)6Hb{X{=#R4)w+s zZ}8aDNIrn8d!uAqY;q2|HK7p}@Ingen-BUdM1^jkixHaJDz2!5M^1|yFTvIBqPQK} z8H(d)!LA>KAwytewved}wW9^02`pYCRMddObA^-nfJ_lGE`k^b;dvsM=OXlr1KpPk z;m1JBL1Eq{P*X0n+y$|^;*m!1YKgea7{-){w`Rh5W*|Be8maX{DLnocjKuKK0@!sF zYAb=JYRxqWeNRS%6HxhcE)H>@*u=grA(mr$UL_FYKj7?@l6HV(FJea#5eX zXj1#tEPp)hBC^mhy2XR9DxzwOZ(P z&!zSWZ1G=d!WHJ*PPQyzReJKzM@-O`kJT_!rKC~CYM)9|Z?Q4SQqu{R=_&o*#1836 zZjNkIG2g4tVpsFh25N1{cVyAc_t=|#wA%(&vWR}_!QPIh9n1btlHT&8U4M}1_ViT| zp=XFeG&wqwgwG`p@8ExihV(hF>@#GiN-v5gHg``y;UXH`HSi)G0t!ZJ_%Rgwg@>M-j`xJV%60fsqON^G?KV z&^$#fs)8&1MbnG0-9#~e51iIp+_nl{(i0W#(8fS)ng!)s zc@Cw_K(A`ipAytygyY8Imy5768xOpItIUb+Pn>s*xDFvNF)3W3mcummIQg4N7nBiB z==Gn(ar*yVf`~YFb{M_!mVKQ~ll$^XTWRwOzU%~zxybw8ru-eh{+@o;k$Ph4HC#H> zh57y`m7B5J+0rmecGOMM9?A@zq$%diafIYy!i9#(yNGc zNg_EB#B2=NGnf4L3cL0tM|NQIR@|d6o_Ze-e1!B*;qV=3$yTg02>o)!daq%@09+Oh zBQTmf7B)UZ+09^VIvSn^9QL8#YeCUcw7?cTpNb0g!R_H_N~@S+iYC4l@9U$J6=F9{ zlyXbV5#ZjdqWVHv@p*AfE$o^i>OFu~7sL+*u<^QhCkO7W78m8gHbJz$3wKz7-mhWi z1~5VaRX2g9F^W|Iy9tQQguojuJPya~LLm(>J09r{M22aoP929UM!jyMA+=~_7d#7~ zSDW#q{`lHkJY^wXI*lAUhWp+knd+qU0P5G1B*oA>o@8w^{TWZHvc6rSj|%Q@D0vv zOE;dD$(1eS;!iwW#Lr!@!#@1H7|k=qU3B({FBMoSV2s#;{d%Q624(OOC%x(ZP8iYViI9ON2Lt(*qaj`q}TrH}0!2Ku1 z>B(^N6LD%W9HRsDKEteepjH<>N(TefEnA(SRWQ;Gh3Cr9Km>~o@TuJ>cLm~dvJ@Me8n=qP&I0C zpcB&#;*&=+>j&JDkp+1Tw;b0(`xlHS~AgL9>pGB)6jq*cb&-I1aS znZXUo;VN5rPO?d4tHLF}AV!x;@xIJ;tb``AADWV{F;iA@y%ri2&W9D#$}wCklD>J* zR=Ut5yIHLkeQUs~?-IWYbW0%dwx)i=NO3u_`+BT>5u6jcZFz0mai5Dr6GBj8|7H2X7H^A3Jb2BQmM`C^cl0=Hto* z!Oa7KvktO$1#2u&|6ou*6TQC$4(>n%fhB2ZmJ{@A9;>#Z zFGum%BBZ*3-53gehG$H|&wk*zPz)$Os)k9sl3)!YT9D@HEGNzh~SmR)NX+N{Fq4udPZ9D~K?1LS3ddE^H z(Ic&_W-@KocXqtPTDp2NKed%++VPRQY4%8-9!y8| z=83y$fyA7*(KYW`-dcJ$kC`l?Ll3aRdGyXgwqhD>G-p#LP`@_%a2VZQNGtnNy%74m zGj*M)HoM3p1u^+Z-lvi^cgYD4Vta~YsIa(|bh?H&xssz^*lPeu)W*LkzFvf4-{O)@ z=;ckUF&HHr#rrPkY6OwI*$Ijf&_mQ*b|(z zLu*^bd&X$>bMdDL#}$hm8=xXv^sIy{GsNV4xaOLecm;ag6!)HiqszsGN8#f)Vq*;4 zrW6aJ;2jf?69Z*eaPBx%MuB@*;qvETX(jA35a$1ZpF$z&g?fI3*Jh#5j>u>)x^W5J z%|n&#G5LySt;7Sn;NACdf!c5HMMkZ{M*_*UBly}&5>SE#8@jF)zdJ$i3?Vg~?p{Mi z__N3?67ZCjie%>~KG%V2#`10vbnpk>y@KZTmpU;T;w2>vWjppsOXjfH6v=WuTa_(s z*~9+al6oFtZMP(l#4^zd9tY-fi(xY-=`6R-njjK{H-CfaoX8<0iUU#I39XordP?hxvIpUn79X3rvf&k?s; zeAt@=Ey32tr0u8rf*qE&psP{XmBX=<@y8fw3Xp**bjw1^^MIZ|@|pvl^hZksk$r(T zE{aX*aHo%WbvwM#PwYDb2DAws2E+Usp`#w$RV)-xu(3dh5W%Z*;XDR*AA}d$aD$fU z-Vd&tAU=;8z91a~qx2o@*q~OHMrJ8ube0-74wM8{FzM9s7;=6spUt zETr_FY}k0o!;FoZBsG~a4O=O_7aMOOoziF32GU%KMl1M+7JBhHAM=*3&fpH^blM(1 z>l)RX%{L@cM`J#1H~s#K9a=;$9b}uv&@r~`uMYM2NZC6QzK<@rN{$VrwmXSgCHX#` z^xi~9^&PU)`IPO(6)`hC=7+}1gFE$fN0QTKQcWJybq${d!RfKJ^ukLFQXhI z_%k0(cY$ry$Z;=h{}qibhD{LFt2q)q+;QK96v>QdrB{hx7E3E6dX=nu6)FF~0_w=%f9zNj zS*~Iq3He`p_Zd{x()0^lF=IGp445!r0CUbc=bRP6oO3uT=7h%_FGD#P$QH=~;*GCZb5!IWCwiqt{ zY(4TM=kp8e-cva}u33{d zaH~s2Yr}MFtgH2wt#Rp+bxu8Fud>?pGphDA=FBn2iyA|A7$v_Mzx|A*4Ma+);lEh4 z`ebamCHmzU^$ZbHNTfEEmn(_7X8A-T(ej+^*gqvEh=ff(NjMsF4Q=fDFm zv0*=Kxh}Q`z_w6feGPY_giL^G@uE)(4E`e8q`g{CW!P%DE&dWKZ31MBFqnl zyb|$yVfHi8cm;F|701WJsbDdwHWxlY!cS;mlIR=at|gSFN?i;O2% ztZJFjHOboD-FQ*aD74w|A8Pd7Wr!Wd7f<8VGh?>5aVU>)y=wewBO2T{iYyR)o*ABJ z#gs_nT(nsF(O8pD-v4TNG?w?$4g2x3NRH8Ci}?ySLN3c)1x0qWEKyp-3pv`{cPa-# zP9mTYtZFGTyFx@~5jzNa_7P`Bg1M*tW0bj-A$AXk>7&Jr0gyRPICO)LE&^J>_9^1A zBV^1JpGv{f`Q~{I*>i~qiIXFii$>;(bh)^+OTIIoagmqiiOuz8=u|Pw5M{@SVvod! zLE_6+(XEG&LqwDI;$0PytA&`GVJvJUy4^J*oP@WBQMR^7A8j;o5D)7ZE)_%%^Oj<9 z;q=7XJ+JuZptYdYcs9d&HO1)A*}5>!C{o4RGtzjU+xj5P2s39lgc|E&Ep390-Y+di zkkLEDQai{9d1%oOjqVRDxgHon4=ql2jol%ZAGeH=ugvRcBj$r;)O929hvn>bV`2g8 zq?^Wrn%0AN&D~e)yT?YK`PShtj2TC*c5jVE&#dL+j1E?-#}^~MhVkROadVJSH`VZ1 zWw@srQ+oWIV1Z_I@=S2MFsIqwO+L>8r8tgn0SY*z!c2N-_qg zi;CZjs>NhLlF{5r7W``1beEwChFQoO`@y(AQ%-nmJY6dNUmBm>WxEh#*9Lj~y0*s*x3L+_4(VZWv?piG;((zLKKDYNKrx@qLmpww@T*)2P-$bZ%fg z?kt*@Gv@af<|?blXffcIwapYEzF2E55I171p{qpKaO=}8qQ`S<%pNf`)OzlSC=_h{ zaZ1=dwl47zLmpfI@)y^Gtp_fM!B4Ee{KdF1YlZWo*c)r9v*K`!6^@B&pRG3g#f1#( z)NP^)Sm&)4Eo_a^^TqKBMjuy^T+iq~R9tRn^zAB+4KPYI5ldW*nN>xtg+}!vB5;FI z*J6x5U>u7#+WHt_A;v#}Ml*k7@=Ig+9^-C;(SErx?YB|I#i&wH9PVxGs3@?RF|odw zSKS!hQK*u}*g@i8USq0@Xkb_m&oldgnF~wNHp!Z`UEGVeHuV&J-&=2ci_CZbPLVtR z&N}0+SQ2YZ3l@Fjtn0%>qc7IV;i6@l^=*XMmSfF~6yt1+r;#GDgmEQ8G^%05n_q2Z z^m--E4Kyx17a7xyeIcUX24l{BVL5Kxxi0G6Hk{3WixEc1Au&JIFv}|b&MoRM6c#(N zeY{v(UwrN*0=oS>K-@Y`RIM)hFBLmWh|#;n?_6TMpLm*SxCD#23C5HUV)AR__)jtU zp0PKVTzTGTQB2w%G~&xi|MiA_HCcMTvE5N7O)$ldD!^qX{`4%-ntu$9vE+?8WB;(zQM-YRAX!hqZt{cosDZH#GwjCv1%f30pq=s z$jGv`Y9`ixutv2OTY{|@+l$s0t;wB40Z;43ZeqYzYs22cVU^W>ps-tFwHYB=F0!^5 zFUl^p#<_`?U-Cwe&kEgqFr;N<52Nxxbc3l(94aAgM_`8F|ofG5@OWtEuN+sExL-UCB*Kw zVqtT!rHSw!CA{m1T7Qd!RYc(tzs0fK!l{Iupbd1EAJDkk zTfRl3;8dAk8|&A}T4weBQAsvpz;!t*zv%Z&POuf$_wr*gktao3N(q;28B|`hm2!Ah zk)q@SNAXt60u9AfCF`{kSwdFtB98u+iT%X13^{U?Xz*1oo-D4#Nb_wQ@LY~?7li|5 z=62!jEn_{!`CZcDBPK7Cb#97&17r}eT& z0a13BEUS%EN2Q%%#QVrOImZ3#vYN$c{7~9SqgI%7$Su~rl{SS%{ZDeGohY3w$Ja8~ zhti{k7;TlRw+K-3(L~WP4`eJ8c?-gxy~4H#T)r%Z{{^+e#rG0mw#&Fx3bIPeN2Os_ zD``^(yvNH8rNMcF99ar{edVDN@adIw`OCaRE1wmCI_2PPA#iRBOY(!?1lV8$)7L?$ zk~U{RS>=z%kew-aCc^e)*&QJ|UhXf2MWdv%BOZAwZCYT#d$LCtOu8Z`^~Ia#WaMCs zKQ7CTz|9Bc`cY^$EonCzX}hdF3R`TImSLE&QMMY0Z`Vop9{9{%E^UKvSIWFj*nEZT zQyzPog_L=4=~A=lB~)4_J4C?J74phW*t=SuJPLz1$nk4ozK6^^1&-~N>wCl2qw+?5 zptEv8Nl3qBww98vfpU3_+!iA3@5sGxq&gC{3`I>Lx z(=53p3Zgg55s@(ZjEsK+HG^dOYe-Fy?O(#kT(J5Xm?v0WgP~$a2)+kJCc=dq@ZB9; z{Naoj3_AtgA3(`N@GKsx?tls!Qa3>HN_b)=Ol*U#7K86doI3~RFT%*F(0m6bPlUhE zVuw)>ej9fWgs;!>Wp~&VgVox?`Olc%5GJPJjhYaij)f~iqYPYJ0s>QUbRjsNgoX{+ z#N&7^b4TGqkls%)mz369cvncvIUFmc&pz}7SxjZ9ZGa42Mu`1%TF7m&-NVP8qPDh_H@lUZLNtg(ztfeJlj zm7m}@PQJ{BdP`)q6f1l%Zzk-y+^Q+$~Ug@>&ISDtj@YrD(Xrlsm!;|9b%vP9XuVUB0-WsaVQn*=L)tdzeYOBo? zp-m0tGZb=Eh^+y5RI2SJi@E*KvJy@IH!PtAgEn z99h99An#E5@e#NWkWC_CazA#xl({SER@G-EhFT~oYo}N&^R=wK> z-HWMlf5Y{^)Vx`+x|q5&9-?iP|6mY#)vKQ1sIXNhIGv3lZQy7!7HtVlKH`RE;Q9&= zHi3!{a9JbBxqxZ)VSy+1Z~~7F__GeU%|dfZ9Sy=kjxe+tzIKG5vbdlQ#8@D|6J)=I zfCk`x9ac7l`Fp{(HJn)pwL8L$Veq{>gtdf;{oz3+I6WNd=Yb`wn7C1>w~5r4}MharBd>~b0|nssRYFsG+X4FI2Z()SVEYAzeRghx%} zz*uw1)7xBW4YT-<`4KVkp=J6AwVaPFUs^)NY5nV<5H#_BY^h zBkXO5yX)bqW*Ar#-wejdRk6xkoLUjC>YF?+8QVEw~OgZ)WBn+*fV)nt}@+xL4+%2Ovu7Ns#DaU1SE1&u{ z7p4oODbOnwtz%(%3*l>34@gt!R`Xos|v9@;Al;FJr9EGL8BoM(iplmhq%_@QUUsPg(Q;g z20-KQ(r*kL376BR!-TuC+A=tEUb=0DB}e4l1Mp&pymc0KZjf{h&aadMA3?FDa_dXD zyHFmDfhP;(uFueSkzASvQjmlYSgAYojl!fZ|CYd=?hHR};=0a|-y6?$fw0}^+#QbnjWv6NcE#NT zU~?awF&u&$~ZC*{S4m~ct1Zh&VW%HvM>DnkBn#BB3eM>R|k@=D(=ODxrYo3H#S8(BU|R`vTmeDZ;JOCx zzlC|5A-`Ggx)X{WG0(3-J$LXv0b9&=pT1D14=lI_zZ*l!T?nZN%|l^bKIrrsoHAwJ z7)Ueg84{rQGnttTg>K3ISpigYK=TeJ9+?3b+F%uPia}$%=LNHBpgan!uw- zd|3|`hTylF5P1WSI6&-KyyO5)4&dZkP;xW6IYX^w7~UK%O~WOf;MxeZ?+abK;)7xE z*csoAgOa6jjvJh_La7?C~rM9!Fs7hXz-=~yvJ7M+Gc@8v2t^huOv!=nl*vdl=_ zl_e(*#D74x>WQlgn6;XCpbSuR466wzo$*ZzaIKEceE{sR-vsDT7|$+*x(Y%!!`BR$ ze*|vD!GViV;3W)x1WoTkY$P1=gUR1u@e%Wo5oCEl$s$;36;!N(zvn=9Bb@01jk@Bp zp)g?>+V_MalhLg$d{}_<8kv_lIMWeoZNVLt;o?p#ZvHpfkB%i^-(eI*!2Kv5v4zpc z(bg84dSRWyU>wJ1MPcqSyjTJTc;eBr(0f0+*#Fy_=CcDl-+!{J^SQx~2N z!NS$x)mZ$y9E@?pRI>}*3~X!*Q|91+yzpZlP9<4z0p0=mV?Le`GTMB`+-{$P{XmwO zg>y*`n1=WB!8}(iSPVLiLum&`hGCQHFsm;PX#n>+Vd1uLs~Ohl1;=aSmf>)(0=9L7 zNkwqf0(he!_gZN669(*pTM01m1PqNZoAN;6V5kygzOZ3SB>0-6cD};y!|*)^JUyUe ze*9#%^eltx=0UX@=j&|^TIo2x;h5yEZC1KqPyj23)EyHOg!EF(`m4=3M(a#Q&rlHv~<)I7a zcYvRx(54P-8HAM@!M2{*p*4JJkAu3x^rqNm02Hl*>7(JXJr0`+$Ns{_iy$mF_FD&@ z+3G z^Ie$V6?PxMB2%H^5$rw-RvgE93!v`_?6(|_pTNL1u+j_tw?MvQ*km_MI*ju@;qE@% zZT8*Ti8n97>n-^E4(wiw?Vf?#a-8uF8qCAy-{A9P?3V*8M&t0jSiC>ll)`r%(Y_j% zYl2(rW9^zapbd_*LysP4V~dlAU@ryvCSdO@STh4{zCqMt9RC3l*I=hNu)qWNKY`Bs zaoBw*b{u!zfKz92)&=k0X9qFE>Z@D|$dho`r3>ON>2h{g6p z%|QHp09M?_T%J(t7CIh>QUN&YG&H`3pL}7>B|LZqZ2izX5K_-#i4bUa0w0G%B~RQH z2S@i}<21Ol9Y+aRuo15pz-y~8cUfGz7|Yeb;92;i5#DgcgB|ei(b%e=*+U*_G_LQ7 zg{NbWjyQcOK5dElH(}|s5NDUd8@ZK#S+oDJim!lXPvDbP@KzzUy9PQJQse62 z>-;Lt886zX@{Q4a^k|NSEcmE3HvWO>9WWvZt8~Zuap=(xXGfv)aNHAyuP5N@AS^Ni z^WMhUi!k#du3n9m&tURq+;j-n?!r#nap)o3;*JNs(02hI^hPxq&t1UCQJCunPVJ8Y zcQK|5`Uhj3)_C*h(5?V>OGSsm=H(kk|AkePuwfZ2nTVY$ zVzZCf*a0J=(Wx$WeS;&KU_uy%npYyBIIkCO3Br2A@#cNJ;))~hpx=CKcnj~l|NAo^ z{0DblLFI`77cl%B&hy3M*D?GIMm)fRC$ME04nKk}(TE4oB@vtK#ir?4Z5Q6RV8lNd znM<|aj?HXU8xQm=seHC!o(gK@Hmq7zIc`H)L*?CuUbR)$R;*J`*=)l*PHOBnobRk0 zJ9U&_}!S|s6zK)fg0-KA6=4zxd&iaBw znxRU>b*=DZ0={gIGe2SFZa69)2lmAgaoBJucK(PP$71$-tTq|@#A3p1Z1)bYEy3_8 zJnfFvBQRtOPI+yfr^eSWu#yS#9M^hd_*0y41wEhO{=1kIiWyIEPbgN3#D$^wHV#LI z;;Zjy7mDGTxF-Z382H#6gH9Om7+2;~n}Tqwts46Xf0-Bc5Aj1ub>RVyDXS(v!1?9X z@&{PRUS&N%s-!MG#M)KV!ACfF#a^wi^6+Ha6}XqFgN9*@WpmiQCMmdy1&H_?pQ1u ztE|Ai(HOG`e@A29d1(I*Pt3%;@9?6zI*P^?Zdg4UySm`&xA=WL#ztX_ao8^kdymB* zk=S4?zKF!kv6vi*|BT00QK%;3WdA*A@(l#dSOI_IvEQAH&Qpa7QugBc48uj`3L64<~=ZsH=EB0sjd^NW>07 zIQ9#^dxmenV)r*V^Bb-;H!YKK%_sbog6+TIyEF_)$B+z6`i0ki;xhw}Wuc44b=i0! zkD8T(=L;%VD~>Fx76`mrLLCRZR7QPK*rB{?oJ$R`R|{;^v&w2+UUj&t>YiU+uBOfv zP__>0SV48Anrc)?-LGm^*Qh~N)Tu&hUnLb*NcmM%cDCwDdF5%VuG^_$g_VC9)vSow zhD+HORW(Ye&qdX=Vroh;)uFK3Q|#Yw=vqu&&!bX{Di6XQMb#w0Gey)KD>@ZX@!6;g ztCyKrr-{eQJNWju%)z?JqQBD>7ilG(M@^3h=k}8yfb*id;Y537W z75jnfYO1xFSfjSG&BoZe>P-&bcUH>4%!bMzaZ6K`l1uGrseJRPg6-5RTl18zs#9Ej z@2)(`sKR|!NCj1Gkh)n#6&|hz)KCGV)y~?=euCQIq`JAPZVgqJsp@+Z8a{&P{DoF$SrD8f7N%pIyF%J+NstJRvq`M z{6kff1FH2f6@5t68?IU%Q7OaK{9|hU2(`mYJ4_YvQ+J1| z!1HSJP_^;AIx|$wIYRUvP+gk;=sCM3Jy_*{6tzJ!4ue{Z^nQGfPb!x6!>!Z{{b;eh% zTc#4uD~DCe$zMgSRckLO&rK@vqFU{thFwC%F{y;h0RuMs}>I0P#qVk5Q3eVJw=W6f^wJKcgf357J)wc+B>4WMQ zty(3jH?iv0H?=HI)lOI464lBq)iOz)x2T>e${W;-4CR(f@5oYddG+ZWRkV;kEL3(8 zJw>VA#dY4?I<1smoL3jL)AtMLwDLO5Rv)(41&Zl1RdmG?dSW&0R9dg7u3Osai8XbL z3i>ZcU8$1ZP+P}U(f8}9GA_XfIZOa0CKYD=Bm zKu>O^b2ZeWmG0C~A8xI0Hq?XK=;@7g&9-`QV_m$Rp4LPcYOjwp)ixb;Y%~3{gD%%X zKkcZ8w$wX2>AkIVpU(PAYh9#^9@$oZ?V|Iw*I&A7O9x%1yKdZBuj-+%bk${hY1bY) zvbS#1Tc7Ex3-!}W`|F4Sy3;_tZm`Bdy8JNhH${J3)t! z*4-!Rl(D+QWL;~5?mtbtyJ)wWI@wjvo}-6M(PQT8o6~g7MY`Bb{d9>QHCy*xuD#~! zz`u3!d|hR=Zo5dYU!zYg(dp}S`Q>`pMt$sW9k5x~TCMYL)92RchTC? zoqE@1eQ38nxK-!dt5w2g3h%@@-DP8BRUT|9fI;+c^(dWGNg)_R#Io;x{4mqc< zp4HAix|+9M>7%!M>p&lk-ukzXUU5!0^VLOt^afvj%||Et>gm3^*Lhv{yuNW>|2nTL z`{`SL+TBkt^4F1my2b@<=dYh%(Ea`O?2FpnUzfk6Z~E(~OWNjw_P(sAU(kE6=*$cH z&{ebFl=i!(`(M(**LBIuI{JowcUeaT=v`Ozt(&^pRlVVs4!x?|-PXOX>5SX@*)@GU zP!GPYN8Qm`*LBUi+W&^Oxu-V-=+t|9(M=tBU(dg#FFnxnZ|lVm_0&LJ^^xv(0SC{DDpj*7qK2uMqv6&3WEKE0lp|8HsvtH^; zFZGRA`t~bbC|rlU)-&JeSK&G_Lce*V2Sn-D5&Fnm{W4M~MC<4%-7-f1daKXJ>bmcA z`wx0ujL!O~Rjj@fuMd6D2NJY-(tpKgZTCr!{i-t)bn9>W?PpyySqFX9?^5)wZ~Ana zzMZU>XXxjtdcsego^EzW(GEYgYqp-5rB~(Xuxx$SsykS8tk5BbE{%E&=n-0LrM+^I zPcBNgq2YO`Q$EU*pH38@3kAv6mIf52RYfSZC|Qcpy5cmy1XU?TKT1((8Jbm=R+gjG za@4sZO|M85D^pq}f~qvPD!s2xza1!~7A>zyr|OV&q@hl5K97sQgQqjS5WjOsBLeoZ4^e`$rngT~q&=@*8 zie`_arDLe%1nN7EE=(l53DjW{MNgzqSK2y>x=yAhZggu3#ZIQ$(`eOHIxw9arc=I| z6gGpF&!QEx$U2*x=g^wDlrWb{&ZoWe$!`I5T1Y(>QSu`CvY0%U(DJ3^w2TTZr?BO; za|KQRn~JWa{Hti=Dmt^8a5Z&WLyOkX$F-ETmX@ujCF`loM#{gDZfqj2O*CQ)b=X2; zD}CNdhdpSW2Q~SJ?Ej&l9ptxzy6&O|yC`rs`R=Cbd#Tc1+OUsy@1w;1l>Y#AI7n*` z(y2pabC`;H(pFDeeS}IKrC&$M^BB1urwU&5+Kc`b zmkF*=*cHlsje@R{x<*&7QTBE6yiVV5(1siIA%NxwQ20%9xk;h7Xy`4vbDO%~ri+2p zERa0!P?bBh<}TUXrE&M@<6UZcpRU}a{10gDeR}(V20WnC52^A)n)is(A5yy@y8DO< zJtmJJihN8XACqS=IRw+V5K0av`%v-=p_ovb5lVZWQ0*tw=P7-ALiwMO_frabM$@0s z+UHdNIdux7oabcog6@XVqZhRH1+98XonKP(SCsn|rN5#(ujs^UTKJjJU$};^{#={fwt}pJ?tU3j9PrKT-1pnw>zG6DTEt>LikDBArO2_lZ>MGxhsS z>ps)n&y@a|YJQ=?UufGGy84CEzEJV6)blIN_)4d~QqWhDU#VOY4NRgrNpv=ef|AHc zqB7s8_cxmIjXb|mz&HB-jq-k{Cf{k`cUtwGj(n$(@09eNiX@YBG7U+l#mRIyneHaj zr)088p&BXFBZa1=(6$shpF%HEC@qDGrc%9B>Yqw8QfXHzolm8xR7y*wifPm!joi{` zaT;ArqkCy&O{4tj)IOd1rPG>p+MQ1K(&=?NWv5fY462nuZ8B&?2F=Q#Z5ia1L60-& zZ3gB0L8X7t;2$*h2c7#tmwu4VPb%<}Tz}H!pA__yLVi-2OsbqoBQj}9CT-6ouS~j` zN#U6kpGlBOMYE_@7WK)Zaapu4i_T?HN*1Xs>h+68|Dr3u=-w|XpH0=XX+<_|$fj4> z^gf$P|0aju)c-e)`%SBV(~jTd`Z4OPyp;b9_HisVNP+ATZ zvQR4vjkM4v3+=Yh9Sc3QP^yJKTPVMk(kx_a{rA&+R?4=}UslR%rCL_1YNhU08f2yA zR@!Z)`&NpvQXzvX7}U?8F$Q@Ubl9LL28A1>4N?Zx5mZA^A3>c3O&2sq&{{zY1nm{H zQP3Gd2LxRgbWYG?LAM1(2zo9kQBa(qY(ZIq@=3}sshp$=lIlsSFR7iRE|LaF8YyX# zq&bq7O4=yRY9l%(>6D}!l5R_SDJeoylB85gC@D8kNuY8-j$jsXP#d65Km&k=0=WTA z16l#J3TQXbULYT!^FWV)f`DRy-UDR<{Q}B|WbQ?kN2-kEgwz+m2+bsJJpW5~#YO`ihz<>ZquvqQQzr zD{@sdOVLtA?uxc4+N0>WB5y^P72Q%4r0BV#2t^+hB`Qi&l&MH6Qi=*`Dy+%wPikl~ z)96pyYckXOPey1Ou4$4c7fo|C&C;}7(-KYVHLd-3kw_kz4rwwMj3+gDY4X$Lqv^V) zE1K?U3e*&;>9M9)n!+?iYl_qqr|E;HubRGSO4pR8Df>^PCeUOL{EsG5{}{wHsUB8VdYB#OvP^q;&V zG86MB?}^NO_>+%BX5#)Np2*CnKS>}mllUi}iOhWYldnW(lK$iy(SP%u$V~E|q!9f# zsYGVd{zuY@%w+tJ{2=;IeiHpBnMD6h7SVt5i|9YeCi-uF6a6{^;=iX*Y7tf7K#EEc6Xv;U}2(Q7X~U|cc6laVh3Vk-H6?SqN1pX-KbzAwupg+ z3C7v4x99l>&UJo!p6j_^-*wI2duFY*XV0uzGau~Kx@F6KQIHpmc4%HnTs#LF0rAECPHLKLB zYySWL)MfEM!vE6$9XfvGur~i+ZSU{t<9R32(ik%0i&<~*{*NYoH@-JnBIT{gZL{8( zynip*WcNPsYQ!s(t8*bcI>JKrUYl%?nLFPu<0*&KcP2f4QO}mg)Va?!)#RLcUrmlF z`orYc5ospJJ^f`e?Z+RJf2?@Fo1*!I4)n@oa^Mb2lb@evHre}S7L$(>vzl}|oXuq9 z?Cd7(8(W!tm<(%A&0%uU5BSC-r^&FbuvP-h`xPc;ur_(&JMxmJaNJ&)!y6uY&9iRJ z;HR$CS)vrJ%nGJm_hV=>ZXUW_vO&+GegB$VwFoP&@BC>pF7BJjUitCofQ6q-wyg8P zWRKD*CKEd2pJx~G*zW+mmvsUDJpVMwc=V2J#k@8&Qxo1s9d^=$LJClCxscqHH z&n9yWqpb=)c(Ow%KKyzKt;StL&qa&SJkv+)N=RnJ3K?Z*vSdJBleT5^n+)t-z~uf4 z_9iU?VbmPh${L<5!qw7Y`AjC9%wzITCtH)B#^y5lr3PM_x)aa%{=g^8oA3t9Z~hG% zq1E~d-%aM4fH$&MM(0az^oQhl`o`>|_+;l7?CLue=5GgE^oN;Z;7>2^Ina*#lV@W4 zhjeO)-A`@StEqWt2!3dKfPS?khIVrv;f+*Th);ZKm@sU2pxew(_@_-Cw6(LRca6@9 zuP!CSi^u6>J7;3ilv?z$5trcJI(RZ-0&G45KI{gI{o-nsN$}T0p2}SVW|~1gzVXy~ zvjQyPPg^x(X?Kk|Ul@jS`HUuWmO`HwILSD(i@p@v0-ej>MgJwE(@kb5g+=ziu`V?$ zba3SX+cQ`_L49Cf1LWCNVV(Lgt`~f?4Ca3ZhqdFW0f}(%a_Y%8_NU3D3DjR=9c>+q zr`>6`=@2y$c;r5VU@Ldnq6Ms0 z5f;b@^T$$K&?30K89bU!EAOIVpy>GzCeBAkBAasv_Sw6tMxv0}a%$h>U zjoolUN7(2E&%PK8Ydtk#tZ;?LHe;jP3+(-y6&}q^@3sDmH%>gnGr1!1Qk4)qc5@Tn zdl!#C56S0io-?LZy9^7whqKyn_3nL`Ihgy~P2<@Di>asHDe63$A3yI{NLwxPqQjk? zCJe_0Xq!D6TiTbP&%EeC|B32Dj~Y>t-c|J&EMrTr8|nc67K2|OQEo8~PI%1yTgt&6 z?Ww_|7quPeNZ(sjkNQ&z(w1JeapesVz`-Y>%X7HYmV2yP!9$av#|~KHDm?fcPIwQUQ(&2AF#8SY83n&=fmfEpQVZe0 zB{1h!*k5-#=0&%j6U48u;xcs3<%$)tg|Wy2LybAO-}vyIw|_SstAGFDLX}^5<4+2n znH`Ilas}hD5>4>o?o)879ljk@6}shs;TrKiS#ot-Dee!_7*h2FwH0uqW`|R>b+5b$ zqs$WY>HQZC=LevFyV7_mAsMxP+@Xh5N~O0XG=Wo2(~EZb!RCYDn1yiFOIXK;d(PJ4 zsV=3cVMu-Y)v>YES>X)zzbcL&5~tH{hj{$3*BX8N_5F6WLBlB*;T#uq9=8-u+6I>{ zhFjagsL$M!JpeY%3X|vKy`0w2aSe3_RE25RO&D*c!FCN`9$aO(B++`F+i>X}IPERG z%S2@Owu6^f!*vYsMu|4m=C}o3_yUL2q^&>Qp;s?x)P=5ZxSDy4?zu*BJ`^y1)$6CeHXC@D$|M;GSAFjZq zLGVv5IK4gh?CHk+nXB>a^5@hx!XMi^*;0S8ZM0b11xue~M292#j;g*x!v+BI@8F6k*zYVPtn&@ zVio%Q_zN#oN5het*H*uw#Fz%VzU6+q2GGyXgb}-&+Uy@tvr~5H;Y?eTX3_4ud+2bu zISkM^(_t#Qo$d}hddsfO@K^!v*%E{b>TW^c&ie;n8ww>aCo9E+=vM~cZSrgsfEA@M-tgC6_*Y|H zQbTHvnhTw7!M-K2y3;85<1jq`1zswRR@<9HmoBhOH)z`)u4)0lH-Uv*U_d`OX)!!- z9kwWA!l<|arqF$ikYQN8U0$8t3a^&g316ru)N;i?77Jmj=CTtnk$3%qv;V^P$uL(0 z+^Ptn;2mlRstA|5Q-8aGCJYx3bQsebTaxNv>3H?enr-nyF`X!YYL?6C_j4u^ZMz*cE+Vtwv>J|C`m3179QHpc_d!rp}OYa;Z$ z2rV+9L*6nlr5v=?D(r;D^BaC!^{jrIfZ)jw=cb#^#O@V#%c7_?q zrElZOun1_q4$fEzXU&151L5thaCa&ra#;I?WDqyE?ZOc;@$ zvC9~Ry}2LL1NW*=4n0Bdt<{j(C*>CN{*@)Hbec|L9X)CzYoBx*c;pUi%p%{#36unM ztSn=NmEOK0eb)hj>dZiI;V+)rzh7spr%zxZd z<{0;%&4KMFr%_MDFY5d?j{5i9=6ipk2)vIYt=p(4XUdH}EAB%7AQMJ$XY?$X0w1j4 z-My{NdrZFq2Y14jq;s;U4Db56541T7XQjauXRMyT3O4#Bj%8+F`5A5>i)TV#z{T|l zm_oc^U3b{o2EJd+)wwsh|I{j>8>Ug;26ny*b3BFD0NxL;)qeFkK-Oe|{KUT8+%pokYa?fFM+a>zR$oKS|t-EYZ zh9%jV^jwz5WS8ymYz26#9OZ8h+nBWZL?0WXoJQfRo z8kt+6iEBE2KKKwC-kOQFm3`3s*Ltj|l^bttO~f0=b3waZSZccy?#V#Or|q!*Tx?$$ z$o_MoR#Jb*sGFx|L6kbbpmAN{I8 z0`GCbGkld2t)4TIcir$COz4eu{r1A$>Nx{z(<|Ql!Gek*9seLVsEjwVHGri{z(H}8 zr@KPOAZp07ojMnERZNi+YY@3o(gE&Xh;%YPCKJP&(+hs&(7IyNV~ z`;C$ZS75*r_@_C%_<iKjo7wMdNcr^y#2^zt&0K{=uR!XfZn9&jNjw6R@|(meL2Y z>x3svD+}LdhM6qjl-%%jWoYdNJ^RDpQE=*T=+hSt>j-DJg?anHi}PX9O<2c)w$^Qe zKkd;WWGPI`f^H9Iz}4CCXY)O9(|D}-Hq3+(Fjd;hMO>& z6~+U3$Kn%Lhw?G4-D3*6}ScsFZ*I?>!oOJyv3rkbKwMU*lIl7t)6o% z2lu#4ft`Ose}A4`ZbLo!_rcKa__KsH_8yCc1D4Zb|IW0&OkL^6dQ&uJYQ$)yIW)&o zG@Px~nagzKOYNbx4fMZk!l*nG4sHNb)2VI1X?QXSrVWBa^~RrQtX}9q$v*kKoK~P; z11Y(xd|FHeQ#48}hDxOyy#~-$(iIp`8o$*T496{nU)IBS${3W=ChtRvlH=30(MI){+?7AH8 zkA@SUKy6JJ9vQIcz6DfRWHfon)x0<0qT_H~7!2AAn;nI7<6#n=!nmqlm(P(y`H0!k>4wZ_htYyb_7GmV(`C3SmgZ_8`~*!lCwJ= z=sX{vEK?qJlX5lN#-or|Iz6_?7MOVqO!0))6X4!Gu$LuwUY-uy7sAg~?!fdt)Y*J3 z^>1E9i_O;1`kB=x3)5K+%*uEZv^ju z;hr%IU|s{BR7BTd4RyA-h#zVZ!W(&V!%`lWpM8x#)R9E2H#8f+70;t%j)|Y zHwS$dUVsnG|IGN`0s8iajs0PI2rLr?i>jYwe2eU>p0>;$8_!pRpPIqpU0}_=uz(l* z(-RhTfsM7^f6a?B#>PNn66mJydi4!7F~-8Bx!`w2ZZo8R$0yiwP#KRog^^dB#@=}C zm2_}GPRtC4e8V#}B;Tt?N%%6@NOMPdt-)9I#SiaG(aQbzuzjQnW5Iaz+^YS-`zLsh zu1)C`W5Z$Fti0uUt>J~Su-*)~X*9g(1YHujb594jYB@E;Y@^NtfhLUa8d0xip!I*N z#Tz}*_P6#XX6C0qXKBRfT_%)|C#y3ID+VvsfnT(es+NXaCYU>mcIByoHE?ULO4L@V zDfVudfS2xQ4h^kpipDf)GQAYKT?$6qh*WxVeeGE7D#biiOnd!%U5R!w42M%2!w6-| zgY3DdYAwxi<(R+1{!&lP3)Ja27klrv#NNIv6O9e`uypZ$GCZS9jger zOkVVN)ciHQEEe_rj&+;Up_KzAH&?>U#kjN28Q6a!HKbIdwgJ!Ktp(KYQj)g9cEEzg zXgzYSsNKe~b$Hj={Nb7t@P*!f2Rl9;TUXQHKJTNa zl)I^Cyhc6U1@#3M(OT<>GL+H(DRta(jQLf zZ=a1XD4SNkO;WM(o6ZYG`N-<8c)%_cCXRup2EkNi`6eGi9-W;#FHD7Txu{`v3@o~Y zo|&k4)yI!k4(mI$+=|CiPol|hWskeuF-2q289Wxg5}nJm!D9)JVXy93)a(Gf@fi*+ zguPv=!WmWIvVt)E8&~U}g*LNbSY3GP1@*Mk{>YSS$klJr%FfBKN;!D*4z2soht(QF zrQeLo|Cyo@*&J?DJQh0zPv*9U1=nK5cf~WSwMQ_<0qdF^!?(Fxzw8Hyr%OEcT2Fp4dCwA`-0{XIg*D>cLj_~7*2por z@q^24XfYJc-^am+`LN}FL%5*5TmMKIZ=7%476vhWz3lwN>d0b3eq} zFT26EckxD|H~eluL#uU%QHW{v;8Q{vBjzq|&H6u>rajqmztKNsCA8M}n`0YxRV)Wb zE1!Q|`^c-#;oENgVEv3RTsemhz3|C-YdHTRUUFUp_xFVr+Q1c^p;G{y@d(Bb;{KmS zsON1uZ1WTMwYNf((#nnWZDfi@Tb<2FYKo=RN@M%)cd&X0K3QB3U-`tqn-27r;eqge zEKEy*`Ch_W`{0}waQ-gtKlFqe#($u;c~7bTy2jq?n@!OuuXVTcV)PlNXlm(mG@R5J z&eeILgB{U)y9L~E0gG~Of=7d3pw2cJVaTnX!u5((0vB4#(Tl zVxgI^eIlGxg7>wm9sD*B9vcZW4}&(Ia6nh+;tJ)^L6c*5?bchP4d zp8T&QjD1XB8hIKPRNwPb?rTm}u4bPH-+hD2eet=U_Czl1#PfeT;qA*7v{hz5+}VrP z^ZkMei%l4TIkCOEAFQEsfX;U4|3T*l8}&zSHUU1BbzzdTC<9YbCjiH2cVk3u?)mtd zdVVO5ow^peidI_=osrXP!RiHJqy>ES+JtdNxr??3kxwmyu|r{)6Vw9T7@#%mvzcgK zPOI!aIt_AK8I@p1taHDEy@SWVty$sT|L{W|ecdCj;-8v20phE#a@=G5_WlSwx))ZB zfp0#-=tkV1q*-Zn8R|K62-fIK&BK1e*e$d>vJ+bURNOUew<#J&I$>RaW{s2ucxGK* z`pknRFy$*8(422`tS>AZ1eYn#aDNbT;RZG_*?z-$`FhWyl3D?BqxzO8G6yg=nHm$(`@1uj-T@Y6i% zeE-sf@udo^KNwAl=|s~;<$-S%M05Y~Sa(Y2C&P8JC`Ttea8IW$E!2PP&Qg-I6?cyL z1Ot!p?9X-B*km3xhmJR4oajNjO)8;D^Jj3WFM95K4X-=!mJfG=0m?Z9Y8S)hG*|O0 zHm-J%r^Z{trp>6W&NvfBmUXn!F@_fBCe!+x0x+nXv{JvFs4V3ujnzMD(yJT)Vk9~F z4!+GoNnAVlTs=R!7j~7`yDp>I%HchA{`ZMiQhw56KMIUdpV6e-4YXRQUR|{!R&-qt zE4_dz7R*7LzavL0`@O&qd8;)HScHvs->G4cdz1a5Z~3nA!>s)Yq4$j8nQDB{j3dHDXc$io7JtUhF>qBfbRU8rB0s=h6YMz2YrQaKndJ*TA!iMF(lF_rFfb zoRzR~HR!#L`wJvcLv6ja>aR@Eh*Fl`QfFvicEb={Fm{@AV1o2(@f+Vg%0}$^2UuyXmvLi8Wt^xA1rigXmlukD4Y*QZ^I(* zwlE+wJaPj|Q)I7g2y)LI&{5^J?YSr0Z1~ug8thNPwE=izk~^*BsZEP5l`CG8gbqd2 z5ATjbx7DRh7!Bf~zdycOT^;>nU&84+H?^Y&ek-f~8ml=0!7yiEdUk@Y~j6=xm)A zE3Rsv-P#%J`p(DFw4d1iP}?~^U6Et7+djD&vgJ^CZ!hd)&HbGh!iS}(VW-X%4Oowt z_Kl(b6>Vv2SvlH0{2d;RM3ZCQCX6vWWSpOZmon*m(UrD%DUW85`LB?@iqi*7DPQ^{Rnh@!;9`i^Y1CvtxmZI#zv!@J?gp^E44mO|Fj z%J_QG6pgr5@Us^T(j0W@E1GZ9dD?Lkkh@fX>DRHV)eso|6?=zIgiSx=f#Mqd0<|+$ zU<97IZorgn7{9{{mUe`56+_yWM;_b~j+_Dy>-@~T3OtqLKj>G2nrkY;xuwydkR`1j ziiWRepj87m6Na6>nz>0>H)=ONiINXxQPZ@3X;>LKkG{c{$GEz` zKa9{*P1fS`7FFS*lhm0*Z!P>5tu)t;gOB2brFF~#c2h&u2c zw!ByYPj!Sji^C^rSUU3|oOcpF+W_}WhI2Z@$=YikY>Axv4Zh0o2nH&jlToX}ZjM}S zG8I0LhX?fiE()QQaIJI(Df3qSGg^6$#)oavVa8x|F4fY6u{|r+HA;YvkyxEgD+@nw zJY!V^kF7fdk12~7G=#4;?+mzk6#T4j{NQWuDK-;EW`)bAQ$w>P{OMI6Zu6!7R+?dZ z?J`BpsvGm5Y}u&Z50n5Q?s?XBF` z2P;&ZU>td6>(o^7BUcawtsi2vc=J>5V_~!%J|91O8m2HOofL9Ve`*GyV?z zzK6EfXhlC$=VUf`ngzzTcIbbkF810M!iQg#6Nu9Z{0NZM{RB!uXIK$T5I{gTk%5pMt=G^?L*f>sK+zPG5JhkT@rwF{_V)~NPBe-9??Sr)Uw=ad-rnSHW8-wBg zXV9iBt)KOTZM8={J(>lVJ6`_tE7&Z02vduCJj6-$d%;)IrF&kbg22Uz?y+C>3 zm3OhUz4GmuERgRgpKwTfuU?<<&!pXO)>!yO{j+8j91dUM%nWXcI^l~C%|G0 z;DW`l=nQyA*{ZH}k(+BZacnO&gm!?hUs7ktXt-UY+<*zDXjIZ^)u;eGb1yF}s&lq~ z-O(_6cG&tXnn%om0Ue;1asorNetfT#u~Q8Fr*#-yrr#zM*o3@#Gkm@Sy6%TxwbS?K zDsn$%BbVrxKJWCKsdD}0CCwdsm4R8N9%ykGP3jj!!xvNGiAOL^-fOM6YrqGy!06T* z8w0k&hw5WZwYNLuIDTlD1OM#Ty8Gp5nYoTjV|y zhEINID~~nMP&jfhy6x6j)Wi$@2P9+NZ0#!6*FMM_MI+T`(l@H>G)SiI$n7*!*y=Z% zSHDrR%bRBtbWZHiWNJI4eHM!gw32@;R$JMdFzz0LE1II)lcUfrKRVy&4vS8LXQshS z%H{>sL*DwGJ5zOvV5&|}jY~EQjEw$pvK>6QnO4@;fJYP`Zf;BKrDbD*8EBP3XP>;j zplw`D!b;^EXz)I3kWJ=svt6wO~m!dB9xL|HVnRz~sl zDC9#^Wt~`CC0lfLgTDP%>dzJTQ$wH^+>#xx+(Rq9l`-~zPrF-!VIDWwH5ZJ1j&7ST z!|*fku(DgBTBAKsCO=re46;*<7q*k9zUdr7pvIGu-D$;jJ`9Y2!{Xt>B$)aJx;=)) z&cGas4->VoH!nXW+4Os*5>u%8ukyw>-l9oYoiFnVarmko=`q2@;h?v zW<2{sxv9s0C6A!yb}!+T0Va&Q?_nvO@%yX{@Sz>_hZIM8{?x;;S5czMbpzniS@7L> z7*Gcu(8{_}E$(!i%(J#Tc`xtIn=lq9(pI4_w0=asT~-s_dVA6ztk$Y;E7C5ZQ!ZOq z5bY%7gex}@TQ=*-D`8_ zzw_)%elD;Klk4N3$Lfu{W|^X~OtW^zaP&W>9H#ejJm#Ma4`{tMZ7BABRu*EIPW4~YZ@@d1 z!JigQ;1(B{qaJk60gV`*8rB7-X_XzQcxj^|@M<@?CAb(o?1NU_ZlP!HCU8Y0nzyS5 z9nWA_SZCPe4OVB<$+~;hkY6f>EU*2kxypjRU5tN5?u7TWHW^WeJL|~j+Y3{V{|0!s z6!rhz3@a6;-GjLLKWiem2K%(-y$|=b)?d26uDg zy|m(Rq*fwDiz4q-raCGE@|Z8&pP*=G^AqGhih1I0Bj<~S?lG{dzOn^#kT-UNk@m2o zd|1%m6b+9GaG*}##ZN@%d?jG@tJo4Z5#A^PUtGZ66@%gLjIf`w{go7HFT0DcHqM3C zZDB_B(&Ab*Tj`g@izZU;rWq}ZGD3I8;?G{nyH2P>ogLT1_)N4IJPz(w#&v@7qWM3e zTdzqbj9vz|1Sy|W^fmH3XZ+K4Hq4;h`1%{jLmxpm#nK0~60CfdlF-etsMdg`sv|$U zPd)XI7O*Rp5mG+UVjYlrm1_o&~b5#^neHeDR zWrt(52j|xT+e0m(r*@!<>n|-VUyPTYXkBbG4SC9Rcx^WHUJifmfqU-3Q1#4ZQ>iCH z`#6StSYPpvujZQ4$|lv(8llU2tlQlOdq-LGmEGO}k5%UD%e593`UF4LVC?#(jO&{b z$k78}Mg8i*>JRrM>HcB)kuUjB&yz#c+~hH>q>=PB8gbEhXpP4risG64PWYk6P;~a* zj};5G?pV8wug|Ckt0gnS|2+=#?SYjK!s@r6vlaJDc8ABKVE5|O^GIt(YgcMcR5TT# z(JWefUR|%zdM9r*@l1ggwMW+bwkaAbYM^tSX|UFDcvb5)FMWOP8s*ZRur5Hkf&wj( zAFJ0{C`Jm`8Ji;dW<9OB>h*_u_PmGf<&(45k)KDy&%59St$M7rHd)k?65re~`kD#D zVHylA36C5{=Uduw*q4AUS(KgbtrhgY^RYo%HDua|2j16*O%w1;rvR8;yCE61jvk|( zotL3_?|m@byaPHYvpTgX_dnB9WiwLGdY$1M)R=eo_7iNm3mvX`q1A|dCX7EZu$RsN z_tQSbu~GP8+he%45j`Y>_IGmX)M)5?N=8`FMJj1_c(1>@;Cq1bIhsMzb0Q~fpoZca z6$fs>KU4C-AKI;ckj)g0Hu?qqbVd6&wH|rg4Gqs{hMi8MbE)|-x+h%Z2rm_all5!p zVSli+o>nLmejr~@gEjPbE#73}&M+$&YzKcAhP}!|2i5kWCGtnrA2b%ZNg!Oc54!8` z1`O8Vvb!c)-5v=WOWP~Ik>fky&nCOzwyY+MaYJE_SolD_wB8DA@1d1%Hf2QDhv1*2 z7qGvwVQZZ6o4;0nxwLXwUXhZQsoc|EQCtmI zy7lP^D`{7BVOK25^#uQ%@q@jTw;BA7p0n{X3_b;K#KNxc;lKUj`klDH^b)xH2Hc;I z+DdC3ROep=o0GQ6jDW{(z;dm+op60P zY;qj#)JT})333PrKaG)@u)VpyJ4f}a`(lnflzXb&-5PmqE;v->{R<)QP%P9{S^fHj zDcP+2?a!B{XgEedi!m^u1WeP3h3fKZvLzPvR}^Ze+``0n_@SoGl(uP&?4!QtABC6p zO@g1Bz&yENn6mv}ui@J+=ipbJZL5)roU01=ywdF8^c?xIJ9T;~OB1c^P_gIucDE;* z5st5m_$q7^(Rl^;v#%6^7ZAaTP`dz?I{Vu>` z3Kj(xfpHhHFV!u5+?1zfg&lLk!3ANSs?bUM^`!+|dp?$D;oi z?WlGrihNO7sZ3Al6$5p0?Y5`oaw7;5ChD)u&NU+ADZnS)C`!=0#{n+ct)F z%hobs?B4*D%rM%!@ivRd0|AOZJq}TFPIAV1$ex|xCoAZB#Dvkh7mWKso%@wvo#KET za2vaB_`@P~;Po{0d9Jl~+njfi^ z4qAJ*D{dATGkU}8L9qI9xHu8+*7(qbij6{q9!CEpn0yxYSP94VfPa(&*`fY$U^1=D zRVJj>ds_EA1e@zk)gOjDx)&_b1K!a|&I1#Wk1m714#K^f<%?*P*jkM`7x#c){NYvQ zTV9?-wpI3^O9FCkeZ|erA#dIakNU!Qjo}#0GES=h-XL0OsZ~sjM)gS=c{~;8Uvk0h zY7Jz3b2vqZJfee;-^M}z($sKBXZ9W|R*&gPD|@u2@O3psqn38)PAF^COe>Uo?da7` zI*pT9ho0Yb1q{6lkEFwW;-y>2$#YjHLg+dBvdiR$Ic^b0fQ*=heQ@`Rbk8WnQE`m+}uOALztA{Z2q{ zW#KB=Q)jhTu>2`nnXv&syqSa!549hfS+mA!t;IZ*6+HF~&Fgl-ipq*`3M%F)tM%^& z?e3kMlF7W9I1|no2c2}HqlOjoy)d2~;{==Tr?!Flpqrw{Hy2IOxL*p6nTsYBwGZCW z2i+?CLC>MfV798Tq(+G=qp@PYR%Gtc_~+XQm|7l|(bs#cH%6Jse+GN5Uv%tt$gm-!|0`^yo zeP4SYbJar%1!8r(i?Gd0xL*61m#!mE-wO8)hb#1}8EeIy^#k$GB1hQd4mFoloHJVA z_3n{of#IN>@R$~OEL@p7e;YJR2tf0?S>U!%EUKY=!m47(eKNt0f8?cX$gaiVp2pCr zKWwboq-!K{MdfDGm1D`&MLt;p=RAbXD$ru6FKntlJV!fFxw@cL|3%P4=K*@^8?W<) z5*=ePoRnLhTiz@%!W%+YcR0ZpCa;HW&cIk@O7CRH4@>mdhuStm4rvcFdqAJTaMd_i zWjYL1rXqeh@-Y48w~y8=XOvmp(1Y^jihrs;G)3dZY`DHOOxKzvuru1)zr(tM8Uy!N zLVg;L?e@wHKXO3sp*-+$?FG1Kg=o~rd!-7%nVPdwdrSrAcSff=;)OP%k&>q%OFLlz1&GW11s}2+3 z+Wz`-z2F*+ic1$D57Y{J*JtFP>dBXNdZC`q*WJ>{?_QF2=bTdO`ilJvngwP)9B$Fc z-jKYgZogGuUopPQliF|kdkMMV4_G`WW169nE!!7L#$JY*g5bv%@UeD69hKD?pNrZy zXfJOGjTSM5_(?l)-IX2f|AMQ9^HZCj8}!on5Eg~(s1N&w=A@Nf`U6Uy zYfsj-Jle*zhnM}}p6#&Q1K7jHgb}V8#Bw3>HF>p<`biBJ`i#ADp*^CI15@D4!g%Js zG7?vYAwQiDpKDe4b-Uz^Fj(aqG#_nM_d26qy}2{>cqtR&(8d&vq{HYlxHxRM5dCYX zV`oh-VcJ*>2Kl-TOG%x#tJ zFZhVs929%(S6g*f&~EW6=y38B3~y_~D6QN``G)9xPpgu_$;fu4urbjM)>rHut^D43 zv4i%restt&ff_J(X1MSs&qixaJ)PJ^6wT=q)JvuHv0B%M>nr|s7Y!3<#bB zIty7nLV3KcfB51OH8zggfjnM0886MJBb048r|g06Lay3rUe2fc2U_5(n_3sdDw0|9 zo|4Y(XzNoL^vi`Nr-r~t&0-Tderx2?2xG4}q25tSBDAWil@nVYSAxOL&{vs}w}#!j!^d4a@d$qLin-#P-J8iJq?P zL|^R%=Z?Ww3BE9*KD?0?j(ExE7j_dKzXreFgY7@VhyvWxMl;mm`N)ND!9Nvg<$ECP zV$kmHX>f8@6GqBfc(n$7d)Ecnv>P)^ChgxvG{adrgJJt?aFKrBeMNu8s_L@?@eHy{U(gkdYfyf(7OA5^eN*2GcG_+Cs|jnHMX2QM#PfZ6#jgk zW^&&qSY!t5sJ}upcpdV`o6tvlC`G4mzk3pFq1|@dIBH94NBt$P!y27v@q=c)^LoEy zlF=u2lnFzByum0w5}gkv!a|L(WyT^{Q)}d_Daei%*gHnK#OmKE>8n+6Xf*QMRj}(2 zm|ZL4nZKy*?lI`7Il4(nkF%1O@5y{zIRy$~V?mHa=P7*ca_`-qZ+rRM~sW zJXkd43N|iO{Oqh%vz;RC309Q%`-?YjX>AguGjEnUI~O#bs~Cx1h}(}>!tsON0H zjhs`F%y+Gnzv?^vSrgqN*TD|zKkgb~YCJUyjL2qKWVr^ml=qrxZ|;D0^a|C%8zXAM z?%G|NmlOHVC+=So504&z=K^63t=$^dL7r>Cz*8oSKa=3`s&L<9G-;>!u&DB-_L(SY ztTDFDTHf`Rad2f9$%-`WOCkGP!BUFZy57OwWk;Zg#;Z>{wGumk5~nWkP8--#EZGUU zh~mJ`<3_in#f_6-l_=QG5^WFngdvg8zJLj1>qL0- zCVZrR(qu3mhzo_6u0rn==$Q#0j<o;HLcWXjVAuJ$Jr2565a<9MuLnT9H!H zDpNFK<kItwjP|UtZ9oNE6mbcx!c2`#9y*9>S4nFjBuJ zK2Q!%_EQ{wPVz?e-fWM#=enLl6@k^q zG&;@vz&BWa6|`s$`=#^c4$~=)<2sFSV=^U^C&B(pVU*6YmQ=1LQ7gN0nis+qD;79Q z{asqo))##d^)&lrSJZm8u~}?n3xkXDV#SX!FzGb>qV?L#23UPw`y)2mS304bu(RZu zIwKv^lzYa@OD*KL&+uGG_0>$}V+Ows7CF?L)jKcqxq7H4Y$?|4 zgPcuQ=jbb*u9@iOHp*LHfy;itV%2EfeiUqT1ZGz}S=Jk!GWTFijTHlfEJ2rN9Y&pR%<9J5(-Ca&70)_ z^1EZu=`8Gd5qg}13y;7D8=!~gl&LPrKeeLVa{*0?PKRllivvDjV=?V;+5ekUv|hfh zd3oAPeBvGrm#u-$6X6_pI8$pti)zRjD#Fq=VO}>_MQ^9hUgUYoZnaU(nU~O_(_?5+ z2~DPJlylsTob3_Jn9YRop$d#^2faqXH4ETm?aSN5Aa78{Dd$UM?{~17)^TkfAv-A! zN!^27Y7u;*n50l^W53n|5kzn3h>k+SLhu8FCB;RUttsFyAFAwRp=_1tg&(KZ{)%bCXD%mVAOiJ{W{dp zV*cxCIAVsr@iv-Io4Zi5MiI+xd*nTzOc--d!yijvDG%6D{@kwKUhx)febYPLr?s#hO8W52IvyM_9Nqd|M0VS45q!Hu7JMmLHoV4_B^WRd?i8 z{or?fx%0*%|5C23#v}RvUSYM02x_AVv@E=@#2CjPoy;I>##X|O9kSjleIV0h%sqml! ztaI0d(Ma#^Tsm#dQ-Ai)iQIP`T9s1&yr@X8{A_g2)*QCCfvw*W`4~E&%%OCNtgKBic@t7hk+>6BQDomHYg7-Ky6 zNqHqPlkUHjLdlUtn66P`)jH%=17SP$`ObIHvyNt>a*C0BlwFlmCodx(62{BbVhN5c)bd% zX}jz&{w8+@&w?|x%iyOeCs}>Waj#iyWGD+eZbFB*W#Au;R~6f!+nYCNTYCkp+X5!* zyspbhEb1ViOz)3ep)EYy686>al!pvMez_E;#=uQq;K>TqGg%Sizum5z7bvM|(Bg2- z*L`Lo2gbq6<ZN}mB#f7cr3mHG?W?my$pG**21%IB1dWk zS4KJiLR#6~(k~O6XpbQDGxyv+3kT1I|NfTCx7Vg<%vlOY)qw*QGuY2VpUN5;#wuoO zwHlqjxWR%MVOkW{ZBk_Nt`@R;I(|5F1!mj{XRd^gmcg|;2m4zdE2_0j9(k_qW1y{iYjwflcks^e(b&zK&Kl3R8-3lFp&LJ>t23)5dflG>K_S#cYTltv^SFv>bE;xJ% z>@fv)QubiHcA$EXq9jZ`HhemAz+%`)J9L|mBJWmC=64oq9;;TatKGxbD7mj4*shwR zi)n>2q`X;dJev%!UV^W4V{a#Yt*!K}89H~jU8khFf8wgMc9brQyYy{-y-#@o-Fa#j z@`ctav4l~YS?=nM=pVN~CUCYRuT@5W^^1t(pX}Ek+DS~GoCBnOkG0Mpsi?yzquUKf(Lt2d2-0k9oCZDz1?K2m7 z_Es1c4Ld8|OVc{8^e0MMYW9z32sc`1g)MWyh3XaGY>~(0hgXZh)f&&O9gx2?hMU{N zy#1iXXqaUhyfzP(UIOz4!Bnku+Du1I9u8wQR#eoP$zIl!ywNuscmUlhOoOLdLO)qI zBOXh0D>Ce<9=Ny)B{zTJpSFrCDl50JDv*-hesJtacuHO^rSJE+eCwpB?8ZH=I?A`F zmG_%9hdKvcf}PZEyAJ5ETzdrnT}7_W1ZZ@t348R0C#S+@!7y$IoP7Z9I0z%cpvx9m zYdIXC`yYEFA6C5my8`kNt!f`A=Bcf|?R%IO9YWwW%?PXYot_;;$)MhFLKk?dJ$%&` zcGM_WS(=aTM9Eu4v;Sh+Rl0M7`fY-u@e&od=aMbl@&nu7=-ao7Mh;ZOo2a><^l(a= zXoYfA9*eC{Nl;Drr#dVq*+%7gbdRs@UpbPi+ZMtjTA?pg^t({`)r%T=j1lOtODj_w z&B&wFH?lW2i;a#$VOk)x+asQW53j-faj=|b9Lo#Hl{JHmmgg5Qq9orKIH?mXR0Hat z={C;3qFv(@{ImwH90%XXlUFJuUsZo7{TcnY--nOSz-r+zgGR`|i;zocrFLHS&Qo74 z-h=Yuo#ELIFjlL=IbD$-_Jq6o!K?$}%Aqh!HTaD~j+z7)sDD0JBv3PglD}F#9n@U5 zzXp2F9thpn!;v@OYV$wySTziv>X4RT@m?YDBmH8pB=^+)qjePBOD8V=== zhugwx25hQ1<(Wp&0Ob=Vf1vs8e_>_KJ8n+gIix?VvJBol1*d$3TMN;;m(~=U`XSf# zgJ%~)U%lmX>S!!X#wtEk^NDfIxUZC;tg}#E18Vz<{p#1DHaW5>U ztli-G$UbA?1b3KK5k<+O$Yvig|4Vy@PkLO0)%Qb3_1H+|uNKdy#9=zTHyN(_*WbkK zDv1);P2sB5G`LIev6i0grJlcb73FyqO)XU>Wab4*%4?oF_X#;R3;Gn5ZrK#yy0@jI z++etE4!p7xW>a4(^cVSEH50~e&A(Z6CddCQB^Ter{I>MZJ~|K3RS{7qS4uMLq(nhQ zL5WQ%Nq2w~3&B%b_t{=IMI&JqY}^q}_(O}w)L0+ZqI!LYFxjMWLcw)$%Z?^aNf?-aDuv#ncG+nB8|E*EXhl~;FXp!I_@ z;cN3hGZ*R$PbR^bR#=g7E!?84{!FbkGHCzqRx76VD4i`Is@>lCwJF)7b>Dd9u6HOu z&|G^fOXQjSS}*TdX5MHtXb7Jq(fWp^a7bgAL1WU2=l6;wxM#?-)Q{+?h6(tFu;O0MYf;Fu)FA1;J zhx6QFkoxEL&B#q}z-{XJxoV;Fts!vsMwq5=$|avE8fz7YSZW+|x_)U*-UwFFdST)OES;GTc8#W=Oc@IY=x-W#V7IWcZ z7x*h1EE|i?lY(KwAlRV^d|n*x%L?tkVWX4!jEg+^^BN@&)w{<3TaoRhB;yX~ruf`e zk@jiz*qbjXUz>y4MmB))W8izO*M2@l-j^SJLR!Oi6Jb^LtIijZ^J^sEVP}fQ=6di} zUsz%`+_4?no`ZiLKp)MgZu-23JfY;G;+dWi$a~c@XK23e+@6w58VOh0AeZ<~i@zVi zKbjrxYxeP5O-b8naJYKfzkg>rTLnrQS;OFO*kbz}T5A>TsJM5&=FoY^C=WOUpY4TH z^<~{$iCkQx)9|s#?fSxlZDEjBD03W@%8o@yuhFoQ`b@9d$dUTePbf;eZc!Lh1J+iKag#=gcJnFe zxDN&^T7Ii{msQ>iccb;Iqv15oan<%AFF6kvYNlxZ2KkG|#w7IzB~6X{S_v*>5-?sV zW*GF7JC}WiU-eY+=g3PFVE8R~E*2V!Cnu|w=4UAxpg#6eJgLa@w#Ji$t6XiR2+;L5 z@}0XdOk5t1JV>>9DKd1}Mv0B0+*3o5$G3#Li@_h?@xvp{k6U*mw~>D)YsImnJtc+f z!2;#r8}-Z|@^FkIldE==hsfK()sY{zhV4~@-%{kI(Qxudc()Ag=JbGGD_{<3+g_vn zDy@7MX;tDd+Pr9doC{ZKHQiwYa;R8m4RV6kab46Kd#KkfbftWT`tUt#YFC4tzWi?r8&OIKVqa;e}iGiUUCDCNF)AI(l;Y_o=_Z{HwW?+ z)lke9xsm9tl3!UViB+9BKJjdiC-9VVS<9o48*B8dq&3}=g_L*(z!v^+g~pct3y^~~ z_r2PQoEigLJ%pa=FtUiW(rA!81UX_ETz4Gidj;oe)+p^{ibjeT+$e8f*p7Vh6fCYN zXi*}vi O#DeoycHG9nu(mXH{9v~4`qS5AJWSIVd^Zyy4spPjNRShQBkoKlTg6| z6$4u_Fi;UuLBPO31iJ&fySux)ySqD%-S7F`^L}_fa9?xHUVGM>SZn8hV^=_n2TNYl zOf1TACysG0u8hLnYab`7f3KdnOpyUJj#Fzr&mLlSQw#K?ko>oQdO6?_V zB6(SRaTwRjwTRf2CDM2w5mP)9Yp`;96xFDfb9RxBwJ2w_G>56rgR3QPJ}RDgBnBq_ z4-PL1iFa8cvxG~|&Uy08HIlCry}KNPreBa|7jiUL%HG@ysuR-VP*ErsWII56A#9T%URC{-IKh6 z__*ZzPjq4WCtpLT_9q*(wdoRZ>lCpDV?Gxmc`bLqPL`G&#B6=S70po2 zf(mb!*DiC#7~(v)hvavy#9uYVUFF2B)Mr-K!$HZUIYm4dC6Zh$t+>reT<$3jX(v_~ zA&%N8_PQg^VkLFg@okWprI~oTtk^K8SR|#GB(XR>x!5?nSduo;{*qr06XSP_E?>pY zR9$+`x`R2NDg9ji?&K)8x1q*UBUbFi-Jwq0YqI}i(k1tQYKO9(VgrA1PnfuPuoy5| zY`;(}wN`ZH8g27_$s3P}4yXwFj{Ovt)?t5nSTiE zaDcO*+;cR}FYU#h1;n?kGYMD9qXJ=KajqR^QDfV=!+&tG;^4w_r8`8)l`Gu*g{8ln zTbz|o%u!x+YcASEy?&63FVt&@TrzIq(BElPzN-n_~UT>c)3>z;xv=E1IjBa#K{l1wm-tH(y^DN`~ zFO6^{cRx06k^G(KVq>RDc7a~=B%fI?ZaN{ZWF_yy702$fvNkh7EH+X+YFBU%CI94{ zq+cbCWOipUWP-SJi&%$hdGKB`A1rk6Pye6j&?K|C8JUk9JCyjMHa8!L2RO^BxKVPK z8KNT?G<91>e_OngB5l_#Y+jfJoalFwnY#4X9k9*7rSh#?=v*_@j-O{GzVF}8Ju zC7*E@d-{sLEydRyJ3jW4{C=buI!*k%P|V6XLl|+ecwQr_7tq{hYvaXgxr`_^rqYhZ zND~+?b_o6KzfTM)dn4 zK6cQ)c~XmAvWY(V#MZ^d9_2-UPw`)R&yVlt>q_&G<9|urOQPLmmgF?d!IRiY7Aj3Aa=Ow-@+ywJo*Z8arI04|H$|)) zbuyh}^lr`#?5i)|PigCWxcaPQN46H{Z2UYKc}I2GmBKA@_pOjD7i0DvZXo%0M==>k z+gx}zfr@%SMIFYAqU+SA%6c()tym1bGv56m4ppZ}A4Ns|9x6E~ML*^9!i4Mb3iu{WJhk?JfwE*j0@q3c%o)YtI5nbkp zHwTOTyNF%<#B@~hXM8;UTVoDp%p1>1p0P*#xlSCjOgy+)Jc&Gbz2t#=#pJ9}<)2CZ z@>g`uDf{!viFN7Am3+nbm*zBC9zx!yE|VrL74pv}$?5UklQrkZ8fmPJ|2)ZY)JdCB zl6|;0xQ3OdP$y~Hv=CF0)7!+hb5&{5RuV|_o&X253~~d;_qkrn!&8RlUMrc8N`5uwEUzfC1GwJrhdlX zkmmhWvE6mi}q*?t&+2J|@}|dq@swDkiBQw#XxvPAYbKr_mnd z2<32E@-~ifO}Rgib%!)RchiO=-J6TZk3@gGTb){VHsf6%cggwdiCLMyPwBl~j5JZB z#fntIeyS+@LTPGotyFiZf{GVqQ71G!5WwG#A;LB9rt!xx@;E#7SkvcU8nMeqsaq zUCwCNP|4|rOW!MAjGHT7WN!XhE7`7;*W5=7Bp0J5DADWBT zTZ$7~i+^ASc$|EdBzFM~wKcYmcnNFsJtbFl6|=dBqd0^5O*LM~Esa0%yhSc1|0B&B zC$SPOD>MEXm840^+LDJJo3h@1>npu4^Jwr4$zPX>xi*S+f4j+k$?Xq`zhUo#lC$j- zGcg|rY?54KmFP!p_ok{hk-LJ_bCJ&4>Pz)jtSFeATFFh9*z86=07BjMTrGcl>wD} z2jch3VumAPnGK>7@qaf!a!O`FWmdC|S)@6Fr$1Q1F0PW-YsZP}dW*}s(#hOHa<(So z_@-i>0I^;OngL?RcrjqD=*XFTBUZ=_nbnJ(X}A)H6!Fs3B+CmJ`J_z$DI9uojkc`0 z;gIHUICy0z9EMJ5q<%r;X+@zlvqYAHpKwk8h~&&7y;i2UKLlHYP? zCOMYlO3402e{t4eanEY8-6iqE7cn*W7`v4HPjuMBGldmxC9en-b5Jc+c<#`>k2LAJ ziMRd5m!9GwM=|K5ta%<4lT8&jbrj2&69bcqC%96W%ALk>Gd0?z-1GkuE;)T?@m72B zBJ78!J@Irf6%}XovrujBah`Dgs^qnw#eG@TOEM~_4eLfMbGGa(>Dz7+#jJNb_ z$-O>7)`Y9DK9L#0%8=@nwz@qMtKAfqWrsKe9Kb!>jjX*v$noe))21Tr zx3TXQ?9^k8nvqvFms1a6^nRqSG)>!x^LmI02a89TQ^lAyahs&6M74iCCV2?adwx!G zLzv>U2cAwHt|@tBGM{#b3_iyqx0s4B`{K zNX${AFwC1;`gpdc&mlQ;J~3NyaiXi3q^jspM|5f;W^XMnqu)iWqMt&fnbu951?^0% zW3V)Hsovb}BzsUxHJVAD(NG**Ppn;AEJjZ2*OYt-9`u!*l{UL+KbbR)NTOXZNLwR0 z-^<=d@~IfnV}zKTSY4hf*?qQno|UaSD^0^8h zB*JxxXTx4k?G@HrhRpQx<2t?B3V0uhMFLP`)9jK3UK|Ek!#z7tnJ~PAr#{|;BoeP=%{^fl@cFN zGr#e%aFjId$BPFRi=(%R9>>H1mqfSQV#EWn_9O8ra>yOY@2`mCPKhmho8mG}HQvUi3SOIw?F+n$gU!$}=UO znI+mUC10R-V4h~CdfVYeL)McRdR$0O2bb4gZ}W@uGl>`e?K8c422kIULfj_x4Ze-G@-crL)t z)I%=vJB&8H$apDgBN5f^Mpa~MDhp4j$?MP&&3?8XZl$fssjH^gY!@oc>0V-~Xwi9y zSd(h0%xpb2Nt$(&#XGQeyyV2xNq$zI)9mGpg;qVabp|z)o^gI^BF!M8`hy5;DI-nV z!eY1FqB|?kHKMwJc-}}M{lb)@Lt61dCh;O!Si)+5jcR#WN&2?f--W*u@o_M7!Kb&j z&R`}sWNmDKcX4B-e>_ec1bb2suSQ5y5Rbyyt5B3QOT$IKu3~T2(oT5Sm^s*v*)fYa z|7xZ-VaVsm?+0?UfP5`uuVdtKA33exMeW_N?8TVN!EeN{KQ(ZmquM-Sok@qa-sHDQ zUFn~Ci$TOTiL2y~CB*trImCxV`%4zd|01W#DY<Wo3NW59ETwol%-# z>g`Z!$ycbf=P4vtME;cunKtjJy`NZcbCSM4y{w~>PZFgY%+)zDEgfD;p2*clT?g4o&DuVRIWn-YH1(*ruGJ-1t1q@}AqJE8j;yY6 z%;@5w(!Zt>8nA|3WbPcLs$2BY*0KG>hE)4Rj&#+AOA|UqbeSYB#NUhake7*j)`^pe zM(4efTOVTU5pm*S@z6f85N-aflze=aSd?lx-CJ_iK=C6hiIbb;_sp6>%z~7};rkuM zAMSdfmg}@C#Y+V5YcY z`NCLfnvj<0fhilI)T+8p}X<#Mp+xzU07oE5Zk(K(t%@un61JV~cDSA;| zy|{O@2|H&K%Kji$n|7=f4?U%6*GMeJ%0D$qa_W(y<1{gv^&!_P^c%#{tQGIKNS+5B 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 zF%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW z5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~ z12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT- zF%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW z5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~ z12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT- zF%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW z5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~ z12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT- zF%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW z5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~ z12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT- zF%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW z5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~ z12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT- zF%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW z5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~ z12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT- zF%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW z5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~ z12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT- zF%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW z5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~ z12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT- zF%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW5Cbs~12GT-F%SbW W5Cbs~12GT-F%SbW5Ci}J8u&lYp9tsx literal 0 HcmV?d00001 diff --git a/stardew-access/assets/sounds/npc_mono_bottom.wav b/stardew-access/assets/sounds/npc_mono_bottom.wav new file mode 100644 index 0000000000000000000000000000000000000000..44bd60eb4aa2d8f13bfa8387d634bfc3f52c7b6b GIT binary patch literal 4484 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 literal 0 HcmV?d00001 diff --git a/stardew-access/assets/sounds/npc_mono_left.wav b/stardew-access/assets/sounds/npc_mono_left.wav new file mode 100644 index 0000000000000000000000000000000000000000..2876644e81eb99be1706806ddd2ddf1ee79ecdd1 GIT binary patch literal 13364 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 OAvo(nBLf6z`2PS5GZUl$ literal 0 HcmV?d00001 diff --git a/stardew-access/assets/sounds/npc_mono_top.wav b/stardew-access/assets/sounds/npc_mono_top.wav new file mode 100644 index 0000000000000000000000000000000000000000..f6a119e9492651ea47754ce075ee5b297c5aab6e GIT binary patch literal 6704 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 zU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_ z12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j- zHedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-Hedrb zU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_ z12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j- zHedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-Hedrb zU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_ z12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j- zHedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-Hedrb zU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_ z12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j- zHedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-Hedrb zU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_ z12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j- zHedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-Hedrb zU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_ z12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j- zHedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-Hedrb zU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_ z12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j- zHedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-Hedrb zU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_ z12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j- zHedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-Hedrb zU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_ z12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j- zHedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-Hedrb zU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_12$j-HedrbU;{Q_ M12$j-|34V`KjAkrD*ylh literal 0 HcmV?d00001 diff --git a/stardew-access/assets/sounds/npc_top.wav b/stardew-access/assets/sounds/npc_top.wav new file mode 100644 index 0000000000000000000000000000000000000000..da11b9cb345d0623acb7e5e196ad4ad4bc587f19 GIT binary patch literal 320404 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 z|4*fU@&A86`SRt{Z~p%%^*YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r t3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>Yx*83?Buli2_O literal 0 HcmV?d00001 diff --git a/stardew-access/assets/sounds/obj_bottom.wav b/stardew-access/assets/sounds/obj_bottom.wav new file mode 100644 index 0000000000000000000000000000000000000000..b75c667a101057c5782d18c6bfc052a3b0a31898 GIT binary patch literal 318490 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 - /// Pauses the read tile for the provided time. + /// Pauses the feature for the provided time. /// /// The amount of time we want to pause the execution (in ms).
Default is 2500 (2.5s). - public void pause(int time = 2500) + public void pauseUntil(int time = 2500) { this.shouldPause = true; Task.Delay(time).ContinueWith(_ => { this.shouldPause = false; }); } + /// + /// Pauses the feature + /// + public void pause() + { + this.shouldPause = true; + } + + /// + /// Resumes the feature + /// + public void resume() + { + this.shouldPause = false; + } + public void run(bool manuallyTriggered = false, bool playersPosition = false) { try diff --git a/stardew-access/Features/TileViewer.cs b/stardew-access/Features/TileViewer.cs index 64c56e4..113d74c 100644 --- a/stardew-access/Features/TileViewer.cs +++ b/stardew-access/Features/TileViewer.cs @@ -148,6 +148,7 @@ namespace stardew_access.Features Game1.player.controller = controller; this.isAutoWalking = true; this.finalTile = this.GetViewingTile(); + MainClass.ReadTileFeature.pause(); MainClass.ScreenReader.Say($"Moving to {this.finalTile.X}x {this.finalTile.Y}y", true); } else @@ -165,6 +166,7 @@ namespace stardew_access.Features this.finalTile = Vector2.Zero; this.isAutoWalking = false; Game1.player.controller = null; + MainClass.ReadTileFeature.resume(); if (wasForced) MainClass.ScreenReader.Say("Stopped moving", true); } diff --git a/stardew-access/Features/Warnings.cs b/stardew-access/Features/Warnings.cs index c88d158..dd683d6 100644 --- a/stardew-access/Features/Warnings.cs +++ b/stardew-access/Features/Warnings.cs @@ -32,7 +32,7 @@ namespace stardew_access.Features { MainClass.ScreenReader.Say(toSpeak, true); // Pause the read tile feature to prevent interruption in warning message - MainClass.ReadTileFeature.pause(); + MainClass.ReadTileFeature.pauseUntil(); } prevHour = hours; @@ -50,7 +50,7 @@ namespace stardew_access.Features { MainClass.ScreenReader.Say(toSpeak, true); // Pause the read tile feature to prevent interruption in warning message - MainClass.ReadTileFeature.pause(); + MainClass.ReadTileFeature.pauseUntil(); } prevStamina = stamina; @@ -68,7 +68,7 @@ namespace stardew_access.Features { MainClass.ScreenReader.Say(toSpeak, true); // Pause the read tile feature to prevent interruption in warning message - MainClass.ReadTileFeature.pause(); + MainClass.ReadTileFeature.pauseUntil(); } prevHealth = health; From b1056a6cd1f7b0d38bc9b0c63a57ea5c50159666 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 16 May 2022 21:34:19 +0530 Subject: [PATCH 161/232] Added chat menu keys to mod config --- stardew-access/Features/TileViewer.cs | 2 +- stardew-access/ModConfig.cs | 50 +++++++++++++++-------- stardew-access/ModEntry.cs | 14 ++----- stardew-access/Patches/ChatMenuPatches.cs | 27 ++++-------- 4 files changed, 44 insertions(+), 49 deletions(-) diff --git a/stardew-access/Features/TileViewer.cs b/stardew-access/Features/TileViewer.cs index 113d74c..ad2d62b 100644 --- a/stardew-access/Features/TileViewer.cs +++ b/stardew-access/Features/TileViewer.cs @@ -133,7 +133,7 @@ namespace stardew_access.Features { this.cursorMoveInput(new Vector2(-Game1.tileSize, 0)); } - else if (MainClass.Config.AutoWalkToTile.JustPressed() && StardewModdingAPI.Context.IsPlayerFree) + else if (MainClass.Config.AutoWalkToTileKey.JustPressed() && StardewModdingAPI.Context.IsPlayerFree) { this.startAutoWalking(); } diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index 7b22ddc..ecea532 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -4,31 +4,28 @@ namespace stardew_access { internal class ModConfig { - public Boolean VerboseCoordinates { get; set; } = true; - public Boolean ReadTile { get; set; } = true; - public Boolean SnapMouse { get; set; } = true; - public Boolean Radar { get; set; } = false; - public Boolean RadarStereoSound { get; set; } = true; - public Boolean ReadFlooring { get; set; } = false; - - #region KeyBinds - // https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Input#SButton button key codes + + #region Simulate mouse clicks public KeybindList LeftClickMainKey { get; set; } = KeybindList.Parse("LeftControl + Enter"); public KeybindList RightClickMainKey { get; set; } = KeybindList.Parse("LeftShift + Enter"); public KeybindList LeftClickAlternateKey { get; set; } = KeybindList.Parse("OemOpenBrackets"); public KeybindList RightClickAlternateKey { get; set; } = KeybindList.Parse("OemCloseBrackets"); - public KeybindList HealthNStaminaKey { get; set; } = KeybindList.Parse("H"); - public KeybindList PositionKey { get; set; } = KeybindList.Parse("K"); - public KeybindList LocationKey { get; set; } = KeybindList.Parse("LeftAlt + K"); - public KeybindList MoneyKey { get; set; } = KeybindList.Parse("R"); - public KeybindList TimeNSeasonKey { get; set; } = KeybindList.Parse("Q"); + #endregion + + #region Chat menu + public KeybindList ChatMenuNextKey { get; set; } = KeybindList.Parse("PageUp"); + public KeybindList ChatMenuPreviousKey { get; set; } = KeybindList.Parse("PageDown"); + #endregion + + #region Read tile + public Boolean ReadTile { get; set; } = true; public KeybindList ReadTileKey { get; set; } = KeybindList.Parse("J"); public KeybindList ReadStandingTileKey { get; set; } = KeybindList.Parse("LeftAlt + J"); - public bool LimitTileCursorToScreen { get; set; } = false; - public int TileCursorPreciseMovementDistance { get; set; } = 8; + public Boolean ReadFlooring { get; set; } = false; + #endregion - //Tile viewer keys + #region Tile viewer public KeybindList TileCursorUpKey { get; set; } = KeybindList.Parse("Up"); public KeybindList TileCursorRightKey { get; set; } = KeybindList.Parse("Right"); public KeybindList TileCursorDownKey { get; set; } = KeybindList.Parse("Down"); @@ -38,7 +35,24 @@ namespace stardew_access public KeybindList TileCursorPreciseDownKey { get; set; } = KeybindList.Parse("LeftShift + Down"); public KeybindList TileCursorPreciseLeftKey { get; set; } = KeybindList.Parse("LeftShift + Left"); public KeybindList ToggleRelativeCursorLockKey { get; set; } = KeybindList.Parse("L"); - public KeybindList AutoWalkToTile { get; set; } = KeybindList.Parse("LeftControl + Enter"); + public KeybindList AutoWalkToTileKey { get; set; } = KeybindList.Parse("LeftControl + Enter"); + public bool LimitTileCursorToScreen { get; set; } = false; + public int TileCursorPreciseMovementDistance { get; set; } = 8; + #endregion + + #region Radar + public Boolean Radar { get; set; } = false; + public Boolean RadarStereoSound { get; set; } = true; + #endregion + + #region Others + public KeybindList HealthNStaminaKey { get; set; } = KeybindList.Parse("H"); + public KeybindList PositionKey { get; set; } = KeybindList.Parse("K"); + public KeybindList LocationKey { get; set; } = KeybindList.Parse("LeftAlt + K"); + public KeybindList MoneyKey { get; set; } = KeybindList.Parse("R"); + public KeybindList TimeNSeasonKey { get; set; } = KeybindList.Parse("Q"); + public Boolean VerboseCoordinates { get; set; } = true; + public Boolean SnapMouse { get; set; } = true; #endregion // TODO Add the exclusion and focus list too diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 8412b81..f564ed6 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -192,22 +192,18 @@ namespace stardew_access if (e == null) return; - bool isLeftAltPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftAlt); - #region Simulate left and right clicks if (Game1.activeClickableMenu != null) { - bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift); - bool isLeftControlPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl); bool isCustomizingChrachter = Game1.activeClickableMenu is CharacterCustomization || (TitleMenu.subMenu != null && TitleMenu.subMenu is CharacterCustomization); #region Mouse Click Simulation // Main Keybinds - if (isLeftControlPressed && Config.LeftClickMainKey.JustPressed()) + if (Config.LeftClickMainKey.JustPressed()) { Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); } - if (isLeftShiftPressed && Config.RightClickMainKey.JustPressed()) + if (Config.RightClickMainKey.JustPressed()) { Game1.activeClickableMenu.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true)); } @@ -226,17 +222,15 @@ namespace stardew_access if (Game1.currentMinigame != null) { - bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift); - bool isLeftControlPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl); bool isCustomizingChrachter = Game1.activeClickableMenu is CharacterCustomization || (TitleMenu.subMenu != null && TitleMenu.subMenu is CharacterCustomization); #region Mouse Click Simulation // Main Keybinds - if (isLeftControlPressed && Config.LeftClickMainKey.JustPressed()) + if (Config.LeftClickMainKey.JustPressed()) { Game1.currentMinigame.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); } - if (isLeftShiftPressed && Config.RightClickMainKey.JustPressed()) + if (Config.RightClickMainKey.JustPressed()) { Game1.currentMinigame.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true)); } diff --git a/stardew-access/Patches/ChatMenuPatches.cs b/stardew-access/Patches/ChatMenuPatches.cs index 6de3d2b..6155a70 100644 --- a/stardew-access/Patches/ChatMenuPatches.cs +++ b/stardew-access/Patches/ChatMenuPatches.cs @@ -16,19 +16,19 @@ namespace stardew_access.Patches if (__instance.chatBox.Selected) { - bool isPrevArrowPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.PageUp); - bool isNextArrowPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.PageDown); + bool isPrevButtonPressed = MainClass.Config.ChatMenuNextKey.JustPressed(); + bool isNextButtonPressed = MainClass.Config.ChatMenuPreviousKey.JustPressed(); if (___messages.Count > 0) { #region To narrate previous and next chat messages - if (isNextArrowPressed && !isChatRunning) + if (isNextButtonPressed && !isChatRunning) { isChatRunning = true; CycleThroughChatMessages(true, ___messages); Task.Delay(200).ContinueWith(_ => { isChatRunning = false; }); } - else if (isPrevArrowPressed && !isChatRunning) + else if (isPrevButtonPressed && !isChatRunning) { isChatRunning = true; CycleThroughChatMessages(false, ___messages); @@ -58,22 +58,9 @@ namespace stardew_access.Patches private static void CycleThroughChatMessages(bool increase, List ___messages) { string toSpeak = " "; - if (increase) - { - ++currentChatMessageIndex; - if (currentChatMessageIndex > ___messages.Count - 1) - { - currentChatMessageIndex = ___messages.Count - 1; - } - } - else - { - --currentChatMessageIndex; - if (currentChatMessageIndex < 0) - { - currentChatMessageIndex = 0; - } - } + + currentChatMessageIndex = (increase) ? (Math.Min(currentChatMessageIndex + 1, ___messages.Count - 1)) : (currentChatMessageIndex = Math.Max(currentChatMessageIndex - 1, 0)); + ___messages[currentChatMessageIndex].message.ForEach(message => { toSpeak += $"{message.message}, "; From 923ad61580a41728e7df226b079f82b21e1d1009 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 16 May 2022 21:35:01 +0530 Subject: [PATCH 162/232] Changed b key to c in buildingNAnimal menu --- stardew-access/Patches/BuildingNAnimalMenuPatches.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs index e314dc9..1d71a5d 100644 --- a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs +++ b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs @@ -125,7 +125,7 @@ namespace stardew_access.Patches return; int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isBPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.B); + bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); string ingredients = ""; string name = currentBluprint.displayName; string upgradeName = currentBluprint.nameOfBuildingToUpgrade; @@ -162,7 +162,7 @@ namespace stardew_access.Patches blueprintInfo = $"{name}, Price: {price}, Ingredients: {ingredients}, Dimensions: {width} width and {height} height, Description: {description}"; - if (isBPressed && !isSayingBlueprintInfo) + if (isCPressed && !isSayingBlueprintInfo) { SayBlueprintInfo(blueprintInfo); } From 9c2931ec2fb9eca5bf223a522605d1550eb54c52 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 16 May 2022 22:01:02 +0530 Subject: [PATCH 163/232] Added all keybinds to mod config --- stardew-access/ModConfig.cs | 24 ++++++++++++++++++- .../Patches/BuildingNAnimalMenuPatches.cs | 2 +- stardew-access/Patches/BundleMenuPatches.cs | 10 ++++---- stardew-access/Patches/DonationMenuPatches.cs | 2 +- stardew-access/Patches/GameMenuPatches.cs | 21 ++++++---------- stardew-access/Patches/MenuPatches.cs | 4 ++-- stardew-access/Patches/QuestPatches.cs | 2 +- stardew-access/Patches/TitleMenuPatches.cs | 12 ++-------- 8 files changed, 42 insertions(+), 35 deletions(-) diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index ecea532..aac7609 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -45,6 +45,28 @@ namespace stardew_access public Boolean RadarStereoSound { get; set; } = true; #endregion + #region Menu Keys + public KeybindList PrimaryInfoKey { get; set; } = KeybindList.Parse("C"); + + // Charachter Creatinon menu (new game menu) keys + public KeybindList CharacterCreationMenuNextKey { get; set; } = KeybindList.Parse("Right"); + public KeybindList CharacterCreationMenuPreviousKey { get; set; } = KeybindList.Parse("Left"); + + // Bundle menu keys + public KeybindList BundleMenuIngredientsKey { get; set; } = KeybindList.Parse("I"); + public KeybindList BundleMenuInventoryItemsKey { get; set; } = KeybindList.Parse("C"); + public KeybindList BundleMenuPurchaseButtonKey { get; set; } = KeybindList.Parse("P"); + public KeybindList BundleMenuIngredientsInputSlotKey { get; set; } = KeybindList.Parse("V"); + public KeybindList BundleMenuBackButtonKey { get; set; } = KeybindList.Parse("Back"); + + // Menus with secondary inventory(shop inventory or chest inventory or crafting recipe list) + public KeybindList SnapToFirstInventorySlotKey { get; set; } = KeybindList.Parse("I"); + public KeybindList SnapToFirstSecondaryInventorySlotKey { get; set; } = KeybindList.Parse("LeftShift + I"); + + // Crafting menu + public KeybindList CraftingMenuCycleThroughRecipiesKey { get; set; } = KeybindList.Parse("C"); + #endregion + #region Others public KeybindList HealthNStaminaKey { get; set; } = KeybindList.Parse("H"); public KeybindList PositionKey { get; set; } = KeybindList.Parse("K"); @@ -58,4 +80,4 @@ namespace stardew_access // TODO Add the exclusion and focus list too // public String ExclusionList { get; set; } = "test"; } -} +} \ No newline at end of file diff --git a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs index 1d71a5d..1ac91d5 100644 --- a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs +++ b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs @@ -125,7 +125,7 @@ namespace stardew_access.Patches return; int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); + bool isCPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); string ingredients = ""; string name = currentBluprint.displayName; string upgradeName = currentBluprint.nameOfBuildingToUpgrade; diff --git a/stardew-access/Patches/BundleMenuPatches.cs b/stardew-access/Patches/BundleMenuPatches.cs index a1bf47c..2f8aae4 100644 --- a/stardew-access/Patches/BundleMenuPatches.cs +++ b/stardew-access/Patches/BundleMenuPatches.cs @@ -151,11 +151,11 @@ namespace stardew_access.Patches } else { - bool isIPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.I); // For the ingredients - bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); // For the items in inventory - bool isPPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.P); // For the Purchase Button - bool isVPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.V); // For the ingredient input slots - bool isBackPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Back); // For the back button + bool isIPressed = MainClass.Config.BundleMenuIngredientsInputSlotKey.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) diff --git a/stardew-access/Patches/DonationMenuPatches.cs b/stardew-access/Patches/DonationMenuPatches.cs index ce244ac..8f4747b 100644 --- a/stardew-access/Patches/DonationMenuPatches.cs +++ b/stardew-access/Patches/DonationMenuPatches.cs @@ -82,7 +82,7 @@ namespace stardew_access.Patches int i = narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y); if (i != -9999) { - bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); // For donating hovered item + bool isCPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); // For donating hovered item if (isCPressed && __instance.inventory.actualInventory[i] != null) { diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index d3d468a..228dded 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -191,15 +191,13 @@ namespace stardew_access.Patches try { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isIPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.I); - bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift); - if (isLeftShiftPressed && isIPressed && __instance.inventory.inventory.Count > 0) + if (MainClass.Config.SnapToFirstInventorySlotKey.JustPressed() && __instance.inventory.inventory.Count > 0) { __instance.inventory.inventory[0].snapMouseCursorToCenter(); __instance.setCurrentlySnappedComponentTo(__instance.inventory.inventory[0].myID); } - else if (!isLeftShiftPressed && isIPressed && __instance.forSaleButtons.Count > 0) + else if (MainClass.Config.SnapToFirstSecondaryInventorySlotKey.JustPressed() && __instance.forSaleButtons.Count > 0) { __instance.forSaleButtons[0].snapMouseCursorToCenter(); __instance.setCurrentlySnappedComponentTo(__instance.forSaleButtons[0].myID); @@ -413,15 +411,13 @@ namespace stardew_access.Patches try { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isIPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.I); - bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift); - if (isLeftShiftPressed && isIPressed && __instance.inventory.inventory.Count > 0) + if (MainClass.Config.SnapToFirstInventorySlotKey.JustPressed() && __instance.inventory.inventory.Count > 0) { __instance.setCurrentlySnappedComponentTo(__instance.inventory.inventory[0].myID); __instance.inventory.inventory[0].snapMouseCursorToCenter(); } - else if (!isLeftShiftPressed && isIPressed && __instance.ItemsToGrabMenu.inventory.Count > 0 && !__instance.shippingBin) + 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(); @@ -677,25 +673,22 @@ namespace stardew_access.Patches try { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isIPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.I); - bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); - bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift); - if (isLeftShiftPressed && isIPressed && __instance.inventory.inventory.Count > 0) + 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 (!isLeftShiftPressed && isIPressed && __instance.pagesOfCraftingRecipes[___currentCraftingPage].Count > 0) + 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 (isCPressed && !isSelectingRecipe) + else if (MainClass.Config.CraftingMenuCycleThroughRecipiesKey.JustPressed() && !isSelectingRecipe) { isSelectingRecipe = true; CycleThroughRecipies(__instance.pagesOfCraftingRecipes, ___currentCraftingPage, __instance); diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index e1abd64..8dc4c92 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -171,7 +171,7 @@ namespace stardew_access.Patches try { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); + bool isCPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); string toSpeak = " ", extra = ""; if (___confirmingEmpty) @@ -355,7 +355,7 @@ namespace stardew_access.Patches try { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); // For narrating animal details + bool isCPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); // For narrating animal details bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box string toSpeak = " ", details = " "; diff --git a/stardew-access/Patches/QuestPatches.cs b/stardew-access/Patches/QuestPatches.cs index 73e6784..f5f9835 100644 --- a/stardew-access/Patches/QuestPatches.cs +++ b/stardew-access/Patches/QuestPatches.cs @@ -154,7 +154,7 @@ namespace stardew_access.Patches { try { - bool isCPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.C); + bool isCPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position string toSpeak = " ", extra = ""; diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index f8f2ec2..8dcb17f 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -175,21 +175,13 @@ namespace stardew_access.Patches { try { - bool isNextArrowPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Right); - bool isPrevArrowPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Left); - - if (__instance.backButton.containsPoint != null && __instance.backButton.visible && __instance.backButton.containsPoint((int)Game1.getMouseX(true), (int)Game1.getMouseY(true))) - { - - } - - if (isNextArrowPressed && !isRunning) + if (MainClass.Config.CharacterCreationMenuNextKey.JustPressed() && !isRunning) { isRunning = true; CycleThroughItems(true, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel); Task.Delay(200).ContinueWith(_ => { isRunning = false; }); } - else if (isPrevArrowPressed && !isRunning) + else if (MainClass.Config.CharacterCreationMenuPreviousKey.JustPressed() && !isRunning) { isRunning = true; CycleThroughItems(false, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel); From 9f8d4273bc21b10bf2825f748ee1cf4382c23f20 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Wed, 18 May 2022 19:16:28 -0400 Subject: [PATCH 164/232] Fixed static tiles json formatting; fixed a few entrances with missing coordinates. --- stardew-access/assets/static-tiles.json | 4924 +++++++++++++---------- 1 file changed, 2702 insertions(+), 2222 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index c0b3908..dc0d299 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -1,2225 +1,2705 @@ { - "farm": { - "Bus Stop Entrance": { - "x": [ 79 ], - "y": [ 15, 16, 17, 18 ], - "type": "door" - }, - "Backwoods Entrance": { - "x": [ 40, 41 ], - "y": [ 0 ], - "type": "door" - }, - "Cindersap Forest Entrance": { - "x": [ 40, 41 ], - "y": [ 64 ], - "type": "door" - }, - "Farm Cave Entrance": { - "x": [ 34 ], - "y": [ 7 ], - "type": "door" - }, - "Grandpa's Shrine": { - "x": [ 8 ], - "y": [ 7 ], - "type": "interactable" - }, - "Water bowl": { - "x": [ 54 ], - "y": [ 7 ], - "type": "interactable" + "farm": + { + "Bus Stop Entrance": + { + "x":[79], + "y":[15,16,17,18], + "type":"door" + }, + "Backwoods Entrance": + { + "x":[40,41], + "y":[0], + "type":"door" + }, + "Cindersap Forest Entrance": + { + "x":[40,41], + "y":[64], + "type":"door" + }, + "Farm Cave Entrance": + { + "x":[34], + "y":[7], + "type":"door" + }, + "Grandpa's Shrine": + { + "x":[8], + "y":[7], + "type":"interactable" + } + }, + "farmcave": + { + "Exit": + { + "x":[8], + "y":[11], + "type":"door" + } + }, + "busstop": + { + "Ticket Machine": + { + "x":[7], + "y":[11], + "type":"interactable" + }, + "Minecart": + { + "x":[4,5], + "y":[3], + "type":"interactable" + }, + "Farm Entrance": + { + "x":[0], + "y":[23], + "type":"door" + }, + "Town Entrance": + { + "x":[34], + "y":[23], + "type":"door" + }, + "Backwoods Entrance": + { + "x":[0], + "y":[6,7,8,9], + "type":"door" + } + }, + "town": + { + "Calender Board": + { + "x":[41], + "y":[56], + "type":"interactable" + }, + "Daily Quest Board": + { + "x":[42], + "y":[56], + "type":"interactable" + }, + "Sewer": + { + "x":[34,35], + "y":[95,96], + "type":"interactable" + }, + "Ice Cream Stand": + { + "x":[88], + "y":[92], + "type":"interactable" + }, + "Minecart": + { + "x":[105,106], + "y":[79], + "type":"interactable" + }, + "Bus Stop Entrance": + { + "x":[0], + "y":[54], + "type":"door" + }, + "Cindersap Forest Entrance": + { + "x":[0], + "y":[90], + "type":"door" + }, + "Beach Entrance": + { + "x":[54], + "y":[109], + "type":"door" + }, + "Mountain Entrance": + { + "x":[81], + "y":[0], + "type":"door" + } + }, + "shed": + { + "Exit": + { + "x":[6], + "y":[13], + "type":"door" + } + }, + "coop": + { + "Hay Hopper": + { + "x":[3], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[7], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Exit": + { + "x":[2], + "y":[9], + "type":"door" + } + }, + "big coop": + { + "Hay Hopper": + { + "x":[3], + "y":[3], + "type":"interactable" + }, + "Incubator": + { + "x":[2], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[7], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[13], + "y":[3], + "type":"interactable" + }, + "Exit": + { + "x":[2], + "y":[9], + "type":"door" + } + }, + "coop2": + { + "Hay Hopper": + { + "x":[3], + "y":[3], + "type":"interactable" + }, + "Incubator": + { + "x":[2], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[7], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[13], + "y":[3], + "type":"interactable" + }, + "Exit": + { + "x":[2], + "y":[9], + "type":"door" + } + }, + "deluxe coop": + { + "Hay Hopper": + { + "x":[3], + "y":[3], + "type":"interactable" + }, + "Incubator": + { + "x":[2], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[7], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[13], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 9": + { + "x":[14], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 10": + { + "x":[15], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 11": + { + "x":[16], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 12": + { + "x":[17], + "y":[3], + "type":"interactable" + }, + "Exit": + { + "x":[2], + "y":[9], + "type":"door" + } + }, + "coop3": + { + "Hay Hopper": + { + "x":[3], + "y":[3], + "type":"interactable" + }, + "Incubator": + { + "x":[2], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[7], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[13], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 9": + { + "x":[14], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 10": + { + "x":[15], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 11": + { + "x":[16], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 12": + { + "x":[17], + "y":[3], + "type":"interactable" + }, + "Exit": + { + "x":[2], + "y":[9], + "type":"door" + } + }, + "barn": + { + "Hay Hopper": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Exit": + { + "x":[11], + "y":[14], + "type":"door" + } + }, + "barn2": + { + "Hay Hopper": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[13], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[14], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[15], + "y":[3], + "type":"interactable" + }, + "Exit": + { + "x":[11], + "y":[14], + "type":"door" + } + }, + "big barn": + { + "Hay Hopper": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[13], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[14], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[15], + "y":[3], + "type":"interactable" + }, + "Exit": + { + "x":[11], + "y":[14], + "type":"door" + } + }, + "barn3": + { + "Hay Hopper": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[13], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[14], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[15], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 9": + { + "x":[16], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 10": + { + "x":[17], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 11": + { + "x":[18], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 12": + { + "x":[19], + "y":[3], + "type":"interactable" + }, + "Exit": + { + "x":[11], + "y":[14], + "type":"door" + } + }, + "deluxe barn": + { + "Hay Hopper": + { + "x":[6], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 1": + { + "x":[8], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 2": + { + "x":[9], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 3": + { + "x":[10], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 4": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 5": + { + "x":[12], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 6": + { + "x":[13], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 7": + { + "x":[14], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 8": + { + "x":[15], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 9": + { + "x":[16], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 10": + { + "x":[17], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 11": + { + "x":[18], + "y":[3], + "type":"interactable" + }, + "Feeding Bench 12": + { + "x":[19], + "y":[3], + "type":"interactable" + }, + "Exit": + { + "x":[11], + "y":[14], + "type":"door" + } + }, + "slime hutch": + { + "Water Trough 1": + { + "x":[16], + "y":[6], + "type":"interactable" + }, + "Water Trough 2": + { + "x":[16], + "y":[7], + "type":"interactable" + }, + "Water Trough 3": + { + "x":[16], + "y":[8], + "type":"interactable" + }, + "Water Trough 4": + { + "x":[16], + "y":[9], + "type":"interactable" + }, + "Exit": + { + "x":[8], + "y":[12], + "type":"door" + } + }, + "adventureguild": + { + "Goals Board": + { + "x":[8], + "y":[10], + "type":"interactable" + }, + "Shop Counter": + { + "x":[5], + "y":[12], + "type":"interactable" + }, + "Gil": + { + "x":[11], + "y":[12], + "type":"npc" + }, + "Exit": + { + "x":[6], + "y":[19], + "type":"door" + } + }, + "caldera": + { + "Rare Chest": + { + "x":[25], + "y":[28], + "type":"chest" + }, + "Forge": + { + "x":[23], + "y":[21], + "type":"interactable" + }, + "Volcano Dungeon 0 Entrance": + { + "x":[11], + "y":[36], + "type":"door" + }, + "Volcano Dungeon 9 Entrance": + { + "x":[21], + "y":[39], + "type":"door" + } + }, + "volcanodungeon0": + { + "Island North Entrance 1": + { + "x":[31], + "y":[54], + "type":"door" + }, + "Island North Entrance 2": + { + "x":[6], + "y":[50], + "type":"door" + }, + "Caldera Entrance": + { + "x":[44], + "y":[50], + "type":"door" + }, + "Volcano Dungeon 1 Entrance": + { + "x":[37], + "y":[5], + "type":"door" + } + }, + "club": + { + "Coin Machine": + { + "x":[12], + "y":[4], + "type":"interactable" + }, + "Shop Counter": + { + "x":[25], + "y":[3], + "type":"interactable" + }, + "Calico Spin Machine": + { + "x":[11,13,15], + "y":[8,10], + "type":"interactable" + }, + "High Stakes Calico Jack Table": + { + "x":[23,24], + "y":[10,11], + "type":"interactable" + }, + "Low Stakes Calico Jack Table": + { + "x":[3], + "y":[7,9], + "type":"interactable" + }, + "Man": + { + "x":[13], + "y":[11], + "type":"npc" + }, + "Welwick": + { + "x":[18], + "y":[9], + "type":"npc" + }, + "Unknown person": + { + "x":[16], + "y":[4], + "type":"npc" + }, + "Stats Checker": + { + "x":[3], + "y":[4], + "type":"interactable" + }, + "Exit": + { + "x":[8], + "y":[12], + "type":"door" + } + }, + "desert": + { + "Bus": + { + "x":[18], + "y":[27], + "type":"interactable" + }, + "Desert Trader": + { + "x":[42], + "y":[23], + "type":"interactable" + }, + "Three Pillars": + { + "x":[34,37,40], + "y":[8,13], + "type":"decoration" + }, + "Three Pillars Center": + { + "x":[37], + "y":[11], + "type":"interactable" + }, + "Skull Cavern Entrance": + { + "x":[8], + "y":[6], + "type":"door" + }, + "Desert Warp Statue": + { + "x":[35], + "y":[43], + "type":"decoration" + }, + "Sand Dragon Skull": + { + "x":[9,10], + "y":[35,36], + "type":"decoration" + } + }, + "fishshop": + { + "Shop Counter": + { + "x":[5], + "y":[5], + "type":"interactable" + }, + "Exit": + { + "x":[5], + "y":[9], + "type":"door" + } + }, + "boattunnel": + { + "Exit": + { + "x":[6], + "y":[11], + "type":"door" + } + }, + "beach": + { + "Town Entrance": + { + "x":[38], + "y":[0], + "type":"door" + }, + "Beach Warp Statue": + { + "x":[20], + "y":[4], + "type":"decoration" + } + }, + "forest": + { + "Farm Entrance": + { + "x":[68], + "y":[0], + "type":"door" + }, + "Town Entrance": + { + "x":[119], + "y":[25], + "type":"door" + }, + "Bridge 1": + { + "x":[77,82], + "y":[49], + "type":"bridge" + }, + "Bridge 2": + { + "x":[87], + "y":[52,56], + "type":"bridge" + }, + "Bridge 3": + { + "x":[65,62], + "y":[70], + "type":"bridge" + }, + "Bridge 4": + { + "x":[41], + "y":[79,82], + "type":"bridge" + }, + "Bridge 5": + { + "x":[38], + "y":[85,87], + "type":"bridge" + }, + "Abandoned House": + { + "x":[34], + "y":[95], + "type":"interactable" + } + }, + "beachnightmarket": + { + "Desert Trader": + { + "x":[14], + "y":[37], + "type":"npc" + }, + "Famous Painter Lupini": + { + "x":[43], + "y":[34], + "type":"npc" + }, + "Fishing Submarine Door": + { + "x":[5], + "y":[34], + "type":"door" + }, + "Travelling Cart": + { + "x":[39], + "y":[30], + "type":"interactable" + }, + "Shrouded Figure": + { + "x":[32], + "y":[34], + "type":"npc" + }, + "Decoration Boat": + { + "x":[19], + "y":[33], + "type":"interactable" + }, + "Magic Shop Boat": + { + "x":[48], + "y":[34], + "type":"interactable" + }, + "Mermaid Boat Door": + { + "x":[58], + "y":[31], + "type":"door" + } + }, + "mermaidhouse": + { + "Exit": + { + "x":[4], + "y":[10], + "type":"door" + }, + "Clam Shell 1": + { + "x":[2], + "y":[6], + "type":"interactable" + }, + "Clam Shell 2": + { + "x":[3], + "y":[6], + "type":"interactable" + }, + "Clam Shell 3": + { + "x":[4], + "y":[6], + "type":"interactable" + }, + "Clam Shell 4": + { + "x":[5], + "y":[6], + "type":"interactable" + }, + "Clam Shell 5": + { + "x":[6], + "y":[6], + "type":"interactable" + } + }, + "submarine": + { + "Exit": + { + "x":[14], + "y":[15], + "type":"door" + }, + "Captain": + { + "x":[2], + "y":[9], + "type":"npc" + } + }, + "cellar": + { + "Exit": + { + "x":[3], + "y":[2], + "type":"door" + } + }, + "communitycenter": + { + "Exit": + { + "x":[32], + "y":[23], + "type":"door" + } + }, + "islandeast": + { + "Banana Shrine": + { + "x":[16], + "y":[26], + "type":"interactable" + }, + "Jungle Parrot Express": + { + "x":[28], + "y":[27], + "type":"interactable" + }, + "Island Hut Entrance": + { + "x":[22], + "y":[10], + "type":"door" + }, + "Island South Entrance": + { + "x":[0], + "y":[46], + "type":"door" + }, + "Island Shrine Entrance": + { + "x":[32], + "y":[30], + "type":"door" + } + }, + "islandhut": + { + "Exit": + { + "x":[7], + "y":[13], + "type":"door" + } + }, + "islandsouth": + { + "Island East Entrance": + { + "x":[34], + "y":[12], + "type":"door" + }, + "Ginger Island Warp Statue": + { + "x":[11], + "y":[11], + "type":"decoration" + }, + "Island West Entrance": + { + "x":[0], + "y":[11], + "type":"door" + }, + "Island North Entrance": + { + "x":[17,18,19], + "y":[0], + "type":"door" + }, + "Island North Entrance 2": + { + "x":[27,28], + "y":[0], + "type":"door" + }, + "Docks Parrot Express": + { + "x":[6], + "y":[31], + "type":"interactable" + }, + "Return Boat": + { + "x":[19], + "y":[43], + "type":"interactable" + } + }, + "islandwest": + { + "Farm Parrot Express": + { + "x":[74], + "y":[9], + "type":"interactable" + }, + "Bridge 1": + { + "x":[67,62], + "y":[16], + "type":"bridge" + }, + "Qi's Walnut Room Door": + { + "x":[20], + "y":[22], + "type":"door" + }, + "Door to Shipwreck Interior": + { + "x":[60], + "y":[92], + "type":"door" + }, + "Hole 1": + { + "x":[37], + "y":[87], + "type":"interactable" + }, + "Hole 2": + { + "x":[41], + "y":[86], + "type":"interactable" + }, + "Hole 3": + { + "x":[45], + "y":[86], + "type":"interactable" + }, + "Hole 4": + { + "x":[48], + "y":[87], + "type":"interactable" + }, + "Bridge 2": + { + "x":[55,52], + "y":[80], + "type":"bridge" + }, + "Island Farm House Door": + { + "x":[77], + "y":[39], + "type":"door" + }, + "Island Farm Cave Entrance": + { + "x":[96], + "y":[33], + "type":"door" + }, + "Island South Entrance": + { + "x":[105], + "y":[40], + "type":"door" + } + }, + "captainroom": + { + "Exit": + { + "x":[0], + "y":[5], + "type":"door" + } + }, + "islandfarmcave": + { + "Exit": + { + "x":[4], + "y":[10], + "type":"door" + }, + "Gourmand Frog": + { + "x":[5], + "y":[4], + "type":"npc" + } + }, + "islandnorth": + { + "Island South Entrance": + { + "x":[35,36,37], + "y":[89], + "type":"door" + }, + "Island South Entrance 2": + { + "x":[43,44], + "y":[89], + "type":"door" + }, + "Island Field Office Entrance": + { + "x":[46], + "y":[46], + "type":"door" + }, + "Island North Cave Entrance": + { + "x":[21,22], + "y":[47], + "type":"door" + }, + "Dig Site Parrot Express": + { + "x":[5], + "y":[48], + "type":"interactable" + }, + "Volcano Dungeon Entrance": + { + "x":[39,40,41,42], + "y":[21], + "type":"door" + }, + "Volcano Dungeon Entrance 2": + { + "x":[12], + "y":[31], + "type":"door" + }, + "Volcano Parrot Express": + { + "x":[60], + "y":[16], + "type":"interactable" + } + }, + "islandfieldoffice": + { + "Exit": + { + "x":[4], + "y":[10], + "type":"door" + }, + "Counter": + { + "x":[8], + "y":[7], + "type":"interactable" + }, + "Island Survey": + { + "x":[5], + "y":[3], + "type":"interactable" + } + }, + "qinutroom": + { + "Exit": + { + "x":[7], + "y":[7], + "type":"door" + }, + "Perfection Tracker": + { + "x":[13], + "y":[4], + "type":"interactable" + }, + "Vending Machine": + { + "x":[11], + "y":[3], + "type":"interactable" + }, + "Special Order Board": + { + "x":[3], + "y":[3], + "type":"interactable" + } + }, + "islandsoutheast": + { + "Island South East Cave Entrance": + { + "x":[30], + "y":[18], + "type":"door" + } + }, + "islandsoutheastcave": + { + "Exit": + { + "x":[1], + "y":[8], + "type":"door" + } + }, + "islandshrine": + { + "Exit": + { + "x":[13], + "y":[28], + "type":"door" + }, + "Shrine": + { + "x":[24], + "y":[22], + "type":"interactable" + }, + "North Pedestal": + { + "x":[24], + "y":[25], + "type":"interactable" + }, + "East Pedestal": + { + "x":[27], + "y":[27], + "type":"interactable" + }, + "West Pedestal": + { + "x":[21], + "y":[27], + "type":"interactable" + }, + "South Pedestal": + { + "x":[24], + "y":[28], + "type":"interactable" + } + }, + "jojamart": + { + "Exit": + { + "x":[13], + "y":[29], + "type":"door" + }, + "Morris's Kiosk": + { + "x":[21], + "y":[25], + "type":"interactable" + }, + "Shop Counter": + { + "x":[10], + "y":[25], + "type":"interactable" + } + }, + "archaeologyhouse": + { + "Exit": + { + "x":[3], + "y":[14], + "type":"door" + }, + "Counter": + { + "x":[3], + "y":[9], + "type":"interactable" + } + }, + "manorhouse": + { + "Exit": + { + "x":[4], + "y":[11], + "type":"door" + }, + "Town Ledger Book": + { + "x":[2], + "y":[5], + "type":"interactable" + }, + "Marriage Log Book": + { + "x":[3], + "y":[5], + "type":"interactable" + }, + "Lost and Found Box": + { + "x":[4], + "y":[5], + "type":"interactable" + }, + "Mayor's Room Door": + { + "x":[16], + "y":[9], + "type":"door" + }, + "Mayor's Oven": + { + "x":[7], + "y":[4], + "type":"decoration" + }, + "Mayor's Fridge": + { + "x":[9], + "y":[4], + "type":"decoration" + } + }, + "mine": + { + "Mountain Exit": + { + "x":[18], + "y":[13], + "type":"door" + }, + "Minecart": + { + "x":[11,12], + "y":[10], + "type":"interactable" + }, + "Quarry Mine Ladder": + { + "x":[67], + "y":[9], + "type":"door" + }, + "Quarry Exit": + { + "x":[18], + "y":[13], + "type":"door" + } + }, + "mountain": + { + "Mine Entrance": + { + "x":[54], + "y":[5], + "type":"door" + }, + "Mine Bridge": + { + "x":[47], + "y":[7], + "type":"bridge" + }, + "Quarry Bridge": + { + "x":[90], + "y":[26], + "type":"bridge" + }, + "Minecart": + { + "x":[124,125], + "y":[11], + "type":"interactable" + }, + "Quarry Mine Entrance": + { + "x":[103], + "y":[17], + "type":"door" + }, + "Bridge 1": + { + "x":[57], + "y":[30], + "type":"bridge" + }, + "Bridge 2": + { + "x":[61], + "y":[21], + "type":"bridge" + }, + "Mountain Warp Statue": + { + "x":[31], + "y":[20], + "type":"decoration" + }, + "Linus Tent Entrance": + { + "x":[29], + "y":[7], + "type":"door" + }, + "Backwoods Entrance": + { + "x":[0], + "y":[13], + "type":"door" + }, + "Town Entrance": + { + "x":[15], + "y":[40], + "type":"door" + }, + "Railroad Entrance": + { + "x":[9], + "y":[0], + "type":"door" + }, + "Science House Secondary Door": + { + "x":[8], + "y":[20], + "type":"door" + } + }, + "undergroundmine77377": + { + "Grim Reaper Statue": + { + "x":[29,30], + "y":[6], + "type":"interactable" + } + }, + "Tent": + { + "Exit": + { + "x":[2], + "y":[5], + "type":"door" + } + }, + "railroad": + { + "Mountain Entrance": + { + "x":[29], + "y":[61], + "type":"door" + } + }, + "backwoods": + { + "Mountain Entrance": + { + "x":[49], + "y":[14], + "type":"door" + }, + "Farm Entrance": + { + "x":[14], + "y":[39], + "type":"door" + }, + "Bus stop Entrance": + { + "x":[49], + "y":[28,29,30,31,32], + "type":"door" + }, + "Tunnel Entrance": + { + "x":[23], + "y":[29,30,31], + "type":"door" + } + }, + "tunnel": + { + "Exit": + { + "x":[39], + "y":[7,8,9,10,11], + "type":"door" + } + }, + "movietheater": + { + "Exit": + { + "x":[13], + "y":[15], + "type":"door" + }, + "Concessions Counter": + { + "x":[7], + "y":[6], + "type":"interactable" + }, + "Crane Game": + { + "x":[1,2], + "y":[8], + "type":"interactable" + }, + "Theater Door": + { + "x":[13], + "y":[3], + "type":"door" + }, + "Crane Man": + { + "x":[2], + "y":[9], + "type":"npc" + } + }, + "seedshop": + { + "Exit": + { + "x":[6], + "y":[29], + "type":"door" + }, + "Shop Counter": + { + "x":[4], + "y":[18], + "type":"interactable" + }, + "Backpack Upgrade": + { + "x":[7], + "y":[18], + "type":"interactable" + }, + "Shrine of Yoba": + { + "x":[37], + "y":[17], + "type":"decoration" + }, + "Fridge": + { + "x":[39], + "y":[4], + "type":"decoration" + }, + "Abigail's Room Door": + { + "x":[13], + "y":[11], + "type":"door" + }, + "Pierre and Caroline's Room Door": + { + "x":[20], + "y":[11], + "type":"door" + }, + "Living Area Door": + { + "x":[14], + "y":[16], + "type":"door" + } + }, + "sewer": + { + "Exit Ladder": + { + "x":[16], + "y":[10], + "type":"door" + }, + "Statue Of Uncertainty": + { + "x":[8], + "y":[20], + "type":"interactable" + }, + "Mutant Bug Lair": + { + "x":[3], + "y":[19], + "type":"door" + } + }, + "wizardhouse": + { + "Exit": + { + "x":[8], + "y":[24], + "type":"door" + }, + "Basement Door": + { + "x":[4], + "y":[4], + "type":"door" + } + }, + "wizardhousebasement": + { + "Exit Ladder": + { + "x":[4], + "y":[3], + "type":"door" + }, + "Shrine of Illusions": + { + "x":[12], + "y":[4], + "type":"interactable" + } + }, + "woods": + { + "Forest Entrance": + { + "x":[59], + "y":[17], + "type":"door" + }, + "Old Master Cannoli": + { + "x":[8,9], + "y":[7], + "type":"interactable" + } + }, + "harveyroom": + { + "Exit": + { + "x":[6], + "y":[12], + "type":"door" + }, + "Fridge": + { + "x":[21], + "y":[6], + "type":"decoration" + }, + "Oven": + { + "x":[19], + "y":[6], + "type":"decoration" + }, + "Airplane Collection": + { + "x":[6,7], + "y":[3], + "type":"decoration" + }, + "Radio Broadcasting Set": + { + "x":[4,5], + "y":[4], + "type":"decoration" + }, + "Cassette Deck": + { + "x":[8], + "y":[4], + "type":"decoration" + } + }, + "hospital": + { + "Exit": + { + "x":[10], + "y":[19], + "type":"door" + }, + "Harvey's Room Entrance": + { + "x":[10], + "y":[2], + "type":"door" + }, + "Harvey's Room Entrance Door": + { + "x":[10], + "y":[5], + "type":"door" + }, + "Main Area Door": + { + "x":[10], + "y":[13], + "type":"door" + }, + "Counter": + { + "x":[6], + "y":[16], + "type":"interactable" + } + }, + "blacksmith": + { + "Exit": + { + "x":[5], + "y":[19], + "type":"door" + }, + "Counter": + { + "x":[3], + "y":[14], + "type":"interactable" + }, + "Clint's Room Door": + { + "x":[4], + "y":[9], + "type":"door" + }, + "Clint's Furnace": + { + "x":[9,10], + "y":[12], + "type":"decoration" + }, + "Blueprints": + { + "x":[13], + "y":[15,16], + "type":"decoration" + }, + "Anvil": + { + "x":[12,13], + "y":[13], + "type":"decoration" + }, + "Cassette Deck": + { + "x":[2], + "y":[4], + "type":"decoration" + }, + "Clint's Drawer": + { + "x":[5,6], + "y":[4], + "type":"decoration" + } + }, + "animalshop": + { + "Exit": + { + "x":[13], + "y":[19], + "type":"door" + }, + "Counter": + { + "x":[12], + "y":[15], + "type":"interactable" + }, + "Marnie's Room Door": + { + "x":[15], + "y":[12], + "type":"door" + }, + "Jas's Room Door": + { + "x":[6], + "y":[13], + "type":"door" + }, + "Shane's Room Door": + { + "x":[21], + "y":[13], + "type":"door" + }, + "Marnie's Barn Door": + { + "x":[30], + "y":[13], + "type":"door" + }, + "Fridge": + { + "x":[28], + "y":[14], + "type":"decoration" + }, + "Oven": + { + "x":[24], + "y":[14], + "type":"decoration" + }, + "Mega Station": + { + "x":[22], + "y":[5], + "type":"decoration" + }, + "Shane's Radio": + { + "x":[24], + "y":[4], + "type":"decoration" + }, + "Marnie's Dresser": + { + "x":[16], + "y":[4], + "type":"decoration" + }, + "Marnie's Drawer": + { + "x":[17], + "y":[4], + "type":"decoration" + }, + "Jack in the Box": + { + "x":[8], + "y":[5], + "type":"decoration" + }, + "Futan Bear": + { + "x":[2,3], + "y":[4], + "type":"decoration" + }, + "Colouring Book": + { + "x":[5], + "y":[4], + "type":"decoration" + }, + "Paint Set": + { + "x":[5], + "y":[7], + "type":"decoration" + }, + "Jas's Alarm Clock": + { + "x":[8], + "y":[8], + "type":"decoration" + }, + "Jas's Radio": + { + "x":[4], + "y":[9], + "type":"decoration" + }, + "Arts And Craft": + { + "x":[7], + "y":[6], + "type":"decoration" + }, + "Doll House": + { + "x":[6,7], + "y":[4], + "type":"decoration" + } + }, + "saloon": + { + "Exit": + { + "x":[14], + "y":[24], + "type":"door" + }, + "Counter": + { + "x":[14], + "y":[19], + "type":"interactable" + }, + "Journey of the Prairie King Arcade": + { + "x":[33], + "y":[17], + "type":"interactable" + }, + "Junimo Kart Arcade": + { + "x":[35], + "y":[17], + "type":"interactable" + }, + "Joja Vending Machine": + { + "x":[37,38], + "y":[17], + "type":"interactable" + }, + "Jukebox": + { + "x":[1,2], + "y":[17], + "type":"interactable" + }, + "Gus's Room Door": + { + "x":[20], + "y":[9], + "type":"door" + }, + "Dining Room Door": + { + "x":[11], + "y":[9], + "type":"door" + }, + "Living Area Door": + { + "x":[4], + "y":[16], + "type":"door" + }, + "Gus's Radio": + { + "x":[16], + "y":[6], + "type":"decoration" + } + }, + "sciencehouse": + { + "Exit": + { + "x":[6], + "y":[24], + "type":"door" + }, + "Secondary Exit": + { + "x":[3], + "y":[8], + "type":"door" + }, + "Counter": + { + "x":[8], + "y":[19], + "type":"interactable" + }, + "Sebastian's Room Entrance": + { + "x":[12], + "y":[21], + "type":"door" + }, + "Beaker Set": + { + "x":[17], + "y":[17], + "type":"decoration" + }, + "Microscope": + { + "x":[19], + "y":[17], + "type":"decoration" + }, + "Stereo Microscope": + { + "x":[23], + "y":[20], + "type":"decoration" + }, + "Robin and Demetrius's Room Entrance": + { + "x":[13], + "y":[10], + "type":"door" + }, + "Maru's Room Entrance": + { + "x":[7], + "y":[10], + "type":"door" + }, + "Bookshelf": + { + "x":[16,17], + "y":[4], + "type":"decoration" + }, + "Maru's Device": + { + "x":[6], + "y":[6], + "type":"decoration" + }, + "Poster": + { + "x":[6], + "y":[3], + "type":"decoration" + }, + "Computer": + { + "x":[9], + "y":[4], + "type":"decoration" + }, + "Fridge": + { + "x":[27], + "y":[8], + "type":"decoration" + }, + "Oven": + { + "x":[30], + "y":[10], + "type":"decoration" + } + }, + "sebastianroom": + { + "Exit": + { + "x":[1], + "y":[1], + "type":"door" + }, + "Room Door": + { + "x":[1], + "y":[3], + "type":"door" + }, + "Sebastian's Radio": + { + "x":[3], + "y":[4], + "type":"decoration" + }, + "Graphic Novel": + { + "x":[10], + "y":[6], + "type":"decoration" + }, + "Computer": + { + "x":[7], + "y":[4], + "type":"decoration" + } + }, + "samhouse": + { + "Exit": + { + "x":[4], + "y":[23], + "type":"door" + }, + "Radio": + { + "x":[6], + "y":[12], + "type":"decoration" + }, + "Vincent's Room Door": + { + "x":[16], + "y":[18], + "type":"door" + }, + "Sam's Room Door": + { + "x":[12], + "y":[14], + "type":"door" + }, + "Jodi's Room Door": + { + "x":[17], + "y":[6], + "type":"door" + }, + "Sam's Drawer": + { + "x":[7,8], + "y":[12], + "type":"decoration" + }, + "Bookshelf": + { + "x":[18,19], + "y":[12], + "type":"decoration" + }, + "Fridge": + { + "x":[7], + "y":[4], + "type":"decoration" + } + }, + "haleyhouse": + { + "Exit": + { + "x":[2], + "y":[24], + "type":"door" + }, + "Sewing Machine": + { + "x":[12,13], + "y":[23], + "type":"interactable" + }, + "Dye Pots": + { + "x":[17], + "y":[25], + "type":"interactable" + }, + "Emily's Room Door": + { + "x":[16], + "y":[12], + "type":"door" + }, + "Emily's Computer": + { + "x":[22], + "y":[6], + "type":"decoration" + }, + "Emily's Pet Parrot": + { + "x":[14], + "y":[4], + "type":"decoration" + }, + "Magazine": + { + "x":[4], + "y":[22], + "type":"decoration" + }, + "Globe": + { + "x":[8], + "y":[15], + "type":"decoration" + }, + "Fridge": + { + "x":[21], + "y":[15], + "type":"decoration" + }, + "Haley's Room Door": + { + "x":[5], + "y":[13], + "type":"door" + }, + "Futan Bear": + { + "x":[8], + "y":[4], + "type":"decoration" + }, + "Diary": + { + "x":[9], + "y":[5], + "type":"decoration" + }, + "Haley's Camera": + { + "x":[1], + "y":[9], + "type":"decoration" + } + }, + "joshhouse": + { + "Exit": + { + "x":[9], + "y":[24], + "type":"door" + }, + "TV": + { + "x":[15,16], + "y":[20], + "type":"decoration" + }, + "Bookshelf": + { + "x":[17,18], + "y":[16], + "type":"decoration" + }, + "Fridge": + { + "x":[5], + "y":[16], + "type":"decoration" + }, + "Evelyn and George's Room Door": + { + "x":[5], + "y":[9], + "type":"door" + }, + "Alex's Room Door": + { + "x":[10], + "y":[10], + "type":"door" + }, + "Radio": + { + "x":[3], + "y":[4], + "type":"door" + }, + "Magazine": + { + "x":[11], + "y":[4], + "type":"door" + }, + "Alex's Bookshelf": + { + "x":[12,13], + "y":[4], + "type":"decoration" + }, + "Alex's Drawer": + { + "x":[17,18], + "y":[4], + "type":"decoration" + }, + "Dumbbell": + { + "x":[14], + "y":[4], + "type":"door" + }, + "Gridball": + { + "x":[23], + "y":[45], + "type":"door" + }, + "Gridball Helmet": + { + "x":[23], + "y":[6], + "type":"door" + } + }, + "trailer": + { + "Exit": + { + "x":[12], + "y":[9], + "type":"door" + }, + "Penny's Room Door": + { + "x":[6], + "y":[7], + "type":"door" + }, + "Bookshelf": + { + "x":[5,6], + "y":[4], + "type":"decoration" + }, + "Book": + { + "x":[2], + "y":[4], + "type":"decoration" + }, + "Magazine": + { + "x":[1], + "y":[9], + "type":"decoration" + } } - }, - "farmcave": { - "Exit": { - "x": [ 8 ], - "y": [ 11 ], - "type": "door" - } - }, - "busstop": { - "Ticket Machine": { - "x": [ 7 ], - "y": [ 11 ], - "type": "interactable" - }, - "Minecart": { - "x": [ 4, 5 ], - "y": [ 3 ], - "type": "interactable" - }, - "Farm Entrance": { - "x": [ 0 ], - "y": [ 23 ], - "type": "door" - }, - "Town Entrance": { - "x": [ 34 ], - "y": [ 23 ], - "type": "door" - }, - "Backwoods Entrance": { - "x": [ 0 ], - "y": [ 6, 7, 8, 9 ], - "type": "door" - } - }, - "town": { - "Calender Board": { - "x": [ 41 ], - "y": [ 56 ], - "type": "interactable" - }, - "Daily Quest Board": { - "x": [ 42 ], - "y": [ 56 ], - "type": "interactable" - }, - "Sewer": { - "x": [ 34, 35 ], - "y": [ 95, 96 ], - "type": "interactable" - }, - "Ice Cream Stand": { - "x": [ 88 ], - "y": [ 92 ], - "type": "interactable" - }, - "Minecart": { - "x": [ 105, 106 ], - "y": [ 79 ], - "type": "interactable" - }, - "Bus Stop Entrance": { - "x": [ 0 ], - "y": [ 54 ], - "type": "door" - }, - "Cindersap Forest Entrance": { - "x": [ 0 ], - "y": [ 90 ], - "type": "door" - }, - "Beach Entrance": { - "x": [ 54 ], - "y": [ 109 ], - "type": "door" - }, - "Mountain Entrance": { - "x": [ 81 ], - "y": [ 0 ], - "type": "door" - } - }, - "shed": { - "Exit": { - "x": [ 6 ], - "y": [ 13 ], - "type": "door" - } - }, - "coop": { - "Hay Hopper": { - "x": [ 3 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 1": { - "x": [ 6 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 2": { - "x": [ 7 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 3": { - "x": [ 8 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 4": { - "x": [ 9 ], - "y": [ 3 ], - "type": "interactable" - }, - "Exit": { - "x": [ 2 ], - "y": [ 9 ], - "type": "door" - } - }, - "big coop": { - "Hay Hopper": { - "x": [ 3 ], - "y": [ 3 ], - "type": "interactable" - }, - "Incubator": { - "x": [ 2 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 1": { - "x": [ 6 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 2": { - "x": [ 7 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 3": { - "x": [ 8 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 4": { - "x": [ 9 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 5": { - "x": [ 10 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 6": { - "x": [ 11 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 7": { - "x": [ 12 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 8": { - "x": [ 13 ], - "y": [ 3 ], - "type": "interactable" - }, - "Exit": { - "x": [ 2 ], - "y": [ 9 ], - "type": "door" - } - }, - "coop2": { - "Hay Hopper": { - "x": [ 3 ], - "y": [ 3 ], - "type": "interactable" - }, - "Incubator": { - "x": [ 2 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 1": { - "x": [ 6 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 2": { - "x": [ 7 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 3": { - "x": [ 8 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 4": { - "x": [ 9 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 5": { - "x": [ 10 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 6": { - "x": [ 11 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 7": { - "x": [ 12 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 8": { - "x": [ 13 ], - "y": [ 3 ], - "type": "interactable" - }, - "Exit": { - "x": [ 2 ], - "y": [ 9 ], - "type": "door" - } - }, - "deluxe coop": { - "Hay Hopper": { - "x": [ 3 ], - "y": [ 3 ], - "type": "interactable" - }, - "Incubator": { - "x": [ 2 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 1": { - "x": [ 6 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 2": { - "x": [ 7 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 3": { - "x": [ 8 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 4": { - "x": [ 9 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 5": { - "x": [ 10 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 6": { - "x": [ 11 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 7": { - "x": [ 12 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 8": { - "x": [ 13 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 9": { - "x": [ 14 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 10": { - "x": [ 15 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 11": { - "x": [ 16 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 12": { - "x": [ 17 ], - "y": [ 3 ], - "type": "interactable" - }, - "Exit": { - "x": [ 2 ], - "y": [ 9 ], - "type": "door" - } - }, - "coop3": { - "Hay Hopper": { - "x": [ 3 ], - "y": [ 3 ], - "type": "interactable" - }, - "Incubator": { - "x": [ 2 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 1": { - "x": [ 6 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 2": { - "x": [ 7 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 3": { - "x": [ 8 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 4": { - "x": [ 9 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 5": { - "x": [ 10 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 6": { - "x": [ 11 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 7": { - "x": [ 12 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 8": { - "x": [ 13 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 9": { - "x": [ 14 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 10": { - "x": [ 15 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 11": { - "x": [ 16 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 12": { - "x": [ 17 ], - "y": [ 3 ], - "type": "interactable" - }, - "Exit": { - "x": [ 2 ], - "y": [ 9 ], - "type": "door" - } - }, - "barn": { - "Hay Hopper": { - "x": [ 6 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 1": { - "x": [ 8 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 2": { - "x": [ 9 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 3": { - "x": [ 10 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 4": { - "x": [ 11 ], - "y": [ 3 ], - "type": "interactable" - }, - "Exit": { - "x": [ 11 ], - "y": [ 14 ], - "type": "door" - } - }, - "barn2": { - "Hay Hopper": { - "x": [ 6 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 1": { - "x": [ 8 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 2": { - "x": [ 9 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 3": { - "x": [ 10 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 4": { - "x": [ 11 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 5": { - "x": [ 12 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 6": { - "x": [ 13 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 7": { - "x": [ 14 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 8": { - "x": [ 15 ], - "y": [ 3 ], - "type": "interactable" - }, - "Exit": { - "x": [ 11 ], - "y": [ 14 ], - "type": "door" - } - }, - "big barn": { - "Hay Hopper": { - "x": [ 6 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 1": { - "x": [ 8 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 2": { - "x": [ 9 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 3": { - "x": [ 10 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 4": { - "x": [ 11 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 5": { - "x": [ 12 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 6": { - "x": [ 13 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 7": { - "x": [ 14 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 8": { - "x": [ 15 ], - "y": [ 3 ], - "type": "interactable" - }, - "Exit": { - "x": [ 11 ], - "y": [ 14 ], - "type": "door" - } - }, - "barn3": { - "Hay Hopper": { - "x": [ 6 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 1": { - "x": [ 8 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 2": { - "x": [ 9 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 3": { - "x": [ 10 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 4": { - "x": [ 11 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 5": { - "x": [ 12 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 6": { - "x": [ 13 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 7": { - "x": [ 14 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 8": { - "x": [ 15 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 9": { - "x": [ 16 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 10": { - "x": [ 17 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 11": { - "x": [ 18 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 12": { - "x": [ 19 ], - "y": [ 3 ], - "type": "interactable" - }, - "Exit": { - "x": [ 11 ], - "y": [ 14 ], - "type": "door" - } - }, - "deluxe barn": { - "Hay Hopper": { - "x": [ 6 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 1": { - "x": [ 8 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 2": { - "x": [ 9 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 3": { - "x": [ 10 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 4": { - "x": [ 11 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 5": { - "x": [ 12 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 6": { - "x": [ 13 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 7": { - "x": [ 14 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 8": { - "x": [ 15 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 9": { - "x": [ 16 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 10": { - "x": [ 17 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 11": { - "x": [ 18 ], - "y": [ 3 ], - "type": "interactable" - }, - "Feeding Bench 12": { - "x": [ 19 ], - "y": [ 3 ], - "type": "interactable" - }, - "Exit": { - "x": [ 11 ], - "y": [ 14 ], - "type": "door" - } - }, - "slime hutch": { - "Water Trough 1": { - "x": [ 16 ], - "y": [ 6 ], - "type": "interactable" - }, - "Water Trough 2": { - "x": [ 16 ], - "y": [ 7 ], - "type": "interactable" - }, - "Water Trough 3": { - "x": [ 16 ], - "y": [ 8 ], - "type": "interactable" - }, - "Water Trough 4": { - "x": [ 16 ], - "y": [ 9 ], - "type": "interactable" - }, - "Exit": { - "x": [ 8 ], - "y": [ 12 ], - "type": "door" - } - }, - "adventureguild": { - "Goals Board": { - "x": [ 8 ], - "y": [ 10 ], - "type": "interactable" - }, - "Shop Counter": { - "x": [ 5 ], - "y": [ 12 ], - "type": "interactable" - }, - "Gil": { - "x": [ 11 ], - "y": [ 12 ], - "type": "npc" - }, - "Exit": { - "x": [ 6 ], - "y": [ 19 ], - "type": "door" - } - }, - "caldera": { - "Rare Chest": { - "x": [ 25 ], - "y": [ 28 ], - "type": "chest" - }, - "Forge": { - "x": [ 23 ], - "y": [ 21 ], - "type": "interactable" - }, - "Volcano Dungeon 0 Entrance": { - "x": [ 11 ], - "y": [ 36 ], - "type": "door" - }, - "Volcano Dungeon 9 Entrance": { - "x": [ 21 ], - "y": [ 39 ], - "type": "door" - } - }, - "volcanodungeon0": { - "Caldera Entrance": { - "x": [ 44 ], - "y": [ 50 ], - "type": "door" - }, - "Volcano Dungeon 1 Entrance": { - "x": [ 37 ], - "y": [ 5 ], - "type": "door" - }, - "Island North Entrance": { - "x": [ 31 ], - "y": [ 54 ], - "type": "door" - }, - "Island North Entrance 2": { - "x": [6], - "y": [ 50 ], - "type": "door" - } - }, - "club": { - "Coin Machine": { - "x": [ 12 ], - "y": [ 4 ], - "type": "interactable" - }, - "Shop Counter": { - "x": [ 25 ], - "y": [ 3 ], - "type": "interactable" - }, - "Calico Spin Machine": { - "x": [ 11, 13, 15 ], - "y": [ 8, 10 ], - "type": "interactable" - }, - "High Stakes Calico Jack Table": { - "x": [ 23, 24 ], - "y": [ 10, 11 ], - "type": "interactable" - }, - "Low Stakes Calico Jack Table": { - "x": [ 3 ], - "y": [ 7, 9 ], - "type": "interactable" - }, - "Man": { - "x": [ 13 ], - "y": [ 11 ], - "type": "npc" - }, - "Welwick": { - "x": [ 18 ], - "y": [ 9 ], - "type": "npc" - }, - "Unknown person": { - "x": [ 16 ], - "y": [ 4 ], - "type": "npc" - }, - "Stats Checker": { - "x": [ 3 ], - "y": [ 4 ], - "type": "interactable" - }, - "Exit": { - "x": [ 8 ], - "y": [ 12 ], - "type": "door" - } - }, - "desert": { - "Bus": { - "x": [ 18 ], - "y": [ 27 ], - "type": "interactable" - }, - "Desert Trader": { - "x": [ 42 ], - "y": [ 23 ], - "type": "interactable" - }, - "Three Pillars": { - "x": [ 34, 37, 40 ], - "y": [ 8, 13 ], - "type": "decoration" - }, - "Three Pillars Center": { - "x": [ 37 ], - "y": [ 11 ], - "type": "interactable" - }, - "Skull Cavern Entrance": { - "x": [ 8 ], - "y": [ 6 ], - "type": "door" - }, - "Desert Warp Statue": { - "x": [ 35 ], - "y": [ 43 ], - "type": "decoration" - }, - "Sand Dragon Skull": { - "x": [ 9, 10 ], - "y": [ 35, 36 ], - "type": "decoration" - } - }, - "fishshop": { - "Shop Counter": { - "x": [ 5 ], - "y": [ 5 ], - "type": "interactable" - }, - "Exit": { - "x": [ 5 ], - "y": [ 9 ], - "type": "door" - } - }, - "boattunnel": { - "Exit": { - "x": [ 6 ], - "y": [ 11 ], - "type": "door" - } - }, - "beach": { - "Town Entrance": { - "x": [ 38 ], - "y": [ 0 ], - "type": "door" - }, - "Beach Warp Statue": { - "x": [ 20 ], - "y": [ 4 ], - "type": "decoration" - } - }, - "forest": { - "Farm Entrance": { - "x": [ 68 ], - "y": [ 0 ], - "type": "door" - }, - "Town Entrance": { - "x": [ 119 ], - "y": [ 25 ], - "type": "door" - }, - "Bridge 1": { - "x": [ 77, 82 ], - "y": [ 49 ], - "type": "bridge" - }, - "Bridge 2": { - "x": [ 87 ], - "y": [ 52, 56 ], - "type": "bridge" - }, - "Bridge 3": { - "x": [ 65, 62 ], - "y": [ 70 ], - "type": "bridge" - }, - "Bridge 4": { - "x": [ 41 ], - "y": [ 79, 82 ], - "type": "bridge" - }, - "Bridge 5": { - "x": [ 38 ], - "y": [ 85, 87 ], - "type": "bridge" - }, - "Abandoned House": { - "x": [ 34 ], - "y": [ 95 ], - "type": "interactable" - } - }, - "beachnightmarket": { - "Desert Trader": { - "x": [ 14 ], - "y": [ 37 ], - "type": "npc" - }, - "Famous Painter Lupini": { - "x": [ 43 ], - "y": [ 34 ], - "type": "npc" - }, - "Fishing Submarine Door": { - "x": [ 5 ], - "y": [ 34 ], - "type": "door" - }, - "Travelling Cart": { - "x": [ 39 ], - "y": [ 30 ], - "type": "interactable" - }, - "Shrouded Figure": { - "x": [ 32 ], - "y": [ 34 ], - "type": "npc" - }, - "Decoration Boat": { - "x": [ 19 ], - "y": [ 33 ], - "type": "interactable" - }, - "Magic Shop Boat": { - "x": [ 48 ], - "y": [ 34 ], - "type": "interactable" - }, - "Mermaid Boat Door": { - "x": [ 58 ], - "y": [ 31 ], - "type": "door" - } - }, - "mermaidhouse": { - "Exit": { - "x": [ 4 ], - "y": [ 10 ], - "type": "door" - }, - "Clam Shell 1": { - "x": [ 2 ], - "y": [ 6 ], - "type": "interactable" - }, - "Clam Shell 2": { - "x": [ 3 ], - "y": [ 6 ], - "type": "interactable" - }, - "Clam Shell 3": { - "x": [ 4 ], - "y": [ 6 ], - "type": "interactable" - }, - "Clam Shell 4": { - "x": [ 5 ], - "y": [ 6 ], - "type": "interactable" - }, - "Clam Shell 5": { - "x": [ 6 ], - "y": [ 6 ], - "type": "interactable" - } - }, - "submarine": { - "Exit": { - "x": [ 14 ], - "y": [ 15 ], - "type": "door" - }, - "Captain": { - "x": [ 2 ], - "y": [ 9 ], - "type": "npc" - } - }, - "cellar": { - "Exit": { - "x": [ 3 ], - "y": [ 2 ], - "type": "door" - } - }, - "communitycenter": { - "Exit": { - "x": [ 32 ], - "y": [ 23 ], - "type": "door" - } - }, - "islandeast": { - "Banana Shrine": { - "x": [ 16 ], - "y": [ 26 ], - "type": "interactable" - }, - "Jungle Parrot Express": { - "x": [ 28 ], - "y": [ 27 ], - "type": "interactable" - }, - "Island Hut Entrance": { - "x": [ 22 ], - "y": [ 10 ], - "type": "door" - }, - "Island South Entrance": { - "x": [ 0 ], - "y": [ 46 ], - "type": "door" - }, - "Island Shrine Entrance": { - "x": [ 32 ], - "y": [ 30 ], - "type": "door" - } - }, - "islandhut": { - "Exit": { - "x": [ 7 ], - "y": [ 13 ], - "type": "door" - } - }, - "islandsouth": { - "Island East Entrance": { - "x": [ 34 ], - "y": [ 12 ], - "type": "door" - }, - "Ginger Island Warp Statue": { - "x": [ 11 ], - "y": [ 11 ], - "type": "decoration" - }, - "Island West Entrance": { - "x": [ 0 ], - "y": [ 11 ], - "type": "door" - }, - "Island North Entrance": { - "x": [ 17 ], - "y": [ 0 ], - "type": "door" - }, - "Island North Entrance 2": { - "x": [ 27, 28 ], - "y": [ 0 ], - "type": "door" - }, - "Docks Parrot Express": { - "x": [ 6 ], - "y": [ 31 ], - "type": "interactable" - }, - "Return Boat": { - "x": [ 19 ], - "y": [ 43 ], - "type": "interactable" - } - }, - "islandwest": { - "Farm Parrot Express": { - "x": [ 74 ], - "y": [ 9 ], - "type": "interactable" - }, - "Bridge 1": { - "x": [ 67, 62 ], - "y": [ 16 ], - "type": "bridge" - }, - "Qi's Walnut Room Door": { - "x": [ 20 ], - "y": [ 22 ], - "type": "door" - }, - "Door to Shipwreck interior": { - "x": [ 60 ], - "y": [ 92 ], - "type": "door" - }, - "Hole 1": { - "x": [ 37 ], - "y": [ 87 ], - "type": "interactable" - }, - "Hole 2": { - "x": [ 41 ], - "y": [ 86 ], - "type": "interactable" - }, - "Hole 3": { - "x": [ 45 ], - "y": [ 86 ], - "type": "interactable" - }, - "Hole 4": { - "x": [ 48 ], - "y": [ 87 ], - "type": "interactable" - }, - "Bridge 2": { - "x": [ 55, 52 ], - "y": [ 80 ], - "type": "bridge" - }, - "Island Farm House Door": { - "x": [ 77 ], - "y": [ 39 ], - "type": "door" - }, - "Island Farm Cave Entrance": { - "x": [ 96 ], - "y": [ 33 ], - "type": "door" - }, - "Island South Entrance": { - "x": [ 105 ], - "y": [ 40 ], - "type": "door" - } - }, - "captainroom": { - "exit": { - "x": [ 0 ], - "y": [ 5 ], - "type": "door" - } - }, - "islandfarmcave": { - "Exit": { - "x": [ 4 ], - "y": [ 10 ], - "type": "door" - }, - "Gourmand Frog": { - "x": [ 5 ], - "y": [ 4 ], - "type": "npc" - } - }, - "islandnorth": { - "Island South Entrance": { - "x": [ 35 ], - "y": [ 89 ], - "type": "door" - }, - "Island South Entrance 2": { - "x": [ 43, 44 ], - "y": [ 89 ], - "type": "door" - }, - "Island Field Office Entrance": { - "x": [ 46 ], - "y": [ 46 ], - "type": "door" - }, - "Island North Cave Entrance": { - "x": [ 21, 22 ], - "y": [ 47 ], - "type": "door" - }, - "Dig Site Parrot Express": { - "x": [ 5 ], - "y": [ 48 ], - "type": "interactable" - }, - "Volcano Dungeon Entrance": { - "x": [ 40 ], - "y": [ 22 ], - "type": "door" - }, - "Volcano Dungeon Entrance 2": { - "x": [ 12 ], - "y": [ 30 ], - "type": "door" - }, - "Volcano Parrot Express": { - "x": [ 60 ], - "y": [ 16 ], - "type": "interactable" - } - }, - "islandfieldoffice": { - "Exit": { - "x": [ 4 ], - "y": [ 10 ], - "type": "door" - }, - "Counter": { - "x": [ 8 ], - "y": [ 7 ], - "type": "interactable" - }, - "Island Survey": { - "x": [ 5 ], - "y": [ 3 ], - "type": "interactable" - } - }, - "qinutroom": { - "Exit": { - "x": [ 7 ], - "y": [ 7 ], - "type": "door" - }, - "Perfection Tracker": { - "x": [ 13 ], - "y": [ 4 ], - "type": "interactable" - }, - "Vending Machine": { - "x": [ 11 ], - "y": [ 3 ], - "type": "interactable" - }, - "Special Order Board": { - "x": [ 3 ], - "y": [ 3 ], - "type": "interactable" - } - }, - "islandsoutheast": { - "Island South East Cave Entrance": { - "x": [ 30 ], - "y": [ 18 ], - "type": "door" - } - }, - "islandsoutheastcave": { - "Exit": { - "x": [ 1 ], - "y": [ 8 ], - "type": "door" - } - }, - "islandshrine": { - "Exit": { - "x": [ 13 ], - "y": [ 28 ], - "type": "door" - }, - "Shrine": { - "x": [ 24 ], - "y": [ 22 ], - "type": "interactable" - }, - "North Pedestal": { - "x": [ 24 ], - "y": [ 25 ], - "type": "interactable" - }, - "East Pedestal": { - "x": [ 27 ], - "y": [ 27 ], - "type": "interactable" - }, - "West Pedestal": { - "x": [ 21 ], - "y": [ 27 ], - "type": "interactable" - }, - "South Pedestal": { - "x": [ 24 ], - "y": [ 28 ], - "type": "interactable" - } - }, - "jojamart": { - "Exit": { - "x": [ 13 ], - "y": [ 29 ], - "type": "door" - }, - "Morris's Kiosk": { - "x": [ 21 ], - "y": [ 25 ], - "type": "interactable" - }, - "Shop Counter": { - "x": [ 10 ], - "y": [ 25 ], - "type": "interactable" - } - }, - "archaeologyhouse": { - "Exit": { - "x": [ 3 ], - "y": [ 14 ], - "type": "door" - }, - "Counter": { - "x": [ 3 ], - "y": [ 9 ], - "type": "interactable" - } - }, - "manorhouse": { - "Exit": { - "x": [ 4 ], - "y": [ 11 ], - "type": "door" - }, - "Town Ledger Book": { - "x": [ 2 ], - "y": [ 5 ], - "type": "interactable" - }, - "Marriage Log Book": { - "x": [ 3 ], - "y": [ 5 ], - "type": "interactable" - }, - "Lost and Found Box": { - "x": [ 4 ], - "y": [ 5 ], - "type": "interactable" - }, - "Mayor's Room Door": { - "x": [ 16 ], - "y": [ 9 ], - "type": "door" - }, - "Mayor's Oven": { - "x": [ 7 ], - "y": [ 4 ], - "type": "decoration" - }, - "Mayor's Fridge": { - "x": [ 9 ], - "y": [ 4 ], - "type": "decoration" - } - }, - "mine": { - "Mountain Exit": { - "x": [ 18 ], - "y": [ 13 ], - "type": "door" - }, - "Minecart": { - "x": [ 11, 12 ], - "y": [ 10 ], - "type": "interactable" - }, - "Quarry Mine Ladder": { - "x": [ 67 ], - "y": [ 9 ], - "type": "door" - }, - "Quarry Exit": { - "x": [ 18 ], - "y": [ 13 ], - "type": "door" - } - }, - "mountain": { - "Mine Entrance": { - "x": [ 54 ], - "y": [ 5 ], - "type": "door" - }, - "Mine Bridge": { - "x": [ 47 ], - "y": [ 7 ], - "type": "bridge" - }, - "Quarry Bridge": { - "x": [ 90 ], - "y": [ 26 ], - "type": "bridge" - }, - "Minecart": { - "x": [ 124, 125 ], - "y": [ 11 ], - "type": "interactable" - }, - "Quarry Mine Entrance": { - "x": [ 103 ], - "y": [ 17 ], - "type": "door" - }, - "Bridge 1": { - "x": [ 57 ], - "y": [ 30 ], - "type": "bridge" - }, - "Bridge 2": { - "x": [ 61 ], - "y": [ 21 ], - "type": "bridge" - }, - "Mountain Warp Statue": { - "x": [ 31 ], - "y": [ 20 ], - "type": "decoration" - }, - "Linus Tent Entrance": { - "x": [ 29 ], - "y": [ 7 ], - "type": "door" - }, - "Backwoods Entrance": { - "x": [ 0 ], - "y": [ 13 ], - "type": "door" - }, - "Town Entrance": { - "x": [ 15 ], - "y": [ 40 ], - "type": "door" - }, - "Railroad Entrance": { - "x": [ 9 ], - "y": [ 0 ], - "type": "door" - }, - "Science House Secondary Door": { - "x": [ 8 ], - "y": [ 20 ], - "type": "door" - } - }, - "undergroundmine77377": { - "Grim Reaper Statue": { - "x": [ 29, 30 ], - "y": [ 6 ], - "type": "interactable" - } - }, - "Tent": { - "Exit": { - "x": [ 2 ], - "y": [ 5 ], - "type": "door" - } - }, - "railroad": { - "Mountain Entrance": { - "x": [ 29 ], - "y": [ 61 ], - "type": "door" - } - }, - "backwoods": { - "Mountain Entrance": { - "x": [ 49 ], - "y": [ 14 ], - "type": "door" - }, - "Farm Entrance": { - "x": [ 14 ], - "y": [ 39 ], - "type": "door" - }, - "Bus stop Entrance": { - "x": [ 49 ], - "y": [ 28, 29, 30, 31, 32 ], - "type": "door" - }, - "Tunnel Entrance": { - "x": [ 23 ], - "y": [ 29, 30, 31 ], - "type": "door" - } - }, - "tunnel": { - "Exit": { - "x": [ 39 ], - "y": [ 7, 8, 9, 10, 11 ], - "type": "door" - } - }, - "movietheater": { - "Exit": { - "x": [ 13 ], - "y": [ 15 ], - "type": "door" - }, - "Concessions Counter": { - "x": [ 7 ], - "y": [ 6 ], - "type": "interactable" - }, - "Crane Game": { - "x": [ 1, 2 ], - "y": [ 8 ], - "type": "interactable" - }, - "Theater Door": { - "x": [ 13 ], - "y": [ 3 ], - "type": "door" - }, - "Crane Man": { - "x": [ 2 ], - "y": [ 9 ], - "type": "npc" - } - }, - "seedshop": { - "Exit": { - "x": [ 6 ], - "y": [ 29 ], - "type": "door" - }, - "Shop Counter": { - "x": [ 4 ], - "y": [ 18 ], - "type": "interactable" - }, - "Backpack Upgrade": { - "x": [ 7 ], - "y": [ 18 ], - "type": "interactable" - }, - "Shrine of Yoba": { - "x": [ 37 ], - "y": [ 17 ], - "type": "decoration" - }, - "Fridge": { - "x": [ 39 ], - "y": [ 4 ], - "type": "decoration" - }, - "Abigail's Room Door": { - "x": [ 13 ], - "y": [ 11 ], - "type": "door" - }, - "Pierre and Caroline's Room Door": { - "x": [ 20 ], - "y": [ 11 ], - "type": "door" - }, - "Living Area Door": { - "x": [ 14 ], - "y": [ 16 ], - "type": "door" - } - }, - "sewer": { - "Exit Ladder": { - "x": [ 16 ], - "y": [ 10 ], - "type": "door" - }, - "Statue Of Uncertainty": { - "x": [ 8 ], - "y": [ 20 ], - "type": "interactable" - }, - "Mutant Bug Lair": { - "x": [ 3 ], - "y": [ 19 ], - "type": "door" - } - }, - "wizardhouse": { - "Exit": { - "x": [ 8 ], - "y": [ 24 ], - "type": "door" - }, - "Basement Door": { - "x": [ 4 ], - "y": [ 4 ], - "type": "door" - } - }, - "wizardhousebasement": { - "Exit Ladder": { - "x": [ 4 ], - "y": [ 3 ], - "type": "door" - }, - "Shrine of Illusions": { - "x": [ 12 ], - "y": [ 4 ], - "type": "interactable" - } - }, - "woods": { - "Forest Entrance": { - "x": [ 59 ], - "y": [ 17 ], - "type": "door" - }, - "Old Master Cannoli": { - "x": [ 8, 9 ], - "y": [ 7 ], - "type": "interactable" - } - }, - "harveyroom": { - "Exit": { - "x": [ 6 ], - "y": [ 12 ], - "type": "door" - }, - "Fridge": { - "x": [ 21 ], - "y": [ 6 ], - "type": "decoration" - }, - "Oven": { - "x": [ 19 ], - "y": [ 6 ], - "type": "decoration" - }, - "Airplane Collection": { - "x": [ 6, 7 ], - "y": [ 3 ], - "type": "decoration" - }, - "Radio Broadcasting Set": { - "x": [ 4, 5 ], - "y": [ 4 ], - "type": "decoration" - }, - "Cassette Deck": { - "x": [ 8 ], - "y": [ 4 ], - "type": "decoration" - } - }, - "hospital": { - "Exit": { - "x": [ 10 ], - "y": [ 19 ], - "type": "door" - }, - "Harvey's Room Entrance": { - "x": [ 10 ], - "y": [ 2 ], - "type": "door" - }, - "Harvey's Room Entrance Door": { - "x": [ 10 ], - "y": [ 5 ], - "type": "door" - }, - "Main Area Door": { - "x": [ 10 ], - "y": [ 13 ], - "type": "door" - }, - "Counter": { - "x": [ 6 ], - "y": [ 16 ], - "type": "interactable" - } - }, - "blacksmith": { - "Exit": { - "x": [ 5 ], - "y": [ 19 ], - "type": "door" - }, - "Counter": { - "x": [ 3 ], - "y": [ 14 ], - "type": "interactable" - }, - "Clint's Room Door": { - "x": [ 4 ], - "y": [ 9 ], - "type": "door" - }, - "Clint's Furnace": { - "x": [ 9, 10 ], - "y": [ 12 ], - "type": "decoration" - }, - "Blueprints": { - "x": [ 13 ], - "y": [ 15, 16 ], - "type": "decoration" - }, - "Anvil": { - "x": [ 12, 13 ], - "y": [ 13 ], - "type": "decoration" - }, - "Cassette Deck": { - "x": [ 2 ], - "y": [ 4 ], - "type": "decoration" - }, - "Clint's Drawer": { - "x": [ 5, 6 ], - "y": [ 4 ], - "type": "decoration" - } - }, - "animalshop": { - "Exit": { - "x": [ 13 ], - "y": [ 19 ], - "type": "door" - }, - "Counter": { - "x": [ 12 ], - "y": [ 15 ], - "type": "interactable" - }, - "Marnie's Room Door": { - "x": [ 15 ], - "y": [ 12 ], - "type": "door" - }, - "Jas's Room Door": { - "x": [ 6 ], - "y": [ 13 ], - "type": "door" - }, - "Shane's Room Door": { - "x": [ 21 ], - "y": [ 13 ], - "type": "door" - }, - "Marnie's Barn Door": { - "x": [ 30 ], - "y": [ 13 ], - "type": "door" - }, - "Fridge": { - "x": [ 28 ], - "y": [ 14 ], - "type": "decoration" - }, - "Oven": { - "x": [ 24 ], - "y": [ 14 ], - "type": "decoration" - }, - "Mega Station": { - "x": [ 22 ], - "y": [ 5 ], - "type": "decoration" - }, - "Shane's Radio": { - "x": [ 24 ], - "y": [ 4 ], - "type": "decoration" - }, - "Marnie's Dresser": { - "x": [ 16 ], - "y": [ 4 ], - "type": "decoration" - }, - "Marnie's Drawer": { - "x": [ 17 ], - "y": [ 4 ], - "type": "decoration" - }, - "Jack in the Box": { - "x": [ 8 ], - "y": [ 5 ], - "type": "decoration" - }, - "Futan Bear": { - "x": [ 2, 3 ], - "y": [ 4 ], - "type": "decoration" - }, - "Colouring Book": { - "x": [ 5 ], - "y": [ 4 ], - "type": "decoration" - }, - "Paint Set": { - "x": [ 5 ], - "y": [ 7 ], - "type": "decoration" - }, - "Jas's Alarm Clock": { - "x": [ 8 ], - "y": [ 8 ], - "type": "decoration" - }, - "Jas's Radio": { - "x": [ 4 ], - "y": [ 9 ], - "type": "decoration" - }, - "Arts And Craft": { - "x": [ 7 ], - "y": [ 6 ], - "type": "decoration" - }, - "Doll House": { - "x": [ 6, 7 ], - "y": [ 4 ], - "type": "decoration" - } - }, - "saloon": { - "Exit": { - "x": [ 14 ], - "y": [ 24 ], - "type": "door" - }, - "Counter": { - "x": [ 14 ], - "y": [ 19 ], - "type": "interactable" - }, - "Journey of the Prairie King Arcade": { - "x": [ 33 ], - "y": [ 17 ], - "type": "interactable" - }, - "Junimo Kart Arcade": { - "x": [ 35 ], - "y": [ 17 ], - "type": "interactable" - }, - "Joja Vending Machine": { - "x": [ 37, 38 ], - "y": [ 17 ], - "type": "interactable" - }, - "Jukebox": { - "x": [ 1, 2 ], - "y": [ 17 ], - "type": "interactable" - }, - "Gus's Room Door": { - "x": [ 20 ], - "y": [ 9 ], - "type": "door" - }, - "Dining Room Door": { - "x": [ 11 ], - "y": [ 9 ], - "type": "door" - }, - "Living Area Door": { - "x": [ 4 ], - "y": [ 16 ], - "type": "door" - }, - "Gus's Radio": { - "x": [ 16 ], - "y": [ 6 ], - "type": "decoration" - } - }, - "sciencehouse": { - "Exit": { - "x": [ 6 ], - "y": [ 24 ], - "type": "door" - }, - "Secondary Exit": { - "x": [ 3 ], - "y": [ 8 ], - "type": "door" - }, - "Counter": { - "x": [ 8 ], - "y": [ 19 ], - "type": "interactable" - }, - "Sebastian's Room Entrance": { - "x": [ 12 ], - "y": [ 21 ], - "type": "door" - }, - "Beaker Set": { - "x": [ 17 ], - "y": [ 17 ], - "type": "decoration" - }, - "Microscope": { - "x": [ 19 ], - "y": [ 17 ], - "type": "decoration" - }, - "Stereo Microscope": { - "x": [ 23 ], - "y": [ 20 ], - "type": "decoration" - }, - "Robin and Demetrius's Room Entrance": { - "x": [ 13 ], - "y": [ 10 ], - "type": "door" - }, - "Maru's Room Entrance": { - "x": [ 7 ], - "y": [ 10 ], - "type": "door" - }, - "Bookshelf": { - "x": [ 16, 17 ], - "y": [ 4 ], - "type": "decoration" - }, - "Maru's Device": { - "x": [ 6 ], - "y": [ 6 ], - "type": "decoration" - }, - "Poster": { - "x": [ 6 ], - "y": [ 3 ], - "type": "decoration" - }, - "Computer": { - "x": [ 9 ], - "y": [ 4 ], - "type": "decoration" - }, - "Fridge": { - "x": [ 27 ], - "y": [ 8 ], - "type": "decoration" - }, - "Oven": { - "x": [ 30 ], - "y": [ 10 ], - "type": "decoration" - } - }, - "sebastianroom": { - "Exit": { - "x": [ 1 ], - "y": [ 1 ], - "type": "door" - }, - "Room Door": { - "x": [ 1 ], - "y": [ 3 ], - "type": "door" - }, - "Sebastian's Radio": { - "x": [ 3 ], - "y": [ 4 ], - "type": "decoration" - }, - "Graphic Novel": { - "x": [ 10 ], - "y": [ 6 ], - "type": "decoration" - }, - "Computer": { - "x": [ 7 ], - "y": [ 4 ], - "type": "decoration" - } - }, - "samhouse": { - "Exit": { - "x": [ 4 ], - "y": [ 23 ], - "type": "door" - }, - "Radio": { - "x": [ 6 ], - "y": [ 12 ], - "type": "decoration" - }, - "Vincent's Room Door": { - "x": [ 16 ], - "y": [ 18 ], - "type": "door" - }, - "Sam's Room Door": { - "x": [ 12 ], - "y": [ 14 ], - "type": "door" - }, - "Jodi's Room Door": { - "x": [ 17 ], - "y": [ 6 ], - "type": "door" - }, - "Sam's Drawer": { - "x": [ 7, 8 ], - "y": [ 12 ], - "type": "decoration" - }, - "Bookshelf": { - "x": [ 18, 19 ], - "y": [ 12 ], - "type": "decoration" - }, - "Fridge": { - "x": [ 7 ], - "y": [ 4 ], - "type": "decoration" - } - }, - "haleyhouse": { - "Exit": { - "x": [ 2 ], - "y": [ 24 ], - "type": "door" - }, - "Sewing Machine": { - "x": [ 12, 13 ], - "y": [ 23 ], - "type": "interactable" - }, - "Dye Pots": { - "x": [ 17 ], - "y": [ 25 ], - "type": "interactable" - }, - "Emily's Room Door": { - "x": [ 16 ], - "y": [ 12 ], - "type": "door" - }, - "Emily's Computer": { - "x": [ 22 ], - "y": [ 6 ], - "type": "decoration" - }, - "Emily's Pet Parrot": { - "x": [ 14 ], - "y": [ 4 ], - "type": "decoration" - }, - "Magazine": { - "x": [ 4 ], - "y": [ 22 ], - "type": "decoration" - }, - "Globe": { - "x": [ 8 ], - "y": [ 15 ], - "type": "decoration" - }, - "Fridge": { - "x": [ 21 ], - "y": [ 15 ], - "type": "decoration" - }, - "Haley's Room Door": { - "x": [ 5 ], - "y": [ 13 ], - "type": "door" - }, - "Futan Bear": { - "x": [ 8 ], - "y": [ 4 ], - "type": "decoration" - }, - "Diary": { - "x": [ 9 ], - "y": [ 5 ], - "type": "decoration" - }, - "Haley's Camera": { - "x": [ 1 ], - "y": [ 9 ], - "type": "decoration" - } - }, - "joshhouse": { - "Exit": { - "x": [ 9 ], - "y": [ 24 ], - "type": "door" - }, - "TV": { - "x": [ 15, 16 ], - "y": [ 20 ], - "type": "decoration" - }, - "Bookshelf": { - "x": [ 17, 18 ], - "y": [ 16 ], - "type": "decoration" - }, - "Fridge": { - "x": [ 5 ], - "y": [ 16 ], - "type": "decoration" - }, - "Evelyn and George's Room Door": { - "x": [ 5 ], - "y": [ 9 ], - "type": "door" - }, - "Alex's Room Door": { - "x": [ 10 ], - "y": [ 10 ], - "type": "door" - }, - "Radio": { - "x": [ 3 ], - "y": [ 4 ], - "type": "door" - }, - "Magazine": { - "x": [ 11 ], - "y": [ 4 ], - "type": "door" - }, - "Alex's Bookshelf": { - "x": [ 12, 13 ], - "y": [ 4 ], - "type": "decoration" - }, - "Alex's Drawer": { - "x": [ 17, 18 ], - "y": [ 4 ], - "type": "decoration" - }, - "Dumbbell": { - "x": [ 14 ], - "y": [ 4 ], - "type": "door" - }, - "Gridball": { - "x": [ 23 ], - "y": [ 45 ], - "type": "door" - }, - "Gridball Helmet": { - "x": [ 23 ], - "y": [ 6 ], - "type": "door" - } - }, - "trailer": { - "Exit": { - "x": [ 12 ], - "y": [ 9 ], - "type": "door" - }, - "Penny's Room Door": { - "x": [ 6 ], - "y": [ 7 ], - "type": "door" - }, - "Bookshelf": { - "x": [ 5, 6 ], - "y": [ 4 ], - "type": "decoration" - }, - "Book": { - "x": [ 2 ], - "y": [ 4 ], - "type": "decoration" - }, - "Magazine": { - "x": [ 1 ], - "y": [ 9 ], - "type": "decoration" - } - } } \ No newline at end of file From 9c1a185047cd615ac862e8af328ed79718ac3d8f Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 30 May 2022 22:34:54 +0530 Subject: [PATCH 165/232] Revamped character creation menu --- stardew-access/Patches/TitleMenuPatches.cs | 96 ++++++++++++++-------- stardew-access/manifest.json | 2 +- stardew-access/stardew-access.csproj | 1 - 3 files changed, 62 insertions(+), 37 deletions(-) diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index 8dcb17f..892cdc7 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -10,6 +10,9 @@ namespace stardew_access.Patches { private static int saveGameIndex = -1; private static bool isRunning = false; + public static string characterCreationMenuQueryKey = " "; + public static string prevPetName = " "; + internal static void CoopMenuPatch(CoopMenu __instance, CoopMenu.Tab ___currentTab) { @@ -171,11 +174,43 @@ namespace stardew_access.Patches } internal static void CharacterCustomizationMenuPatch(CharacterCustomization __instance, bool ___skipIntro, - ClickableComponent ___startingCabinsLabel, ClickableComponent ___difficultyModifierLabel) + ClickableComponent ___startingCabinsLabel, ClickableComponent ___difficultyModifierLabel, TextBox ___nameBox, + TextBox ___farmnameBox, TextBox ___favThingBox) { try { - if (MainClass.Config.CharacterCreationMenuNextKey.JustPressed() && !isRunning) + bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box + string toSpeak = " "; + string currentPetName = getCurrentPetName(); + + if (___nameBox.Selected) + { + toSpeak = ___nameBox.Text; + + if (isEscPressed) + { + ___nameBox.Selected = false; + } + } + else if (___farmnameBox.Selected) + { + toSpeak = ___farmnameBox.Text; + + if (isEscPressed) + { + ___farmnameBox.Selected = false; + } + } + else if (___favThingBox.Selected) + { + toSpeak = ___favThingBox.Text; + + if (isEscPressed) + { + ___favThingBox.Selected = false; + } + } + else if (MainClass.Config.CharacterCreationMenuNextKey.JustPressed() && !isRunning) { isRunning = true; CycleThroughItems(true, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel); @@ -187,6 +222,18 @@ namespace stardew_access.Patches CycleThroughItems(false, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel); Task.Delay(200).ContinueWith(_ => { isRunning = false; }); } + + if (prevPetName != currentPetName) + { + prevPetName = currentPetName; + toSpeak = $"Current Pet: {currentPetName} \n {toSpeak}"; + } + + if (characterCreationMenuQueryKey != toSpeak && toSpeak != " ") + { + characterCreationMenuQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } } catch (Exception e) { @@ -204,21 +251,21 @@ namespace stardew_access.Patches #region Character related if (__instance.nameBoxCC != null && __instance.nameBoxCC.visible) - buttons.Add(__instance.nameBoxCC, "Enter Farmer's Name"); + buttons.Add(__instance.nameBoxCC, "Farmer's Name Text box"); if (__instance.farmnameBoxCC != null && __instance.farmnameBoxCC.visible) - buttons.Add(__instance.farmnameBoxCC, "Enter Farm's Name"); + buttons.Add(__instance.farmnameBoxCC, "Farm's Name Text box"); if (__instance.favThingBoxCC != null && __instance.favThingBoxCC.visible) - buttons.Add(__instance.favThingBoxCC, "Enter Favourite Thing"); + buttons.Add(__instance.favThingBoxCC, "Favourite Thing Text box"); if (__instance.petPortraitBox.HasValue) // Cannot get petButtons like with others { ClickableComponent petPrev = __instance.getComponentWithID(511); - buttons.Add(petPrev, "Previous pet: " + getPetName(-1, __instance.isModifyingExistingPet)); + buttons.Add(petPrev, "Previous pet button"); ClickableComponent petNext = __instance.getComponentWithID(510); - buttons.Add(petNext, "Next pet: " + getPetName(+1, __instance.isModifyingExistingPet)); + buttons.Add(petNext, "Next pet button"); } if (__instance.randomButton != null && __instance.randomButton.visible) @@ -226,21 +273,18 @@ namespace stardew_access.Patches if (__instance.genderButtons.Count > 0) { - buttons.Add(__instance.genderButtons[0], "Gender: Male Button"); - buttons.Add(__instance.genderButtons[1], "Gender: Female Button"); + buttons.Add(__instance.genderButtons[0], ((Game1.player.IsMale) ? "Selected " : "") + "Gender: Male Button"); + buttons.Add(__instance.genderButtons[1], ((!Game1.player.IsMale) ? "Selected " : "") + "Gender: Female Button"); } #endregion #region Farm layout related if (__instance.farmTypeButtons.Count > 0) { - buttons.Add(__instance.farmTypeButtons[0], getFarmHoverText(__instance.farmTypeButtons[0])); - buttons.Add(__instance.farmTypeButtons[1], getFarmHoverText(__instance.farmTypeButtons[1])); - buttons.Add(__instance.farmTypeButtons[2], getFarmHoverText(__instance.farmTypeButtons[2])); - buttons.Add(__instance.farmTypeButtons[3], getFarmHoverText(__instance.farmTypeButtons[3])); - buttons.Add(__instance.farmTypeButtons[4], getFarmHoverText(__instance.farmTypeButtons[4])); - buttons.Add(__instance.farmTypeButtons[5], getFarmHoverText(__instance.farmTypeButtons[5])); - buttons.Add(__instance.farmTypeButtons[6], getFarmHoverText(__instance.farmTypeButtons[6])); + for (int i = 0; i < __instance.farmTypeButtons.Count; i++) + { + buttons.Add(__instance.farmTypeButtons[i], ((i == Game1.whichFarm) ? "Selected " : "") + getFarmHoverText(__instance.farmTypeButtons[i])); + } } if (__instance.farmTypeNextPageButton != null && __instance.farmTypeNextPageButton.visible) @@ -314,26 +358,8 @@ namespace stardew_access.Patches } } - private static string getPetName(int change, bool isModifyingExistingPet) + private static string getCurrentPetName() { - Game1.player.whichPetBreed += change; - if (Game1.player.whichPetBreed >= 3) - { - Game1.player.whichPetBreed = 0; - if (!isModifyingExistingPet) - { - Game1.player.catPerson = !Game1.player.catPerson; - } - } - else if (Game1.player.whichPetBreed < 0) - { - Game1.player.whichPetBreed = 2; - if (!isModifyingExistingPet) - { - Game1.player.catPerson = !Game1.player.catPerson; - } - } - return ((Game1.player.catPerson) ? "Cat" : "Dog") + " Breed: " + Game1.player.whichPetBreed; } diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index 2258452..6eb3d30 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.2.3", + "Version": "1.2.4", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", diff --git a/stardew-access/stardew-access.csproj b/stardew-access/stardew-access.csproj index c7ca1ff..b3ab566 100644 --- a/stardew-access/stardew-access.csproj +++ b/stardew-access/stardew-access.csproj @@ -1,7 +1,6 @@  - /home/shoaib/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/Stardew Valley/ net5.0 stardew_access enable From e534a6aed976cd853ff192e4daf7a46afe4099d2 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 30 May 2022 23:38:09 +0530 Subject: [PATCH 166/232] Fixed move animal to different building --- stardew-access/CustomCommands.cs | 13 +- stardew-access/HarmonyPatches.cs | 10 +- .../Patches/BuildingNAnimalMenuPatches.cs | 130 +++++++++++++++++- stardew-access/Patches/MenuPatches.cs | 73 ---------- 4 files changed, 141 insertions(+), 85 deletions(-) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index e8bba02..c3bdfc8 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -347,7 +347,7 @@ namespace stardew_access helper.ConsoleCommands.Add("buildlist", "List all buildings for selection for upgrading/demolishing/painting", (string commmand, string[] args) => { - if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu) || !BuildingNAnimalMenuPatches.isOnFarm) + if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu && Game1.activeClickableMenu is not AnimalQueryMenu) || !BuildingNAnimalMenuPatches.isOnFarm) { MainClass.DebugLog($"Cannot list buildings."); return; @@ -380,7 +380,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) || !BuildingNAnimalMenuPatches.isOnFarm) + if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu && Game1.activeClickableMenu is not AnimalQueryMenu) || !BuildingNAnimalMenuPatches.isOnFarm) { MainClass.DebugLog($"Cannot select building."); return; @@ -450,7 +450,14 @@ namespace stardew_access string? response = null; - if (Game1.activeClickableMenu is PurchaseAnimalsMenu) { BuildingNAnimalMenuPatches.PurchaseAnimal(BuildingNAnimalMenuPatches.availableBuildings[index]); } + if (Game1.activeClickableMenu is PurchaseAnimalsMenu) + { + BuildingNAnimalMenuPatches.PurchaseOrMoveAnimal(BuildingNAnimalMenuPatches.availableBuildings[index]); + } + else if (Game1.activeClickableMenu is AnimalQueryMenu) + { + BuildingNAnimalMenuPatches.PurchaseOrMoveAnimal(BuildingNAnimalMenuPatches.availableBuildings[index]); + } else { if (BuildingNAnimalMenuPatches.isConstructing && !BuildingNAnimalMenuPatches.isUpgrading) { response = BuildingNAnimalMenuPatches.Contstruct(BuildingNAnimalMenuPatches.marked[index]); } diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index 1250a4a..bb77f1f 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -162,11 +162,6 @@ namespace stardew_access prefix: new HarmonyMethod(typeof(DonationMenuPatches), nameof(DonationMenuPatches.MuseumMenuKeyPressPatch)) ); - harmony.Patch( - original: AccessTools.Method(typeof(AnimalQueryMenu), nameof(AnimalQueryMenu.draw), new Type[] { typeof(SpriteBatch) }), - postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.AnimalQueryMenuPatch)) - ); - harmony.Patch( original: AccessTools.Method(typeof(ChooseFromListMenu), nameof(ChooseFromListMenu.draw), new Type[] { typeof(SpriteBatch) }), postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ChooseFromListMenuPatch)) @@ -240,6 +235,11 @@ namespace stardew_access prefix: new HarmonyMethod(typeof(BuildingNAnimalMenuPatches), nameof(BuildingNAnimalMenuPatches.PurchaseAnimalsMenuPatch)) ); + harmony.Patch( + original: AccessTools.Method(typeof(AnimalQueryMenu), nameof(AnimalQueryMenu.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(BuildingNAnimalMenuPatches), nameof(BuildingNAnimalMenuPatches.AnimalQueryMenuPatch)) + ); + #endregion #region Donation Menus diff --git a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs index 1ac91d5..3112aa7 100644 --- a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs +++ b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs @@ -12,20 +12,100 @@ namespace stardew_access.Patches 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 PurchaseAnimalsMenuPatch(PurchaseAnimalsMenu __instance, bool ___onFarm, bool ___namingAnimal, TextBox ___textBox) + internal static void AnimalQueryMenuPatch(AnimalQueryMenu __instance, bool ___confirmingSell, FarmAnimal ___animal, TextBox ___textBox, string ___parentName, bool ___movingAnimal) + { + try + { + int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position + bool isCPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); // For narrating animal details + bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box + string toSpeak = " ", details = " "; + + isOnFarm = ___movingAnimal; + animalQueryMenu = __instance; + animalBeingPurchasedOrMoved = ___animal; + + if (___textBox.Selected) + { + toSpeak = ___textBox.Text; + + if (isEscPressed) + { + ___textBox.Selected = false; + } + } + else + { + if (isCPressed & !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 { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position purchaseAnimalsMenu = __instance; isOnFarm = ___onFarm; + animalBeingPurchasedOrMoved = ___animalBeingPurchased; if (___onFarm && ___namingAnimal) { @@ -557,17 +637,59 @@ namespace stardew_access.Patches return response; } - public static void PurchaseAnimal(Building? selection) + public static void PurchaseOrMoveAnimal(Building? selection) { if (selection == null) return; - if (purchaseAnimalsMenu == null) + if (purchaseAnimalsMenu == null && animalQueryMenu == null) return; int x = (selection.tileX.Value * Game1.tileSize) - Game1.viewport.X; int y = (selection.tileY.Value * Game1.tileSize) - Game1.viewport.Y; - purchaseAnimalsMenu.receiveLeftClick(x, y); + + if (purchaseAnimalsMenu != null) + { + 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); + } + else if (animalQueryMenu != null) + { + if (animalBeingPurchasedOrMoved != null && !selection.buildingType.Value.Contains(animalBeingPurchasedOrMoved.buildingTypeILiveIn.Value)) + { + string warn = Game1.content.LoadString("Strings\\UI:AnimalQuery_Moving_CantLiveThere", animalBeingPurchasedOrMoved.shortDisplayType()); + MainClass.ScreenReader.Say(warn, true); + return; + } + + if (((AnimalHouse)selection.indoors.Value).isFull()) + { + string warn = Game1.content.LoadString("Strings\\UI:AnimalQuery_Moving_BuildingFull"); + MainClass.ScreenReader.Say(warn, true); + return; + } + if (animalBeingPurchasedOrMoved != null && selection.Equals(animalBeingPurchasedOrMoved.home)) + { + string warn = Game1.content.LoadString("Strings\\UI:AnimalQuery_Moving_AlreadyHome"); + MainClass.ScreenReader.Say(warn, true); + return; + } + + animalQueryMenu.receiveLeftClick(x, y); + } } } } \ No newline at end of file diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 8dc4c92..62a78ab 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -12,8 +12,6 @@ namespace stardew_access.Patches internal static string currentLevelUpTitle = " "; internal static bool firstTimeInNamingMenu = true; internal static bool isNarratingPondInfo = false; - internal static bool isNarratingAnimalInfo = false; - internal static string animalQueryMenuQuery = " "; internal static string tailoringMenuQuery = " "; internal static string pondQueryMenuQuery = " "; internal static string forgeMenuQuery = " "; @@ -350,77 +348,6 @@ namespace stardew_access.Patches } } - internal static void AnimalQueryMenuPatch(AnimalQueryMenu __instance, bool ___confirmingSell, FarmAnimal ___animal, TextBox ___textBox, string ___parentName) - { - try - { - int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isCPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); // For narrating animal details - bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box - string toSpeak = " ", details = " "; - - if (___textBox.Selected) - { - toSpeak = ___textBox.Text; - - if (isEscPressed) - { - ___textBox.Selected = false; - } - } - else - { - if (isCPressed & !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 bool PlaySoundPatch(string cueName) { try From 157bdd0150299602b80a4f1585bcfacf458c83e3 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 6 Jun 2022 22:13:36 +0530 Subject: [PATCH 167/232] Fixed moving animal --- stardew-access/CustomCommands.cs | 4 +- .../Patches/BuildingNAnimalMenuPatches.cs | 77 ++++++++++++------- 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index c3bdfc8..80411c7 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -452,11 +452,11 @@ namespace stardew_access if (Game1.activeClickableMenu is PurchaseAnimalsMenu) { - BuildingNAnimalMenuPatches.PurchaseOrMoveAnimal(BuildingNAnimalMenuPatches.availableBuildings[index]); + BuildingNAnimalMenuPatches.PurchaseAnimal(BuildingNAnimalMenuPatches.availableBuildings[index]); } else if (Game1.activeClickableMenu is AnimalQueryMenu) { - BuildingNAnimalMenuPatches.PurchaseOrMoveAnimal(BuildingNAnimalMenuPatches.availableBuildings[index]); + BuildingNAnimalMenuPatches.MoveAnimal(BuildingNAnimalMenuPatches.availableBuildings[index]); } else { diff --git a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs index 3112aa7..63e51da 100644 --- a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs +++ b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs @@ -637,59 +637,78 @@ namespace stardew_access.Patches return response; } - public static void PurchaseOrMoveAnimal(Building? selection) + public static void PurchaseAnimal(Building? selection) { if (selection == null) return; - if (purchaseAnimalsMenu == null && animalQueryMenu == null) + 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 (purchaseAnimalsMenu != null) + if (animalBeingPurchasedOrMoved != null && !selection.buildingType.Value.Contains(animalBeingPurchasedOrMoved.buildingTypeILiveIn.Value)) { - 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); + string warn = Game1.content.LoadString("Strings\\StringsFromCSFiles:PurchaseAnimalsMenu.cs.11326", animalBeingPurchasedOrMoved.displayType); + MainClass.ScreenReader.Say(warn, true); + return; } - else if (animalQueryMenu != null) - { - if (animalBeingPurchasedOrMoved != null && !selection.buildingType.Value.Contains(animalBeingPurchasedOrMoved.buildingTypeILiveIn.Value)) - { - string warn = Game1.content.LoadString("Strings\\UI:AnimalQuery_Moving_CantLiveThere", animalBeingPurchasedOrMoved.shortDisplayType()); - 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 (animalBeingPurchasedOrMoved != null && selection.Equals(animalBeingPurchasedOrMoved.home)) + if (selection.Equals(animalBeingPurchasedOrMoved.home)) { string warn = Game1.content.LoadString("Strings\\UI:AnimalQuery_Moving_AlreadyHome"); MainClass.ScreenReader.Say(warn, true); return; } - - animalQueryMenu.receiveLeftClick(x, y); + ((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; } } } \ No newline at end of file From 617251c3de1bf3def0b6ac775f5ceec76398367c Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 6 Jun 2022 22:38:51 +0530 Subject: [PATCH 168/232] Patched advanced options menu --- stardew-access/HarmonyPatches.cs | 5 ++ stardew-access/Patches/TitleMenuPatches.cs | 59 ++++++++++++++++++++++ stardew-access/manifest.json | 2 +- 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index bb77f1f..ec74193 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -50,6 +50,11 @@ namespace stardew_access original: AccessTools.Method(typeof(CoopMenu), nameof(CoopMenu.update), new Type[] { typeof(GameTime) }), postfix: new HarmonyMethod(typeof(TitleMenuPatches), nameof(TitleMenuPatches.CoopMenuPatch)) ); + + harmony.Patch( + original: AccessTools.Method(typeof(AdvancedGameOptions), nameof(AdvancedGameOptions.draw), new Type[] { typeof(SpriteBatch) }), + postfix: new HarmonyMethod(typeof(TitleMenuPatches), nameof(TitleMenuPatches.AdvancedGameOptionsPatch)) + ); #endregion #region Game Menu Patches diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index 892cdc7..85b5010 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -11,8 +11,64 @@ namespace stardew_access.Patches private static int saveGameIndex = -1; private static bool isRunning = false; public static string characterCreationMenuQueryKey = " "; + public static string advancedGameOptionsQueryKey = " "; public static string prevPetName = " "; + 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); + 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) { @@ -327,6 +383,9 @@ namespace stardew_access.Patches if (__instance.skipIntroButton != null && __instance.skipIntroButton.visible) buttons.Add(__instance.skipIntroButton, (___skipIntro ? "Enabled" : "Disabled") + " Skip Intro Button"); + if (__instance.advancedOptionsButton != null && __instance.advancedOptionsButton.visible) + buttons.Add(__instance.advancedOptionsButton, "Advanced Options Button"); + if (__instance.okButton != null && __instance.okButton.visible) buttons.Add(__instance.okButton, "OK Button"); diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index 6eb3d30..728ed20 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.2.4", + "Version": "1.2.5", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From 1e096cf521d05f05c55816b1e564fb9d3afdf285 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 6 Jun 2022 22:40:37 +0530 Subject: [PATCH 169/232] Release 1.3.0 --- stardew-access/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index 728ed20..a37d589 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.2.5", + "Version": "1.3.0", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From 5b378592161305d660548dcb6650d5c704904502 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 6 Jun 2022 22:43:25 +0530 Subject: [PATCH 170/232] Fixed advanced game options menu --- stardew-access/Patches/TitleMenuPatches.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index 85b5010..3873cf3 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -20,6 +20,18 @@ namespace stardew_access.Patches { 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)) From 4a1e4f9f4aaca47ec24d55004835e5be07af2340 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Fri, 10 Jun 2022 22:50:06 +0530 Subject: [PATCH 171/232] Bug fix --- stardew-access/Patches/BundleMenuPatches.cs | 3 +-- stardew-access/Patches/MiniGamesPatches.cs | 1 - stardew-access/Patches/QuestPatches.cs | 1 - stardew-access/manifest.json | 2 +- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/stardew-access/Patches/BundleMenuPatches.cs b/stardew-access/Patches/BundleMenuPatches.cs index 2f8aae4..2e34511 100644 --- a/stardew-access/Patches/BundleMenuPatches.cs +++ b/stardew-access/Patches/BundleMenuPatches.cs @@ -97,7 +97,6 @@ namespace stardew_access.Patches if (currentJunimoArea != areaName) { currentJunimoArea = areaName; - MainClass.DebugLog(areaName); MainClass.ScreenReader.Say($"Area {areaName}, {reward}", true); return; } @@ -151,7 +150,7 @@ namespace stardew_access.Patches } else { - bool isIPressed = MainClass.Config.BundleMenuIngredientsInputSlotKey.JustPressed(); // For the ingredients + 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 diff --git a/stardew-access/Patches/MiniGamesPatches.cs b/stardew-access/Patches/MiniGamesPatches.cs index 1020aeb..8addd64 100644 --- a/stardew-access/Patches/MiniGamesPatches.cs +++ b/stardew-access/Patches/MiniGamesPatches.cs @@ -46,7 +46,6 @@ namespace stardew_access.Patches { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position string toSpeak = " "; - MainClass.DebugLog("" + ___scene); if (___letterView != null) { diff --git a/stardew-access/Patches/QuestPatches.cs b/stardew-access/Patches/QuestPatches.cs index f5f9835..015df71 100644 --- a/stardew-access/Patches/QuestPatches.cs +++ b/stardew-access/Patches/QuestPatches.cs @@ -1,5 +1,4 @@ using Microsoft.Xna.Framework.Graphics; -using StardewModdingAPI; using StardewValley; using StardewValley.Menus; using StardewValley.Quests; diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index a37d589..a82c930 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.3.0", + "Version": "1.3.1", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From df192ee90c3319b29d9a9e2a041a84280105de3d Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Fri, 12 Aug 2022 16:37:39 +0530 Subject: [PATCH 172/232] Added docs and replaced escape key with enter --- stardew-access/CustomCommands.cs | 2 +- stardew-access/Features/ReadTile.cs | 3 + stardew-access/Features/TileViewer.cs | 5 +- stardew-access/Features/Warnings.cs | 13 +++++ stardew-access/ModConfig.cs | 57 ++++++++++--------- .../Patches/BuildingNAnimalMenuPatches.cs | 12 ++-- stardew-access/Patches/DonationMenuPatches.cs | 4 +- stardew-access/Patches/MenuPatches.cs | 8 +-- stardew-access/Patches/QuestPatches.cs | 4 +- stardew-access/Patches/TitleMenuPatches.cs | 8 +-- 10 files changed, 65 insertions(+), 51 deletions(-) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index 80411c7..026a193 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -39,6 +39,7 @@ namespace stardew_access MainClass.DebugLog("Flooring is " + (MainClass.Config.ReadFlooring ? "on" : "off")); }); + #region Radar Feature helper.ConsoleCommands.Add("radar", "Toggle radar feature.", (string commmand, string[] args) => { MainClass.Config.Radar = !MainClass.Config.Radar; @@ -47,7 +48,6 @@ namespace stardew_access MainClass.DebugLog("Radar " + (MainClass.Config.Radar ? "on" : "off")); }); - #region Radar Feature helper.ConsoleCommands.Add("rdebug", "Toggle debugging in radar feature.", (string commmand, string[] args) => { MainClass.radarDebug = !MainClass.radarDebug; diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 79fa5ad..7cdb846 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -4,6 +4,9 @@ using StardewValley; namespace stardew_access.Features { + /// + /// Reads the name and information about a tile. + /// public class ReadTile { private bool isBusy; // To pause execution of run method between fixed intervals diff --git a/stardew-access/Features/TileViewer.cs b/stardew-access/Features/TileViewer.cs index ad2d62b..030610e 100644 --- a/stardew-access/Features/TileViewer.cs +++ b/stardew-access/Features/TileViewer.cs @@ -1,10 +1,7 @@ -using System; -using System.Collections.Generic; -using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework; using xTile; using StardewValley; using StardewValley.Menus; -using stardew_access.Features; namespace stardew_access.Features { diff --git a/stardew-access/Features/Warnings.cs b/stardew-access/Features/Warnings.cs index dd683d6..328f78d 100644 --- a/stardew-access/Features/Warnings.cs +++ b/stardew-access/Features/Warnings.cs @@ -1,7 +1,11 @@ namespace stardew_access.Features { + /// + /// Warns the player when their health or stamina/energy is low. Also warns when its past midnight. + /// public class Warnings { + // Store the previously checked value private int prevStamina; private int prevHealth; private int prevHour; @@ -20,6 +24,9 @@ namespace stardew_access.Features this.checkForTimeOfDay(); } + /// + /// Warns when its past 12:00 am and 1:00 am + /// private void checkForTimeOfDay() { if (MainClass.ModHelper == null) @@ -38,6 +45,9 @@ namespace stardew_access.Features prevHour = hours; } + /// + /// Warns when stamina reaches below 50, 25 and 10. + /// public void checkForStamina() { if (MainClass.ModHelper == null) @@ -56,6 +66,9 @@ namespace stardew_access.Features prevStamina = stamina; } + /// + /// Warns when health reaches below 50, 25 and 10. + /// public void checkForHealth() { if (MainClass.ModHelper == null) diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index aac7609..d323e27 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -7,37 +7,37 @@ namespace stardew_access // https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Input#SButton button key codes #region Simulate mouse clicks - public KeybindList LeftClickMainKey { get; set; } = KeybindList.Parse("LeftControl + Enter"); - public KeybindList RightClickMainKey { get; set; } = KeybindList.Parse("LeftShift + Enter"); - public KeybindList LeftClickAlternateKey { get; set; } = KeybindList.Parse("OemOpenBrackets"); - public KeybindList RightClickAlternateKey { get; set; } = KeybindList.Parse("OemCloseBrackets"); + public KeybindList LeftClickMainKey { get; set; } = KeybindList.Parse("LeftControl + Enter"); // Primary key to simulate mouse left click + public KeybindList RightClickMainKey { get; set; } = KeybindList.Parse("LeftShift + Enter"); // Primary key to simulate mouse right click + public KeybindList LeftClickAlternateKey { get; set; } = KeybindList.Parse("OemOpenBrackets"); // Secondary key to simulate mouse left click + public KeybindList RightClickAlternateKey { get; set; } = KeybindList.Parse("OemCloseBrackets"); // Secondary key to simulate mouse right click #endregion #region Chat menu - public KeybindList ChatMenuNextKey { get; set; } = KeybindList.Parse("PageUp"); - public KeybindList ChatMenuPreviousKey { get; set; } = KeybindList.Parse("PageDown"); + public KeybindList ChatMenuNextKey { get; set; } = KeybindList.Parse("PageUp"); // Read previous chat message + public KeybindList ChatMenuPreviousKey { get; set; } = KeybindList.Parse("PageDown"); // Read next chat message #endregion #region Read tile - public Boolean ReadTile { get; set; } = true; - public KeybindList ReadTileKey { get; set; } = KeybindList.Parse("J"); - public KeybindList ReadStandingTileKey { get; set; } = KeybindList.Parse("LeftAlt + J"); - public Boolean ReadFlooring { get; set; } = false; + public Boolean ReadTile { get; set; } = true; // Toggle this feature. + public KeybindList ReadTileKey { get; set; } = KeybindList.Parse("J"); // Manually trigger read tile for the tile player is *looking at*. + public KeybindList ReadStandingTileKey { get; set; } = KeybindList.Parse("LeftAlt + J"); // Manually trigger read tile for the tile player is *standing on*. + public Boolean ReadFlooring { get; set; } = false; // Toggle reading floorings. #endregion #region Tile viewer - public KeybindList TileCursorUpKey { get; set; } = KeybindList.Parse("Up"); - public KeybindList TileCursorRightKey { get; set; } = KeybindList.Parse("Right"); - public KeybindList TileCursorDownKey { get; set; } = KeybindList.Parse("Down"); - public KeybindList TileCursorLeftKey { get; set; } = KeybindList.Parse("Left"); - public KeybindList TileCursorPreciseUpKey { get; set; } = KeybindList.Parse("LeftShift + Up"); - public KeybindList TileCursorPreciseRightKey { get; set; } = KeybindList.Parse("LeftShift + Right"); - public KeybindList TileCursorPreciseDownKey { get; set; } = KeybindList.Parse("LeftShift + Down"); - public KeybindList TileCursorPreciseLeftKey { get; set; } = KeybindList.Parse("LeftShift + Left"); - public KeybindList ToggleRelativeCursorLockKey { get; set; } = KeybindList.Parse("L"); - public KeybindList AutoWalkToTileKey { get; set; } = KeybindList.Parse("LeftControl + Enter"); - public bool LimitTileCursorToScreen { get; set; } = false; - public int TileCursorPreciseMovementDistance { get; set; } = 8; + public KeybindList TileCursorUpKey { get; set; } = KeybindList.Parse("Up"); // Move the cursor one tile up + public KeybindList TileCursorRightKey { get; set; } = KeybindList.Parse("Right"); // Move the cursor one tile right + public KeybindList TileCursorDownKey { get; set; } = KeybindList.Parse("Down"); // Move the cursor one tile down + public KeybindList TileCursorLeftKey { get; set; } = KeybindList.Parse("Left"); // Move the cursor one tile left + public KeybindList TileCursorPreciseUpKey { get; set; } = KeybindList.Parse("LeftShift + Up"); // Move the cursor up by precision i.e. pixel by pixel + public KeybindList TileCursorPreciseRightKey { get; set; } = KeybindList.Parse("LeftShift + Right"); // Move the cursor right by precision i.e. pixel by pixel + public KeybindList TileCursorPreciseDownKey { get; set; } = KeybindList.Parse("LeftShift + Down"); // Move the cursor down by precision i.e. pixel by pixel + public KeybindList TileCursorPreciseLeftKey { get; set; } = KeybindList.Parse("LeftShift + Left"); // Move the cursor left by precision i.e. pixel by pixel + public KeybindList ToggleRelativeCursorLockKey { get; set; } = KeybindList.Parse("L"); // Toggles realative cursor lock i.e. if enabled, the cursor will reset when player moves. + public KeybindList AutoWalkToTileKey { get; set; } = KeybindList.Parse("LeftControl + Enter"); // Auto walk to the tile + public bool LimitTileCursorToScreen { get; set; } = false; // #TODO add command for this // Toggle whether to prevent cursor from going out of screen. + public int TileCursorPreciseMovementDistance { get; set; } = 8; // Specifies the number of pixels the cursor should move when using precision movement i.e. with *left shift*. #endregion #region Radar @@ -68,13 +68,14 @@ namespace stardew_access #endregion #region Others - public KeybindList HealthNStaminaKey { get; set; } = KeybindList.Parse("H"); - public KeybindList PositionKey { get; set; } = KeybindList.Parse("K"); - public KeybindList LocationKey { get; set; } = KeybindList.Parse("LeftAlt + K"); - public KeybindList MoneyKey { get; set; } = KeybindList.Parse("R"); - public KeybindList TimeNSeasonKey { get; set; } = KeybindList.Parse("Q"); + public KeybindList HealthNStaminaKey { get; set; } = KeybindList.Parse("H"); // Narrate health and stamina. + public KeybindList PositionKey { get; set; } = KeybindList.Parse("K"); // Narrate player position. + public KeybindList LocationKey { get; set; } = KeybindList.Parse("LeftAlt + K"); // Narrate current location name. + public KeybindList MoneyKey { get; set; } = KeybindList.Parse("R"); // Narrate the money the player has currently. + public KeybindList TimeNSeasonKey { get; set; } = KeybindList.Parse("Q"); // Narrate the time of day, day and date and season public Boolean VerboseCoordinates { get; set; } = true; - public Boolean SnapMouse { get; set; } = true; + public Boolean SnapMouse { get; set; } = true; // Toggles the snap mouse feature + // TODO add command to toggle warning feature #endregion // TODO Add the exclusion and focus list too diff --git a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs index 63e51da..2b6d929 100644 --- a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs +++ b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs @@ -28,8 +28,8 @@ namespace stardew_access.Patches try { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isCPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); // For narrating animal details - bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box + bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); // For narrating animal details + bool isEnterPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Enter); // For escaping/unselecting from the animal name text box string toSpeak = " ", details = " "; isOnFarm = ___movingAnimal; @@ -40,14 +40,14 @@ namespace stardew_access.Patches { toSpeak = ___textBox.Text; - if (isEscPressed) + if (isEnterPressed) { ___textBox.Selected = false; } } else { - if (isCPressed & !isNarratingAnimalInfo) + if (isPrimaryInfoKeyPressed & !isNarratingAnimalInfo) { string name = ___animal.displayName; string type = ___animal.displayType; @@ -205,7 +205,7 @@ namespace stardew_access.Patches return; int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isCPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); + bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); string ingredients = ""; string name = currentBluprint.displayName; string upgradeName = currentBluprint.nameOfBuildingToUpgrade; @@ -242,7 +242,7 @@ namespace stardew_access.Patches blueprintInfo = $"{name}, Price: {price}, Ingredients: {ingredients}, Dimensions: {width} width and {height} height, Description: {description}"; - if (isCPressed && !isSayingBlueprintInfo) + if (isPrimaryInfoKeyPressed && !isSayingBlueprintInfo) { SayBlueprintInfo(blueprintInfo); } diff --git a/stardew-access/Patches/DonationMenuPatches.cs b/stardew-access/Patches/DonationMenuPatches.cs index 8f4747b..159bc25 100644 --- a/stardew-access/Patches/DonationMenuPatches.cs +++ b/stardew-access/Patches/DonationMenuPatches.cs @@ -82,9 +82,9 @@ namespace stardew_access.Patches int i = narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y); if (i != -9999) { - bool isCPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); // For donating hovered item + bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); // For donating hovered item - if (isCPressed && __instance.inventory.actualInventory[i] != null) + if (isPrimaryInfoKeyPressed && __instance.inventory.actualInventory[i] != null) { foreach (var tile in donationTiles) { diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 62a78ab..5bd970b 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -169,7 +169,7 @@ namespace stardew_access.Patches try { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isCPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); + bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); string toSpeak = " ", extra = ""; if (___confirmingEmpty) @@ -181,7 +181,7 @@ namespace stardew_access.Patches } else { - if (isCPressed && !isNarratingPondInfo) + 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); @@ -458,7 +458,7 @@ namespace stardew_access.Patches { 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 + bool isEnterPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Enter); // For escaping/unselecting from the animal name text box if (firstTimeInNamingMenu) { @@ -471,7 +471,7 @@ namespace stardew_access.Patches ___textBox.Update(); toSpeak = ___textBox.Text; - if (isEscPressed) + if (isEnterPressed) { ___textBox.Selected = false; } diff --git a/stardew-access/Patches/QuestPatches.cs b/stardew-access/Patches/QuestPatches.cs index 015df71..0b38787 100644 --- a/stardew-access/Patches/QuestPatches.cs +++ b/stardew-access/Patches/QuestPatches.cs @@ -153,7 +153,7 @@ namespace stardew_access.Patches { try { - bool isCPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); + bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position string toSpeak = " ", extra = ""; @@ -203,7 +203,7 @@ namespace stardew_access.Patches string description = Game1.parseText(____shownQuest.GetDescription(), Game1.dialogueFont, __instance.width - 128); string title = ____shownQuest.GetName(); - if (firstTimeInIndividualQuest || (isCPressed && !isNarratingQuestInfo)) + if (firstTimeInIndividualQuest || (isPrimaryInfoKeyPressed && !isNarratingQuestInfo)) { if (firstTimeInIndividualQuest) toSpeak = "Back button"; diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index 3873cf3..39f3387 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -247,7 +247,7 @@ namespace stardew_access.Patches { try { - bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box + bool isEnterPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Enter); // For escaping/unselecting from the animal name text box string toSpeak = " "; string currentPetName = getCurrentPetName(); @@ -255,7 +255,7 @@ namespace stardew_access.Patches { toSpeak = ___nameBox.Text; - if (isEscPressed) + if (isEnterPressed) { ___nameBox.Selected = false; } @@ -264,7 +264,7 @@ namespace stardew_access.Patches { toSpeak = ___farmnameBox.Text; - if (isEscPressed) + if (isEnterPressed) { ___farmnameBox.Selected = false; } @@ -273,7 +273,7 @@ namespace stardew_access.Patches { toSpeak = ___favThingBox.Text; - if (isEscPressed) + if (isEnterPressed) { ___favThingBox.Selected = false; } From 9199d764ba46a09d00936902b54de4f5ec76acd5 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 13 Aug 2022 12:00:45 +0530 Subject: [PATCH 173/232] Issue#23 Added indoor pots i.e. garden pot to read tile --- stardew-access/Features/TileInfo.cs | 106 ++++++++++++++++------------ 1 file changed, 62 insertions(+), 44 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 3e1605b..322e691 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -470,52 +470,10 @@ namespace stardew_access.Features string? toReturn = null; CATEGORY category = CATEGORY.Others; - if (terrain.Get() is HoeDirt) + if (terrain.Get() is HoeDirt dirt) { + toReturn = getHoeDirtDetail(dirt); category = CATEGORY.Crops; - HoeDirt dirt = (HoeDirt)terrain.Get(); - if (dirt.crop != null && !dirt.crop.forageCrop.Value) - { - string cropName = Game1.objectInformation[dirt.crop.indexOfHarvest.Value].Split('/')[0]; - toReturn = $"{cropName}"; - - bool isWatered = dirt.state.Value == HoeDirt.watered; - bool isHarvestable = dirt.readyForHarvest(); - bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer; - - if (isWatered) - toReturn = "Watered " + toReturn; - - if (isFertilized) - toReturn = "Fertilized " + toReturn; - - if (isHarvestable) - toReturn = "Harvestable " + toReturn; - - if (dirt.crop.dead.Value) - toReturn = "Dead " + toReturn; - } - else if (dirt.crop != null && dirt.crop.forageCrop.Value) - { - toReturn = dirt.crop.whichForageCrop.Value switch - { - 1 => "Spring onion", - 2 => "Ginger", - _ => "Forageable crop" - }; - } - else - { - toReturn = "Soil"; - bool isWatered = dirt.state.Value == HoeDirt.watered; - bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer; - - if (isWatered) - toReturn = "Watered " + toReturn; - - if (isFertilized) - toReturn = "Fertilized " + toReturn; - } } else if (terrain.Get() is CosmeticPlant) { @@ -561,6 +519,62 @@ namespace stardew_access.Features } return (toReturn, category); + + + } + + /// + /// Returns the detail about the HoeDirt i.e. soil, plant, etc. + /// + /// The HoeDirt to be checked + /// The details about the given HoeDirt + public static string getHoeDirtDetail(HoeDirt dirt) + { + string detail; + + if (dirt.crop != null && !dirt.crop.forageCrop.Value) + { + string cropName = Game1.objectInformation[dirt.crop.indexOfHarvest.Value].Split('/')[0]; + detail = $"{cropName}"; + + bool isWatered = dirt.state.Value == HoeDirt.watered; + bool isHarvestable = dirt.readyForHarvest(); + bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer; + + if (isWatered) + detail = "Watered " + detail; + + if (isFertilized) + detail = "Fertilized " + detail; + + if (isHarvestable) + detail = "Harvestable " + detail; + + if (dirt.crop.dead.Value) + detail = "Dead " + detail; + } + else if (dirt.crop != null && dirt.crop.forageCrop.Value) + { + detail = dirt.crop.whichForageCrop.Value switch + { + 1 => "Spring onion", + 2 => "Ginger", + _ => "Forageable crop" + }; + } + else + { + detail = "Soil"; + bool isWatered = dirt.state.Value == HoeDirt.watered; + bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer; + + if (isWatered) + detail = "Watered " + detail; + + if (isFertilized) + detail = "Fertilized " + detail; + } + return detail; } public static string getFruitTree(FruitTree fruitTree) @@ -677,6 +691,10 @@ namespace stardew_access.Features Chest chest = (Chest)obj; toReturn = (chest.DisplayName, CATEGORY.Chests); } + else if (obj is IndoorPot indoorPot) + { + toReturn.name = $"{obj.DisplayName} {getHoeDirtDetail(indoorPot.hoeDirt)}"; + } else if (obj is Furniture) { if (lessInfo && (((Furniture)obj).TileLocation.X != x || ((Furniture)obj).TileLocation.Y != y)) From 949e5c5bfc3314f6cfef8e3a369fc50b6637e504 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 13 Aug 2022 12:15:38 +0530 Subject: [PATCH 174/232] Issue#23 Added Signs to read tile --- stardew-access/Features/TileInfo.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 322e691..240572c 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -693,11 +693,16 @@ namespace stardew_access.Features } else if (obj is IndoorPot indoorPot) { - toReturn.name = $"{obj.DisplayName} {getHoeDirtDetail(indoorPot.hoeDirt)}"; + toReturn.name = $"{obj.DisplayName}, {getHoeDirtDetail(indoorPot.hoeDirt)}"; } - else if (obj is Furniture) + else if (obj is Sign sign) { - if (lessInfo && (((Furniture)obj).TileLocation.X != x || ((Furniture)obj).TileLocation.Y != y)) + if (sign.displayItem.Value != null) + toReturn.name = $"{obj.DisplayName}, {sign.displayItem.Value.DisplayName}"; + } + else if (obj is Furniture furniture) + { + if (lessInfo && (furniture.TileLocation.X != x || furniture.TileLocation.Y != y)) { toReturn.category = CATEGORY.Others; toReturn.name = null; @@ -727,6 +732,7 @@ namespace stardew_access.Features else if (machineState == MachineState.Busy) toReturn.name = $"Busy {toReturn.name}"; } + return toReturn; } From 408a40121d428ac82e212237378e1c3a90cc4d6e Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 13 Aug 2022 13:06:24 +0530 Subject: [PATCH 175/232] Issue#25 Added command to toggle speaking in percentage for health n stamina --- stardew-access/CustomCommands.cs | 8 +++++ stardew-access/Features/CurrentPlayer.cs | 39 ++++++++++++++++++------ stardew-access/Features/Warnings.cs | 4 +-- stardew-access/ModConfig.cs | 1 + stardew-access/ModEntry.cs | 10 +++++- stardew-access/i18n/de.json | 4 ++- stardew-access/i18n/default.json | 4 ++- stardew-access/i18n/es.json | 4 ++- stardew-access/i18n/fr.json | 4 ++- stardew-access/i18n/hu.json | 4 ++- stardew-access/i18n/it.json | 4 ++- stardew-access/i18n/ja.json | 4 ++- stardew-access/i18n/ko.json | 4 ++- stardew-access/i18n/pt.json | 4 ++- stardew-access/i18n/ru.json | 4 ++- stardew-access/i18n/tr.json | 4 ++- stardew-access/i18n/zh.json | 4 ++- 17 files changed, 85 insertions(+), 25 deletions(-) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index 026a193..9f28d13 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -494,6 +494,14 @@ namespace stardew_access MainClass.DebugLog("Static tiles refreshed!"); }); + + helper.ConsoleCommands.Add("hnspercent", "Toggle between speaking in percentage or full health and stamina.", (string commmand, string[] args) => + { + MainClass.Config.HealthNStaminaInPercentage = !MainClass.Config.HealthNStaminaInPercentage; + helper.WriteConfig(MainClass.Config); + + MainClass.DebugLog("Speaking in percentage is " + (MainClass.Config.HealthNStaminaInPercentage ? "on" : "off")); + }); } } } diff --git a/stardew-access/Features/CurrentPlayer.cs b/stardew-access/Features/CurrentPlayer.cs index 5331576..9424ea9 100644 --- a/stardew-access/Features/CurrentPlayer.cs +++ b/stardew-access/Features/CurrentPlayer.cs @@ -9,37 +9,56 @@ namespace stardew_access.Features /// /// Returns the percentage health remaining of player. /// - public static int Health + public static int PercentHealth { get { if (Game1.player == null) return 0; - int maxHealth = Game1.player.maxHealth; - int currentHealth = Game1.player.health; + return (CurrentHealth * 100) / Game1.player.maxHealth; ; + } + } - int healthPercentage = (int)(currentHealth * 100) / maxHealth; - return healthPercentage; + /// + /// Returns the total health player has currently + /// + public static int CurrentHealth + { + get + { + if (Game1.player == null) + return 0; + + return Game1.player.health; } } /// /// Returns the percentage stamine/energy remaining of player. /// - public static int Stamina + public static int PercentStamina { get { if (Game1.player == null) return 0; - int maxStamina = Game1.player.maxStamina.Value; - int currentStamine = (int)Game1.player.stamina; + return (CurrentStamina * 100) / Game1.player.maxStamina.Value; + } + } - int staminaPercentage = (int)(currentStamine * 100) / maxStamina; + /// + /// Returns the total stamina player has currently + /// + public static int CurrentStamina + { + get + { + if (Game1.player == null) + return 0; - return staminaPercentage; + return (int)Game1.player.stamina; } } diff --git a/stardew-access/Features/Warnings.cs b/stardew-access/Features/Warnings.cs index 328f78d..a43f151 100644 --- a/stardew-access/Features/Warnings.cs +++ b/stardew-access/Features/Warnings.cs @@ -53,7 +53,7 @@ namespace stardew_access.Features if (MainClass.ModHelper == null) return; - int stamina = CurrentPlayer.Stamina; + int stamina = CurrentPlayer.PercentStamina; string toSpeak = MainClass.ModHelper.Translation.Get("warnings.stamina", new { value = stamina }); if ((stamina <= 50 && prevStamina > 50) || (stamina <= 25 && prevStamina > 25) || (stamina <= 10 && prevStamina > 10)) @@ -74,7 +74,7 @@ namespace stardew_access.Features if (MainClass.ModHelper == null) return; - int health = CurrentPlayer.Health; + int health = CurrentPlayer.PercentHealth; string toSpeak = MainClass.ModHelper.Translation.Get("warnings.health", new { value = health }); if ((health <= 50 && prevHealth > 50) || (health <= 25 && prevHealth > 25) || (health <= 10 && prevHealth > 10)) diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index d323e27..c77e9df 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -69,6 +69,7 @@ namespace stardew_access #region Others public KeybindList HealthNStaminaKey { get; set; } = KeybindList.Parse("H"); // Narrate health and stamina. + public bool HealthNStaminaInPercentage { get; set; } = true; public KeybindList PositionKey { get; set; } = KeybindList.Parse("K"); // Narrate player position. public KeybindList LocationKey { get; set; } = KeybindList.Parse("LeftAlt + K"); // Narrate current location name. public KeybindList MoneyKey { get; set; } = KeybindList.Parse("R"); // Narrate the money the player has currently. diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index f564ed6..f9de5d3 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -289,7 +289,15 @@ namespace stardew_access // Narrate health and stamina if (Config.HealthNStaminaKey.JustPressed()) { - string toSpeak = $"Health is {CurrentPlayer.Health} and Stamina is {CurrentPlayer.Stamina}"; + if (ModHelper == null) + return; + + string toSpeak; + if (Config.HealthNStaminaInPercentage) + toSpeak = ModHelper.Translation.Get("manuallytriggered.healthnstamina.percent", new { health = CurrentPlayer.PercentHealth, stamina = CurrentPlayer.PercentStamina }); + else + toSpeak = ModHelper.Translation.Get("manuallytriggered.healthnstamina.normal", new { health = CurrentPlayer.CurrentHealth, stamina = CurrentPlayer.CurrentStamina }); + MainClass.ScreenReader.Say(toSpeak, true); return; } diff --git a/stardew-access/i18n/de.json b/stardew-access/i18n/de.json index 8c66154..6887ac2 100644 --- a/stardew-access/i18n/de.json +++ b/stardew-access/i18n/de.json @@ -8,5 +8,7 @@ "grandpastory.scene6":"Du erreichst deinen Schreibtisch und findest Opas Brief.", "grandpastory.letteropen":"Linksklick, um Opas Brief zu öffnen", "intro.scene3":"Fahrt zur Bushaltestelle Stardew Valley", - "intro.scene4":"Stardew Valley 0.5 Meilen entfernt" + "intro.scene4":"Stardew Valley 0.5 Meilen entfernt", + "manuallytriggered.healthnstamina.percent":"Gesundheit ist {{health}} % und Ausdauer ist {{stamina}} %", + "manuallytriggered.healthnstamina.normal":"Gesundheit ist {{health}} und Ausdauer ist {{stamina}}" } \ No newline at end of file diff --git a/stardew-access/i18n/default.json b/stardew-access/i18n/default.json index cf1b780..6eb682c 100644 --- a/stardew-access/i18n/default.json +++ b/stardew-access/i18n/default.json @@ -8,5 +8,7 @@ "grandpastory.scene6":"You reach your desk finding grandpa's letter.", "grandpastory.letteropen":"Left click to open grandpa's letter", "intro.scene3":"Travelling to Stardew Valley bus stop", - "intro.scene4":"Stardew valley 0.5 miles away" + "intro.scene4":"Stardew valley 0.5 miles away", + "manuallytriggered.healthnstamina.percent":"Health is {{health}} % and Stamina is {{stamina}} %", + "manuallytriggered.healthnstamina.normal":"Health is {{health}} and Stamina is {{stamina}}" } \ No newline at end of file diff --git a/stardew-access/i18n/es.json b/stardew-access/i18n/es.json index 2c0a7fb..9c4b92f 100644 --- a/stardew-access/i18n/es.json +++ b/stardew-access/i18n/es.json @@ -8,5 +8,7 @@ "grandpastory.scene6":"Llegas a tu escritorio y encuentras la carta del abuelo.", "grandpastory.letteropen":"Haz clic izquierdo para abrir la carta del abuelo.", "intro.scene3":"Viajando a la parada de autobús de Stardew Valley", - "intro.scene4":"Valle de Stardew a 0.5 millas de distancia" + "intro.scene4":"Valle de Stardew a 0.5 millas de distancia", + "manuallytriggered.healthnstamina.percent":"La salud es {{health}} % y la resistencia es {{stamina}} %", + "manuallytriggered.healthnstamina.normal":"La salud es {{health}} y la resistencia es {{stamina}}" } \ No newline at end of file diff --git a/stardew-access/i18n/fr.json b/stardew-access/i18n/fr.json index ad36680..4bdac41 100644 --- a/stardew-access/i18n/fr.json +++ b/stardew-access/i18n/fr.json @@ -8,5 +8,7 @@ "grandpastory.scene6":"Vous atteignez votre bureau en trouvant la lettre de grand-père.", "grandpastory.letteropen":"Clic gauche pour ouvrir la lettre de grand-père", "intro.scene3":"Se rendre à l'arrêt de bus Stardew Valley", - "intro.scene4":"Vallée de Stardew à 0.5 miles" + "intro.scene4":"Vallée de Stardew à 0.5 miles", + "manuallytriggered.healthnstamina.percent":"La santé est de {{health}} % et l'endurance est de {{stamina}} %", + "manuallytriggered.healthnstamina.normal":"La santé est {{health}} et l'endurance est {{stamina}}" } \ No newline at end of file diff --git a/stardew-access/i18n/hu.json b/stardew-access/i18n/hu.json index 8421e95..4873aa1 100644 --- a/stardew-access/i18n/hu.json +++ b/stardew-access/i18n/hu.json @@ -8,5 +8,7 @@ "grandpastory.scene6":"Az asztalodhoz érve megtalálod a nagypapa levelét.", "grandpastory.letteropen":"Kattintson a bal gombbal a nagypapa levelének megnyitásához", "intro.scene3":"Utazás a Stardew Valley buszmegállóhoz", - "intro.scene4":"Stardew-völgy 0.5 mérföldre van" + "intro.scene4":"Stardew-völgy 0.5 mérföldre van", + "manuallytriggered.healthnstamina.percent":"Az egészségi állapot {{health}} %, az állóképesség pedig {{stamina}} %", + "manuallytriggered.healthnstamina.normal":"Az egészség {{health}}, az állóképesség pedig {{stamina}}" } \ No newline at end of file diff --git a/stardew-access/i18n/it.json b/stardew-access/i18n/it.json index 1b54896..46f6f7d 100644 --- a/stardew-access/i18n/it.json +++ b/stardew-access/i18n/it.json @@ -8,5 +8,7 @@ "grandpastory.scene6":"Raggiungi la tua scrivania e trovi la lettera del nonno.", "grandpastory.letteropen":"Fare clic con il tasto sinistro per aprire la lettera del nonno", "intro.scene3":"In viaggio verso la fermata dell'autobus di Stardew Valley", - "intro.scene4":"Stardew Valley 0.5 miglia di distanza" + "intro.scene4":"Stardew Valley 0.5 miglia di distanza", + "manuallytriggered.healthnstamina.percent":"La salute è {{health}} % e la resistenza è {{stamina}} %", + "manuallytriggered.healthnstamina.normal":"La salute è {{health}} e la resistenza è {{stamina}}" } \ No newline at end of file diff --git a/stardew-access/i18n/ja.json b/stardew-access/i18n/ja.json index c667345..4a8fcee 100644 --- a/stardew-access/i18n/ja.json +++ b/stardew-access/i18n/ja.json @@ -8,5 +8,7 @@ "grandpastory.scene6":"おじいちゃんの手紙を見つけて机に着きます。", "grandpastory.letteropen":"左クリックしておじいちゃんの手紙を開く", "intro.scene3":"スターデューバレーバス停への移動", - "intro.scene4":"0.5マイル離れたスターデューバレー" + "intro.scene4":"0.5マイル離れたスターデューバレー", + "manuallytriggered.healthnstamina.percent":"体力は {{health}} %、スタミナは {{stamina}} %", + "manuallytriggered.healthnstamina.normal":"体力は{{health}}、スタミナは{{stamina}}です" } \ No newline at end of file diff --git a/stardew-access/i18n/ko.json b/stardew-access/i18n/ko.json index 2261eb5..fc6c953 100644 --- a/stardew-access/i18n/ko.json +++ b/stardew-access/i18n/ko.json @@ -8,5 +8,7 @@ "grandpastory.scene6":"책상에 다가가 할아버지의 편지를 찾습니다.", "grandpastory.letteropen":"할아버지의 편지를 열려면 왼쪽 클릭", "intro.scene3":"스타듀밸리 버스정류장으로 이동", - "intro.scene4":"스타듀 밸리에서 0.8km 떨어짐" + "intro.scene4":"스타듀 밸리에서 0.8km 떨어짐", + "manuallytriggered.healthnstamina.percent":"체력은 {{health}} %이고 체력은 {{stamina}} %입니다.", + "manuallytriggered.healthnstamina.normal":"체력은 {{health}}이고 체력은 {{stamina}}입니다." } \ No newline at end of file diff --git a/stardew-access/i18n/pt.json b/stardew-access/i18n/pt.json index edd1ed5..737798c 100644 --- a/stardew-access/i18n/pt.json +++ b/stardew-access/i18n/pt.json @@ -8,5 +8,7 @@ "grandpastory.scene6":"Você chega à sua mesa encontrando a carta do vovô.", "grandpastory.letteropen":"Clique com o botão esquerdo para abrir a carta do vovô", "intro.scene3":"Viajar para o ponto de ônibus Stardew Valley", - "intro.scene4":"Vale Stardew a 0.5 km de distância" + "intro.scene4":"Vale Stardew a 0.5 km de distância", + "manuallytriggered.healthnstamina.percent":"Saúde é {{health}} % e Stamina é {{stamina}} %", + "manuallytriggered.healthnstamina.normal":"Saúde é {{health}} e Stamina é {{stamina}}" } \ No newline at end of file diff --git a/stardew-access/i18n/ru.json b/stardew-access/i18n/ru.json index 61b2023..149503f 100644 --- a/stardew-access/i18n/ru.json +++ b/stardew-access/i18n/ru.json @@ -8,5 +8,7 @@ "grandpastory.scene6":"Вы подходите к своему столу и находите дедушкино письмо.", "grandpastory.letteropen":"Щелкните левой кнопкой мыши, чтобы открыть письмо дедушки", "intro.scene3":"Поездка на автобусную остановку Stardew Valley", - "intro.scene4":"Долина Стардью: 0.8 км" + "intro.scene4":"Долина Стардью: 0.8 км", + "manuallytriggered.healthnstamina.percent":"Здоровье составляет {{health}}%, а выносливость - {{stamina}}%", + "manuallytriggered.healthnstamina.normal":"Здоровье – {{health}}, а выносливость – {{stamina}}." } \ No newline at end of file diff --git a/stardew-access/i18n/tr.json b/stardew-access/i18n/tr.json index ebcdb31..db02ffd 100644 --- a/stardew-access/i18n/tr.json +++ b/stardew-access/i18n/tr.json @@ -8,5 +8,7 @@ "grandpastory.scene6":"Dedenizin mektubunu bulmak için masanıza ulaşıyorsunuz.", "grandpastory.letteropen":"Büyükbabanın mektubunu açmak için sol tıklayın", "intro.scene3":"Stardew Valley otobüs durağına seyahat", - "intro.scene4":"Stardew vadisi 0.5 mil uzakta" + "intro.scene4":"Stardew vadisi 0.5 mil uzakta", + "manuallytriggered.healthnstamina.percent":"Sağlık %{{health}} ve Dayanıklılık %{{stamina}}", + "manuallytriggered.healthnstamina.normal":"Sağlık {{health}} ve Dayanıklılık {{stamina}}" } \ No newline at end of file diff --git a/stardew-access/i18n/zh.json b/stardew-access/i18n/zh.json index 49037b9..0d42090 100644 --- a/stardew-access/i18n/zh.json +++ b/stardew-access/i18n/zh.json @@ -8,5 +8,7 @@ "grandpastory.scene6":"你走到办公桌前,找到了爷爷的信。", "grandpastory.letteropen":"左方括号打开爷爷的信", "intro.scene3":"前往星露谷物语巴士站", - "intro.scene4":"星露谷物语 0.5 英里外" + "intro.scene4":"星露谷物语 0.5 英里外", + "manuallytriggered.healthnstamina.percent":"健康为 {{health}} %,耐力为 {{stamina}} %", + "manuallytriggered.healthnstamina.normal":"健康为 {{health}},耐力为 {{stamina}}" } From 18a7dc5d23d8cd324010c279faf25ae531a7aba9 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 13 Aug 2022 14:21:22 +0530 Subject: [PATCH 176/232] Issue#33 Fixed rings bug in crafting page --- stardew-access/Patches/GameMenuPatches.cs | 59 ++++++++++++----------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index 228dded..97ce43a 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -773,43 +773,44 @@ namespace stardew_access.Patches #region Health & stamina and buff items (effects like +1 walking speed) Item producesItem = ___hoverRecipe.createItem(); - StardewValley.Object? producesItemObject = ((StardewValley.Object)producesItem); - - if (producesItem is StardewValley.Object && producesItemObject.Edibility != -300) + if (producesItem is StardewValley.Object producesItemObject) { - int stamina_recovery = producesItemObject.staminaRecoveredOnConsumption(); - buffs += $"{stamina_recovery} Energy"; - if (stamina_recovery >= 0) + if (producesItemObject.Edibility != -300) { - int health_recovery = producesItemObject.healthRecoveredOnConsumption(); - buffs += $"\n{health_recovery} Health"; + 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 && producesItem is StardewValley.Object && (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; + // 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++) + if (buffIconsToDisplay != null) { - string buffName = ((Convert.ToInt32(buffIconsToDisplay[j]) > 0) ? "+" : "") + buffIconsToDisplay[j] + " "; - if (j <= 11) + for (int j = 0; j < buffIconsToDisplay.Length; j++) { - buffName = Game1.content.LoadString("Strings\\UI:ItemHover_Buff" + j, buffName); + 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) { } } - try - { - int count = int.Parse(buffName.Substring(0, buffName.IndexOf(' '))); - if (count != 0) - buffs += $"{buffName}\n"; - } - catch (Exception) { } - } - buffs = $"Buffs and boosts:\n {buffs}"; + buffs = $"Buffs and boosts:\n {buffs}"; + } } #endregion From f2d607484e36e68adc8fb00c9aa31e2a42fc7c0f Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sun, 14 Aug 2022 19:50:53 +0530 Subject: [PATCH 177/232] Issue#27 Added mill input and output tiles detection --- stardew-access/Features/TileInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 240572c..1d7a033 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -321,6 +321,10 @@ namespace stardew_access.Features return (CATEGORY.Doors, name + " Animal Door " + ((building.animalDoorOpen.Value) ? "Opened" : "Closed")); else if (building.tileX.Value == x && building.tileY.Value == y) return (CATEGORY.Buildings, name); + else if (building is Mill && (building.tileX.Value + 1) == x && (building.tileY.Value + 1) == y) + return (CATEGORY.Buildings, name + " input"); + else if (building is Mill && (building.tileX.Value + 3) == x && (building.tileY.Value + 1) == y) + return (CATEGORY.Buildings, name + " output"); else if (!lessInfo) return (CATEGORY.Buildings, name); } From b4625ddfc8701c3ba49f27225afd354a3913938e Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sun, 14 Aug 2022 20:09:09 +0530 Subject: [PATCH 178/232] Issue#29 Fixed crab pot not getting detected as a machine --- stardew-access/Features/TileInfo.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 240572c..64df1c7 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -9,7 +9,7 @@ namespace stardew_access.Features { public class TileInfo { - public static string[] trackable_machines = { "bee house", "cask", "press", "keg", "machine", "maker", "preserves jar", "bone mill", "kiln", "crystalarium", "furnace", "geode crusher", "tapper", "lightning rod", "incubator", "wood chipper", "worm bin", "loom", "statue of endless fortune", "statue of perfection" }; + public static string[] trackable_machines = { "bee house", "cask", "press", "keg", "machine", "maker", "preserves jar", "bone mill", "kiln", "crystalarium", "furnace", "geode crusher", "tapper", "lightning rod", "incubator", "wood chipper", "worm bin", "loom", "statue of endless fortune", "statue of perfection", "crab pot" }; ///Returns the name of the object at tile alongwith it's category's name public static (string? name, string? categoryName) getNameWithCategoryNameAtTile(Vector2 tile) @@ -80,17 +80,17 @@ namespace stardew_access.Features toReturn = "Lava"; category = CATEGORY.WaterTiles; } - else if (Game1.currentLocation.isWaterTile(x, y) && isColliding && !lessInfo) - { - toReturn = "Water"; - category = CATEGORY.WaterTiles; - } else if (Game1.currentLocation.isObjectAtTile(x, y)) { (string? name, CATEGORY? category) obj = getObjectAtTile(x, y, lessInfo); toReturn = obj.name; category = obj.category; } + else if (Game1.currentLocation.isWaterTile(x, y) && isColliding && !lessInfo) + { + toReturn = "Water"; + category = CATEGORY.WaterTiles; + } else if (resourceClump != null) { toReturn = resourceClump; @@ -711,9 +711,8 @@ namespace stardew_access.Features toReturn.category = CATEGORY.Furnitures; } - else if (obj.Type == "Crafting" && obj.bigCraftable.Value) + else if ((obj.Type == "Crafting" && obj.bigCraftable.Value) || obj.Name.ToLower().Equals("crab pot")) { - foreach (string machine in trackable_machines) { if (obj.Name.ToLower().Contains(machine)) @@ -739,8 +738,12 @@ namespace stardew_access.Features private static MachineState GetMachineState(StardewValley.Object machine) { if (machine is CrabPot crabPot) + { if (crabPot.bait.Value is not null && crabPot.heldObject.Value is null) return MachineState.Busy; + if (crabPot.bait.Value is not null && crabPot.heldObject.Value is not null) + return MachineState.Ready; + } return GetMachineState(machine.readyForHarvest.Value, machine.MinutesUntilReady, machine.heldObject.Value); } From d309844b28b91c136e1f938057884f7cdf2b56e0 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sun, 14 Aug 2022 20:19:09 +0530 Subject: [PATCH 179/232] Removed unnecessary code --- stardew-access/Features/TileInfo.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 64df1c7..f7ec8f6 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -252,7 +252,7 @@ namespace stardew_access.Features return false; } - public static string? getFarmAnimalAt(GameLocation? location, int x, int y, bool onlyName = false) + public static string? getFarmAnimalAt(GameLocation? location, int x, int y) { if (location == null) return null; @@ -281,9 +281,6 @@ namespace stardew_access.Features int age = farmAnimals[i].age.Value; string type = farmAnimals[i].displayType; - if (onlyName) - return name; - return $"{name}, {type}, age {age}"; } } From a908e098846db8c0069da02e6e270b2d82effa89 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Wed, 17 Aug 2022 12:34:02 +0530 Subject: [PATCH 180/232] Issue#34 Patched upgrades for sprinklers --- stardew-access/Features/TileInfo.cs | 15 +++++++++++++++ stardew-access/i18n/de.json | 4 +++- stardew-access/i18n/default.json | 4 +++- stardew-access/i18n/es.json | 4 +++- stardew-access/i18n/fr.json | 4 +++- stardew-access/i18n/hu.json | 4 +++- stardew-access/i18n/it.json | 4 +++- stardew-access/i18n/ja.json | 4 +++- stardew-access/i18n/ko.json | 4 +++- stardew-access/i18n/pt.json | 4 +++- stardew-access/i18n/ru.json | 4 +++- stardew-access/i18n/tr.json | 4 +++- stardew-access/i18n/zh.json | 4 +++- 13 files changed, 51 insertions(+), 12 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index f7ec8f6..b32d2cf 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -708,6 +708,21 @@ namespace stardew_access.Features toReturn.category = CATEGORY.Furnitures; } + else if (obj.IsSprinkler() && obj.heldObject.Value != null) // Detect the upgrade attached to the sprinkler + { + if (MainClass.ModHelper != null && obj.heldObject.Value.Name.ToLower().Contains("pressure nozzle")) + { + toReturn.name = MainClass.ModHelper.Translation.Get("readtile.sprinkler.pressurenozzle", new { value = toReturn.name }); + } + else if (MainClass.ModHelper != null && obj.heldObject.Value.Name.ToLower().Contains("enricher")) + { + toReturn.name = MainClass.ModHelper.Translation.Get("readtile.sprinkler.enricher", new { value = toReturn.name }); + } + else // fall through + { + toReturn.name = $"{obj.heldObject.Value.DisplayName} {toReturn.name}"; + } + } else if ((obj.Type == "Crafting" && obj.bigCraftable.Value) || obj.Name.ToLower().Equals("crab pot")) { foreach (string machine in trackable_machines) diff --git a/stardew-access/i18n/de.json b/stardew-access/i18n/de.json index 6887ac2..a7687c8 100644 --- a/stardew-access/i18n/de.json +++ b/stardew-access/i18n/de.json @@ -10,5 +10,7 @@ "intro.scene3":"Fahrt zur Bushaltestelle Stardew Valley", "intro.scene4":"Stardew Valley 0.5 Meilen entfernt", "manuallytriggered.healthnstamina.percent":"Gesundheit ist {{health}} % und Ausdauer ist {{stamina}} %", - "manuallytriggered.healthnstamina.normal":"Gesundheit ist {{health}} und Ausdauer ist {{stamina}}" + "manuallytriggered.healthnstamina.normal":"Gesundheit ist {{health}} und Ausdauer ist {{stamina}}", + "readtile.sprinkler.pressurenozzle":"Druck-{{value}}", + "readtile.sprinkler.enricher":"Bereichernd {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/default.json b/stardew-access/i18n/default.json index 6eb682c..60169b4 100644 --- a/stardew-access/i18n/default.json +++ b/stardew-access/i18n/default.json @@ -10,5 +10,7 @@ "intro.scene3":"Travelling to Stardew Valley bus stop", "intro.scene4":"Stardew valley 0.5 miles away", "manuallytriggered.healthnstamina.percent":"Health is {{health}} % and Stamina is {{stamina}} %", - "manuallytriggered.healthnstamina.normal":"Health is {{health}} and Stamina is {{stamina}}" + "manuallytriggered.healthnstamina.normal":"Health is {{health}} and Stamina is {{stamina}}", + "readtile.sprinkler.pressurenozzle":"Pressurized {{value}}", + "readtile.sprinkler.enricher":"Enriching {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/es.json b/stardew-access/i18n/es.json index 9c4b92f..942aa41 100644 --- a/stardew-access/i18n/es.json +++ b/stardew-access/i18n/es.json @@ -10,5 +10,7 @@ "intro.scene3":"Viajando a la parada de autobús de Stardew Valley", "intro.scene4":"Valle de Stardew a 0.5 millas de distancia", "manuallytriggered.healthnstamina.percent":"La salud es {{health}} % y la resistencia es {{stamina}} %", - "manuallytriggered.healthnstamina.normal":"La salud es {{health}} y la resistencia es {{stamina}}" + "manuallytriggered.healthnstamina.normal":"La salud es {{health}} y la resistencia es {{stamina}}", + "readtile.sprinkler.pressurenozzle":"presurizada {{value}}", + "readtile.sprinkler.enricher":"Enriquecedora {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/fr.json b/stardew-access/i18n/fr.json index 4bdac41..1d56ce4 100644 --- a/stardew-access/i18n/fr.json +++ b/stardew-access/i18n/fr.json @@ -10,5 +10,7 @@ "intro.scene3":"Se rendre à l'arrêt de bus Stardew Valley", "intro.scene4":"Vallée de Stardew à 0.5 miles", "manuallytriggered.healthnstamina.percent":"La santé est de {{health}} % et l'endurance est de {{stamina}} %", - "manuallytriggered.healthnstamina.normal":"La santé est {{health}} et l'endurance est {{stamina}}" + "manuallytriggered.healthnstamina.normal":"La santé est {{health}} et l'endurance est {{stamina}}", + "readtile.sprinkler.pressurenozzle":"Sous pression {{value}}", + "readtile.sprinkler.enricher":"Enrichissant {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/hu.json b/stardew-access/i18n/hu.json index 4873aa1..5685644 100644 --- a/stardew-access/i18n/hu.json +++ b/stardew-access/i18n/hu.json @@ -10,5 +10,7 @@ "intro.scene3":"Utazás a Stardew Valley buszmegállóhoz", "intro.scene4":"Stardew-völgy 0.5 mérföldre van", "manuallytriggered.healthnstamina.percent":"Az egészségi állapot {{health}} %, az állóképesség pedig {{stamina}} %", - "manuallytriggered.healthnstamina.normal":"Az egészség {{health}}, az állóképesség pedig {{stamina}}" + "manuallytriggered.healthnstamina.normal":"Az egészség {{health}}, az állóképesség pedig {{stamina}}", + "readtile.sprinkler.pressurenozzle":"Nyomás alatt {{value}}", + "readtile.sprinkler.enricher":"Gazdagítás {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/it.json b/stardew-access/i18n/it.json index 46f6f7d..8e8bbb6 100644 --- a/stardew-access/i18n/it.json +++ b/stardew-access/i18n/it.json @@ -10,5 +10,7 @@ "intro.scene3":"In viaggio verso la fermata dell'autobus di Stardew Valley", "intro.scene4":"Stardew Valley 0.5 miglia di distanza", "manuallytriggered.healthnstamina.percent":"La salute è {{health}} % e la resistenza è {{stamina}} %", - "manuallytriggered.healthnstamina.normal":"La salute è {{health}} e la resistenza è {{stamina}}" + "manuallytriggered.healthnstamina.normal":"La salute è {{health}} e la resistenza è {{stamina}}", + "readtile.sprinkler.pressurenozzle":"Pressurizzato {{value}}", + "readtile.sprinkler.enricher":"Arricchimento {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/ja.json b/stardew-access/i18n/ja.json index 4a8fcee..118fd98 100644 --- a/stardew-access/i18n/ja.json +++ b/stardew-access/i18n/ja.json @@ -10,5 +10,7 @@ "intro.scene3":"スターデューバレーバス停への移動", "intro.scene4":"0.5マイル離れたスターデューバレー", "manuallytriggered.healthnstamina.percent":"体力は {{health}} %、スタミナは {{stamina}} %", - "manuallytriggered.healthnstamina.normal":"体力は{{health}}、スタミナは{{stamina}}です" + "manuallytriggered.healthnstamina.normal":"体力は{{health}}、スタミナは{{stamina}}です", + "readtile.sprinkler.pressurenozzle":"加圧 {{value}}", + "readtile.sprinkler.enricher":"豊かにする {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/ko.json b/stardew-access/i18n/ko.json index fc6c953..b1c4512 100644 --- a/stardew-access/i18n/ko.json +++ b/stardew-access/i18n/ko.json @@ -10,5 +10,7 @@ "intro.scene3":"스타듀밸리 버스정류장으로 이동", "intro.scene4":"스타듀 밸리에서 0.8km 떨어짐", "manuallytriggered.healthnstamina.percent":"체력은 {{health}} %이고 체력은 {{stamina}} %입니다.", - "manuallytriggered.healthnstamina.normal":"체력은 {{health}}이고 체력은 {{stamina}}입니다." + "manuallytriggered.healthnstamina.normal":"체력은 {{health}}이고 체력은 {{stamina}}입니다.", + "readtile.sprinkler.pressurenozzle":"가압 {{value}}", + "readtile.sprinkler.enricher":"풍부하게 하기 {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/pt.json b/stardew-access/i18n/pt.json index 737798c..52d04de 100644 --- a/stardew-access/i18n/pt.json +++ b/stardew-access/i18n/pt.json @@ -10,5 +10,7 @@ "intro.scene3":"Viajar para o ponto de ônibus Stardew Valley", "intro.scene4":"Vale Stardew a 0.5 km de distância", "manuallytriggered.healthnstamina.percent":"Saúde é {{health}} % e Stamina é {{stamina}} %", - "manuallytriggered.healthnstamina.normal":"Saúde é {{health}} e Stamina é {{stamina}}" + "manuallytriggered.healthnstamina.normal":"Saúde é {{health}} e Stamina é {{stamina}}", + "readtile.sprinkler.pressurenozzle":"Pressurizada {{value}}", + "readtile.sprinkler.enricher":"Enriquecimento {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/ru.json b/stardew-access/i18n/ru.json index 149503f..cad9961 100644 --- a/stardew-access/i18n/ru.json +++ b/stardew-access/i18n/ru.json @@ -10,5 +10,7 @@ "intro.scene3":"Поездка на автобусную остановку Stardew Valley", "intro.scene4":"Долина Стардью: 0.8 км", "manuallytriggered.healthnstamina.percent":"Здоровье составляет {{health}}%, а выносливость - {{stamina}}%", - "manuallytriggered.healthnstamina.normal":"Здоровье – {{health}}, а выносливость – {{stamina}}." + "manuallytriggered.healthnstamina.normal":"Здоровье – {{health}}, а выносливость – {{stamina}}.", + "readtile.sprinkler.pressurenozzle":"под давлением {{value}}", + "readtile.sprinkler.enricher":"Обогащение {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/tr.json b/stardew-access/i18n/tr.json index db02ffd..fa9ec6d 100644 --- a/stardew-access/i18n/tr.json +++ b/stardew-access/i18n/tr.json @@ -10,5 +10,7 @@ "intro.scene3":"Stardew Valley otobüs durağına seyahat", "intro.scene4":"Stardew vadisi 0.5 mil uzakta", "manuallytriggered.healthnstamina.percent":"Sağlık %{{health}} ve Dayanıklılık %{{stamina}}", - "manuallytriggered.healthnstamina.normal":"Sağlık {{health}} ve Dayanıklılık {{stamina}}" + "manuallytriggered.healthnstamina.normal":"Sağlık {{health}} ve Dayanıklılık {{stamina}}", + "readtile.sprinkler.pressurenozzle":"basınçlı {{value}}", + "readtile.sprinkler.enricher":"zenginleştirici {{value}}" } \ No newline at end of file diff --git a/stardew-access/i18n/zh.json b/stardew-access/i18n/zh.json index 0d42090..dce2d93 100644 --- a/stardew-access/i18n/zh.json +++ b/stardew-access/i18n/zh.json @@ -10,5 +10,7 @@ "intro.scene3":"前往星露谷物语巴士站", "intro.scene4":"星露谷物语 0.5 英里外", "manuallytriggered.healthnstamina.percent":"健康为 {{health}} %,耐力为 {{stamina}} %", - "manuallytriggered.healthnstamina.normal":"健康为 {{health}},耐力为 {{stamina}}" + "manuallytriggered.healthnstamina.normal":"健康为 {{health}},耐力为 {{stamina}}", + "readtile.sprinkler.pressurenozzle":"加压 {{value}}", + "readtile.sprinkler.enricher":"丰富 {{value}}" } From d12e9b259041391fb28cca2b815cad413fc39147 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Wed, 17 Aug 2022 13:06:47 +0530 Subject: [PATCH 181/232] Fixed casing issue in static tiles location names and added shafts to read tile --- stardew-access/Features/StaticTiles.cs | 6 ++-- stardew-access/Features/TileInfo.cs | 41 ++++++++++++++++++++------ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/stardew-access/Features/StaticTiles.cs b/stardew-access/Features/StaticTiles.cs index 2d0a42f..5befb38 100644 --- a/stardew-access/Features/StaticTiles.cs +++ b/stardew-access/Features/StaticTiles.cs @@ -26,7 +26,7 @@ namespace stardew_access.Features foreach (var location in data) { - if (locationName.ToLower().Equals(location.Key)) + if (locationName.ToLower().Equals(location.Key.ToLower())) return true; } @@ -45,7 +45,7 @@ namespace stardew_access.Features foreach (var location in data) { - if (!Game1.currentLocation.Name.ToLower().Equals(location.Key)) + if (!Game1.currentLocation.Name.ToLower().Equals(location.Key.ToLower())) continue; if (location.Value != null) @@ -83,7 +83,7 @@ namespace stardew_access.Features } if (isXPresent && isYPresent) - return (tile.Key, CATEGORY.FromString(tileType.ToString())); + return (tile.Key, CATEGORY.FromString(tileType.ToString().ToLower())); } } diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index b32d2cf..669bea3 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -122,6 +122,11 @@ namespace stardew_access.Features toReturn = "Ladder"; category = CATEGORY.Doors; } + else if (isShaftAtTile(x, y)) + { + toReturn = "Shaft"; + category = CATEGORY.Doors; + } else if (isMineUpLadderAtTile(x, y)) { toReturn = "Up Ladder"; @@ -917,12 +922,32 @@ namespace stardew_access.Features { if (Game1.currentLocation is Mine or MineShaft) { - int? index = null; + if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] == null) + return false; - if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null) - index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex; + int index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex; - if (index == 173 || index == 174) + if (index == 173) + { + return true; + } + } + } + catch (Exception) { } + + return false; + } + + public static bool isShaftAtTile(int x, int y) + { + try + { + if (Game1.currentLocation is Mine or MineShaft) + { + if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] == null) + return false; + + if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex == 174) return true; } } @@ -937,12 +962,10 @@ namespace stardew_access.Features { if (Game1.currentLocation is Mine or MineShaft) { - int? index = null; + if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] == null) + return false; - if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null) - index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex; - - if (index == 115) + if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex == 115) return true; } } From 2df4cfca2737959914f9e37ae60981ff6516f9e8 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Wed, 17 Aug 2022 13:28:49 +0530 Subject: [PATCH 182/232] Issue#28 Prepend fish name to fish ponds --- stardew-access/Features/TileInfo.cs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index d822ec6..00b0b57 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -317,6 +317,16 @@ namespace stardew_access.Features { string name = building.buildingType.Value; + // Prepend fish name for fish ponds + if (building is FishPond fishPond) + { + if (fishPond.fishType.Value >= 0) + { + name = $"{Game1.objectInformation[fishPond.fishType.Value].Split('/')[4]} {name}"; + } + } + + // Detect doors, input slots, etc. if ((building.humanDoor.Value.X + building.tileX.Value) == x && (building.humanDoor.Value.Y + building.tileY.Value) == y) return (CATEGORY.Doors, name + " Door"); else if ((building.animalDoor.Value.X + building.tileX.Value) == x && (building.animalDoor.Value.Y + building.tileY.Value) == y) @@ -327,7 +337,7 @@ namespace stardew_access.Features return (CATEGORY.Buildings, name + " input"); else if (building is Mill && (building.tileX.Value + 3) == x && (building.tileY.Value + 1) == y) return (CATEGORY.Buildings, name + " output"); - else if (!lessInfo) + else return (CATEGORY.Buildings, name); } } @@ -984,12 +994,10 @@ namespace stardew_access.Features { if (Game1.currentLocation is Mine or MineShaft) { - int? index = null; + if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] == null) + return false; - if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null) - index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex; - - if (index == 112) + if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex == 112) return true; } } From 6d18849f17b8e8183ce7e3a45dd49b747a5bf3e6 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Thu, 18 Aug 2022 12:18:40 +0530 Subject: [PATCH 183/232] Added watered/unwatered toggle for crops --- stardew-access/CustomCommands.cs | 8 ++++++++ stardew-access/Features/TileInfo.cs | 15 ++++++++++----- stardew-access/ModConfig.cs | 1 + 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index 9f28d13..d6d51b6 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -39,6 +39,14 @@ namespace stardew_access MainClass.DebugLog("Flooring is " + (MainClass.Config.ReadFlooring ? "on" : "off")); }); + helper.ConsoleCommands.Add("watered", "Toggle speaking watered or unwatered for crops.", (string commmand, string[] args) => + { + MainClass.Config.WateredToggle = !MainClass.Config.WateredToggle; + helper.WriteConfig(MainClass.Config); + + MainClass.DebugLog("Watered toggle is " + (MainClass.Config.WateredToggle ? "on" : "off")); + }); + #region Radar Feature helper.ConsoleCommands.Add("radar", "Toggle radar feature.", (string commmand, string[] args) => { diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 00b0b57..ebcdd04 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -543,8 +543,9 @@ namespace stardew_access.Features /// Returns the detail about the HoeDirt i.e. soil, plant, etc. ///

*)%b_EAo#s3&r0p-Q^Jp{Fb9 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 z6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)U zP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZ zKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt z00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun z0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP z3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbF zC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epy zpa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+ zfC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O z0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC z1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo z6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)U zP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZ zKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt z00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun z0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP z3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbF zC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epy zpa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+ zfC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O z0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC z1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo z6rcbFC_n)UP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)U zP=Epypa2CZKmiI+fC3bt00k&O0SZun0u-PC1t>rP3Q&Lo6rcbFC_n)UP=Epypa2CZ eKmiI+fC3bt00k&O0SZun0u-PC1t{>p7x)iMAlx_r literal 0 HcmV?d00001 diff --git a/stardew-access/assets/sounds/obj_left.wav b/stardew-access/assets/sounds/obj_left.wav new file mode 100644 index 0000000000000000000000000000000000000000..82a80a06ceba4f266a50211f50c0616bd577d5cc GIT binary patch literal 350998 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%0AGiCcIY%)>K@pLXfjehDD>`+L^GK);&&pXz9#o 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 z2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS} zfnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl z7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+ zf`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZL zAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O> z27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?I zU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ z2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS} zfnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl z7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+ zf`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZL zAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O> z27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?I zU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ z2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS} zfnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl z7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+ zf`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZL pAQ%V+f`MQl7zhS}fnXpQ2nK?IU?3O>27-ZLAQ%V+{=YHse*mgk#E$>~ literal 0 HcmV?d00001 diff --git a/stardew-access/assets/sounds/obj_mono_bottom.wav b/stardew-access/assets/sounds/obj_mono_bottom.wav new file mode 100644 index 0000000000000000000000000000000000000000..0e06fa073d5a2edd25a69eb608c721d4c098dfc7 GIT binary patch literal 6472 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 P{9DHwVV$sE*ev`HHtr6< literal 0 HcmV?d00001 diff --git a/stardew-access/assets/sounds/obj_mono_left.wav b/stardew-access/assets/sounds/obj_mono_left.wav new file mode 100644 index 0000000000000000000000000000000000000000..194635011d5280ef176ed6e7f404d0a22b073110 GIT binary patch literal 10404 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 L|K@gT!1??)BYM+r literal 0 HcmV?d00001 diff --git a/stardew-access/assets/sounds/obj_mono_top.wav b/stardew-access/assets/sounds/obj_mono_top.wav new file mode 100644 index 0000000000000000000000000000000000000000..27608e0ff8481e412fee135ed4bf38f220860735 GIT binary patch literal 4484 zcmb7|eQaN46~}L1rWLUkv12;sm~^-?1!WLiB%5=zx*;-PDWY*WU8;XDZCP~!?MRGb znaTnI9I%n-WQpNr8Fs;vXh@3DZERym(Nc{s{rqw7 z^E~JMoO|2bo8~t(w4Ik_x6Eyo2q1@(Dh{cMk6yo?$br&g;A-YsuF58n00tJ*A$~ z`F_54=AC()xB2HpV`@yxZMk)&t~AA_*kk^fPqS%uf%t*w^iFB7@!>R_?z8(WtbA6!wY0T#o84yfeZJRc^;t`5Nr&yQA^&ahhFF*uCj6{S zE7K0!VGG1~QSH@!yWMULsUfYf6}H(o`*AyNxB9LAfIVO{e1^B=E%~qydzW?D{ z^L747!9MYJMq%<4CaM!xsjy|&)htFm?eadFI!*~6kz+~7C(N-;G} zO^5uDueR0JE(Y_#yxhxud>Wq?i!Sdf^xO0H{2_bDekUfR32C0_@gAoie-_<&cmAM1 z=u5;=ag+Fhm~FGI-P^s%nryAF_5HTrmikivfY>3PwP$UWukvy$w@R<{m+fWS=llG( z0y}k9XD^Ap`QE(An|!;tHeH*p_v_VxYTuA=$amXr+b610RqFSCb+^N*$?paIy3_CU zZ-`%rTSSF;#a^)|#3El*)cFd(!oMh(pFtb61-`%++hW@;ru%gNq1YlG7S!RFf_k6p z=X#%@PWOntw$~cO1~JoT>d9&KAu&x1+i1PDshoGWvA>azsl*+ zHGYl1X>S(KGS66}AZFs^xnl4A;>Uv7;oY;@HWzdAqP=L%-mFPjRLlu-@qRdCX9`*7 zj5-km_Is>HCufYGxjwgeSMeMlw1dW6QWNxW(JrGY!W-Rcy^c}-iPFhe`-MQIVavr1ZS-2F*3w{oYDuq{U-Xz0-UZl2hdPBn_KBGu;GcZS6}!Ynjj0Lu$gz(se#n8Gu#XOL zVITj*j2=4lAau|tH}>f*Hi#3y#6-PVV;7&~i!JT}AFR>m9S!)V59Gob{>lXTu!mjZ z#U?!@F3zyWKKjfr@gT$d5I^kGNA@@;7IdPg)RTPhNq+2;4>2K!K0U-1F~qYFdgRPZ zvPPG4a>p0>phx`JqmS5*J@#2s%cxoOo9kFPVn-fX{4#T4gEQs_9iZoNM(*eUwcwoj z!4@$QJ26lLatz;GsR43ak&AZ(`sg6TK3Bj#XUIjY_$3zh!au&E9@rrl&XJ?$;gd7s zWuF*;H8SBp?vddveB%S($gzhHWU)a_h?h0-q6_4L9#?Fk%RYO^vX4ITaTa;7hp&*~ zJaRyWb7qp9xrRM#L>-7J?vcmVXnSFwGl-g_gB!=Sl$DT)BNAF`3u)&oX9S!f7|Ao*Adtnn_kze#au2C0i0%Lm>>yTp|veY<;UQu(P zH~)?}7S(k<46C^_?MqD f{ikCz<@&$JSojwIZ&d2PB>zb!Y=*B0B*OnYDUaAY literal 0 HcmV?d00001 diff --git a/stardew-access/assets/sounds/obj_right.wav b/stardew-access/assets/sounds/obj_right.wav new file mode 100644 index 0000000000000000000000000000000000000000..4f896e5d1172cdab44255a3e3ea4cbb68865cdf9 GIT binary patch literal 294772 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 literal 0 HcmV?d00001 diff --git a/stardew-access/assets/sounds/obj_top.wav b/stardew-access/assets/sounds/obj_top.wav new file mode 100644 index 0000000000000000000000000000000000000000..2b30ce221b1ca499f0c258aa23a1d257318bb8f0 GIT binary patch literal 302050 zcmeFXXH*nV^esA|q97`uV#0_K#fTZqIg1H%!ia(i#T-zQIf9~sf+!%Un23Ua5=0E3 zC@4Wi1yMwieY&c;`wqXm{$Jjwx8Azn?yJ>n=1g^WO?8E{_dcg>$B!A4>swPZXZY;V zE7xynW2Dh&bTqn}Q8zUjAN`sdJxx>1vK1~{=ILrr{9j&dM-QJi+V=nZAoT9pySHV} zUY5NE_UdEVccAw8pSE=V$N0bWd#$sh!-W5%&u+hT%TnF*HUE#c`TsZe|84}-|B7}^ zYmfhjt*ENDQtL~!_5a(h@t-aKSFfEeZN2!zNriVsTtg30M+Crll`kx*7U;T8A=DVr}scK`5X8C{ZoKaOnjb_V#-8lGPPY!4_ z+8P&CEmhSc8cjQmW|OMERMle|%@B>oSydxc)lH*WtkE1$)f1|^S!-xCzN)%TRcC56 z*EE`VRh^`&-8Gt6jiyLdZB^Amqxr3>KNU4pQoSWLK&Y8Q{UWQKRn@$vhSksvt)V$n zL({mn=6Y?-XQ8GtbuU+Y33bgswN`~%zfz6)rTQL(Q&DG>sgFLZX*!w)2AcG*>X}@1y{@L7o~BVPwbNoncdop^Ne*eDv}&Tn z=7`3_#gXq~P&e7tRP`97CLdM3W7R*A%AaG(S6}5|b7f+c{5?^wpUPj|;7{FHuM+9+ z5Z3pJRMd@MsmCuK;i>t&W`A*Zi12MJFKi_5Es;a&Do;!0%G%1LSlM%ie7v?Ck|`q0 zMe~;8uRjkR%TG<^_HDTH20m>GPkqYQyyx{tis>_i>L5aPiUU)`zj>lu<>X+u~v{YHwL5>vxP&}0faT>wNn!BIn z!S{IWnc}&(FxoFK`y#vlRV;R@y*F!mG|;h*(D^b}clHL|zh87a9M-kW)a}M~!W-yr zsL=E;*4%rjuJKn>{gp%Km64TlT_Z*3y}YQkLSvMX_Z3k?E%#8zJW;0()9ml7$vUVB ztE)NLToc|^J@iMhiI9g5lGnfE7Y6XUf24KeC1FSQPcV8aa*C@g{!~4Usv6xJdgk!@ zh^Ai<98RO}QnbCK+DggU(xj`>=ql;iHEC;+l(Szt`9#7xDgTXh_kz@E0PAYOc2u$2 z&G{(7SB)2I&E)6%<+^PZ#Z&3_QQ15|-D9oRIiS{>pibJYe($Dw%~zi_R~_f514gSi z{Z*Y5_1Jh#l#izQ5S??!b>he9TCCQ!ZBoPMy>9mvHIz`@D~j&lbe)_$otaZK=Iu2L zw7 zxQdtibJl|Eo?tHvrM=T6>(gY^mjXOt=z)Fuh~ElpD^%8^wJHKkq^g0E^&;kSk@daG z%?tR)K;fAw%Erk#cJlbs^5f%jz%{w#vfT2v>=Z4R<;#oeDs3hxm;IHrGNsKL)gefo zJ5bYdjOO2ajlZwPu!T;9hh}E6=AEG?Y^26@tm?B=J>;Z>?NOW(<$8H?(FXbMRk__U z*&$zEQb(EWq%6Cs1emHCE48PG+I7C_xL=+6PkEQG>=g3l8}i|^BFRKV*5=(du$W+J zw3(!&(T$~a-G>IOAid^NQVVHZ2R3Xnlbv{SAlC$lb*aKhTp1yk9FX;w%a7{F zHABVyf#O#tFI&o=G~)*|*p?I)62=-=vZ;;u^s{{LH@Z+Bh_c-LXESz=3mDRk|fH%)z z$1ku~PgrO=J7LQkdGbAD#G;F0`&_yCJ$cw*W!_#TE?8-nq!b@e%GW3ppUagVa-aL+ zjGeIB%U_uDt6SLb-fYtgNjFk*4wcR%N<|u`JA{o0W|oZoTgQW9_{~P5(G(G}T?{-e zYM&6+UgFv+v2wDg*+J~DEdu}Yb6>a#c+DEZW~hi-Ba)NEPCa?;D%tm*?4+w)AFo(# zP~HS+*R!(szH-=2x$miXj#Ew!Q})(YrqxtzO652vzcNsK`zveLDI4x7+eo=SO%<-H zS+1Ivr`~CysmfA)f2t+3)VU*6|JBNxZp!AK^7ULX>MNgdguA4&w))a)a;2= zoGcCbD|KwhQZ}$5{_KYyU)__x+sSPX^AQ0&=P=J+%Ug8ingTXrKl|8`&3YsqbCDXF zNfG*z<8!jtNIQR%#TqHwO>(zlbG9;XYyR#ekDVrN1&P3sa>K*&Qpj1ImG<_^?)^%f zw^AIU>XG&udoxXtm!{`D&5an%YCFwWf6a75 z&9Q!(Z+p}aN7ce~Wml>a*+{Xiugu*m8|;@qwUI~kk_U3}si|DS#3?gbM;1*Q$=UBk z&m1v(lW00o=)~}C>-g5kEX0=G3z1rPmHLEGu_cYG#MNWSxP>{6Xz>HeDeK@=&#&YSM)FzwsxZX zDWR()7cG~Ye3!4TR)&97#%@->W~gQ}HGMW~>V4HzBxpJ`)v0|?6Z2csbFt=vr{>Nl z)n8AuW~utkQ!V_bOzon^geX4Wl}r~UD@qwVQF-dBtg555?WSb;$~CUa`l^^;EZWN(is?Vg4QdUd0U6*xT!T#8?2eIt;b@o?gVY$qrEeJ=MmI)T3=wGY_TfNyS-`CpJ)qos+$v$P0JMhtlMR zVY0QUV%S8nIj*cat-R4!uU9Dx=c#dKYVW1$ioZ&5L-pPsW&9rHHI_aUBpobM0hud?~Jc4Ep_v8z}dnJ$~e%OSm#%`Qs6d?lwy z;TCFcfwH!y+GVq1?5vEBls&G>P6gsYqChM0&`_)h4>-spyURx!%W{es7buDri7D3NMPt!kM;y=- zIdz0)fN-86GB1d=X=1UVd~~>Mx>TNiRLVC-P><6~VH3UuD*LrMt1ZX@k1z zp88f+OS!tFgJxL1n(l~gEMX%Z*$+GR z?KZPZW(!vEC4v06i+GbKydTMieU%kV$#+s$Rj5g}nq}KH7SWn7t{U$vnl)`Th31-! zo$BdLYJ8evlcI3g`8e@E)RQp!6eh0l~mM@p^FNGrOt#}=$*85>iZU(e#5 zYKoT8;&O#JvRPgjAS(rOk}S{vC+|{Zy(BpyPJX{$KDbKW-$C~5E)O@AYuL!k=F2;- z%eGZ=>I$XTb7j>`b^0Ckg^i}fLDREDGxWJ;kcCb`lxEdWjd^cPFB{FP>FV<#s`)tO zU?;_Gg8WRolHJ6m9imPnF}<}|T*)I$gvLmquW%kNQWguRr6PTes5?U(u@*Ll!t5>o z;mt$F^OeSYdnEgQfd!3W<80Zc2JHA)_S%fCa$zO*Ec_K~kjakstp z#^0s#C%1Tf0?!EK_a1TAQ2wZZFDl~g28goRqV+T3S4aNhE@yv~D<>&o;Yx=JrG*xr zrmA_qYWo?g!y&b=tZZzoo?oftu2bqo$p$gP*Tx{bx`PQrhiNZu}Tt_!0xqU{x7ypmhw zBLZs(y#j70@c}>hR5M}HNi?<>V_gMB3!P%|td?xuL$su)OlTXW_^A#;}~`{j?D{ZyE9p?#%@xvc@JHZ2oiOUN_UvJU#rr7gQZ2T;&8q42I<$+`6LALUDSGn}EyyT61 zTvvH*t=!+OY`U#DsET15)q11)WQ)2eR&8@$UGq{k^-x)e>N8f|HA~(8MTz*LY}lgg z-=dfsDkEAbtCHk%U*xt|WW6l8(qA^olSki^`;^KaiSpL(a*sRmw>bIeCOK%K+)+n9 z@=WNi5C@xx#CyEcVt%F`pBl%^k20T0EORWI-j2N)$qrbvlHDx9i#;k}FaNM}Q@Dp6 zuX~S|hH)E(_x#SkYR5C170 z8i+iU6tXA zY~EiPqos1yO&sy(bR}M1gdCXxki=W24&$8RTOs_6??8nCia=TBw(po&0^huVvnCo=;l9GyC#Ry8PdFb}fb7Nn*wi*rQa|K9w#1%Hn^p)&_i~0r&5~XPI)B zZoHx{@7|Idmoq0_E-+P|_om9O$+Jv#tHEcpH7XMPJv0X}BCJk#ZO|X)> zHIhb;m)ebzo}G~@{G|D^G~9q?+OxeY*zenHz(w}{HOmWPlhaw@5%xBa9azdvxUh0- zma~AZv|^8zv4b<1(FvyburD8(3)rT?eC9m<{50QplfTa9wgvoYCI9@3xBAD6KJl?d zeDE{gCY`sB=11ds$2e}E&U12j@OPf7BaYV-L7hea4&rlP(YuAPG8LDK_^t%*x0U}L z$QOTQ%2}2;j@_t`CdNpm4pPmzl4>SB>MgBmAl05AJ+ziKo{)wfmp1>9I((N(1~Z$^ ztZF6G3}gulS(An=sx8}|AWch_&d!y7EtO^|BsG)jJ*SFV(x3OVr?+&si==Z=T5?-5 z?a2nuVn1H9FB0pziYFZ4>2G-Ve15B;u&X0hnu|LIqSQqAgX>iB%b&Sh0bicayXEns zpL|X!&ovYdHN_H3v8}o2K1BR86G~5UTu1y=d9xJ$@fNq-%12J+H(PP{3O3?3i@U+1 zFSD(C*+UQ3(3#EiVn=tgp=X%YA=c$IYq*UCIpzNzNc)MW@i)X)l(WLOZ0375{RPu~ z&f3MYoA=qFI5sYrg`Q!l%a}_CcA`MaOOSjwN>ALStJYGir{q0JT76lnxkcLgOqzC2 zDi)I8AE{z6yV{=Ru4A)Dv)$X+&JiqmF&p28MOd-uGAUn?<~@|QH1b&O@Emsyf0Ykz=+A7t_gHu@SfOkl0^S*XmOH06>NA25>p&E%)1@X6Eo$3EP! z1Fu!ZO7q#^Ko)$A{hiLtMzaDVmeQCdluPXlnP-J`u^o#uVzJ{`sWl7U!n{^6J8$-R zJ9E9n%AHxE8#A8Gau%_ro!FS}EUi-duEXkPOD)PJIa5l}Wf%WSCk8X8er)J5W_p^v zEn)I6W70l!uJ8aL6onjHTEZCU^E@X4YvIaZYuSQJm z%s!S$*Rv(dJSqLY^sQKmx+~Q+V1b2F_z*U!6*F~a*B7u6msyMR?C4FFafjJoWQQ)Z zX&YJOIu_NR&FsK7D^k`gX?c#6>L(3(E`2&Jg?yH(f+ZId=1?YC&R|Pz*r~%z+Rc1I z*sN=8d=PWI!i;yZY3tZ9OBQCrEK;PDVCncO>BCqlu!HopmNd~o3ja%%rjkh`Y5q8A z?m{W=grxUSTJb@etj8=Gvc7|uVJ~Jgp4IKj!g{dbby-c4Hs(oYky31w)Y@H=4oRQ3 zN>zc9|50gFx%4?z3TexlHD`PJuy*TNhyG0OCX3z8yi-~DY37x{#%*E;y;=M=_Gb~> z8p7l*?8O15tIqGfwR zVzrciT}u2-`|Twi51Q9jx;Ba?fSUE9zrX4DMEWFC@ga)tAk9dl=!H@+qlxa)d_!rj zkL2>3LQhC#p>%eGR5^$IY^0HO=urd7A|DR~1${$%6KPpz+HEUwA3FO`>eyZ~>d9J$ zN#Cxr>_#k@SlU=7TJz-5EWQzM*OFcS$Q%l#btjm0s1!Vc9Xu@!uFbk%kwVL**cZ}g zMVeyF_I68uSf&$mF=Nk0v7Zm5Csk70VbbVR(vK|Cv5<~=P~t0E zxQ23k>Eb3jb%borP>MGNKBUp%^gNkN6KHNI9gicEHPqUVei#s~qM%@ubfBh75M7RT zV{jq|cUB?lJH{rUUOPHtL+f0~E|H>h=xR^tUk7Q+MyaDV0kKQkI#aTrC#~!*HEb;n zDyO&K>Dn#omPKi9J0$|3P|npQXN* zJU6l>r=^*CETfy$_=MErAuaAEl~1Pczp05nEh(ndN*L=%`Ss{_cWK)wa+)EH*hk82 zDd`3Uc9pK&qzPHH%Zq#XE+U6h z&rTHTO#1dTXAfoXqownyqYG8EAg}3k`aP0bQLEc9slwZPh$+RYa`@|!$sD>ohVrth zpD)==kW7A(%>~K2y<{071^1Bbj!QWP($}HV@pw9yPg9pupchpc(db2VuMqcF()mKP z^Cxp7Dz2i5OUQe&^y(p*xJl0#-S(1H6Y1wnDYS}ql#^E|>2Ib1qv?%;#RV943rEjl z;aM2(giZw3o<-3=4Egq@dhX=wMd$tL)??D~qSy@TF@Y|H(z}1S??BNvFvXni z@501VY}gO4*BJH?vp=DvKKYu^R7ct}ovuVuM-SSYL!noxT_V|@BintnXa#lbOzT?F zkQ}_t!Jc5a+(4h(u=2;EGI%AT_7rMei?Smr#Fjh_rQIh9-KBs~YTrmQJx9|LDP$(u zPooVI78D@d5Bp9aVL9w~V#hpW1mTny_Un;FDf0Kw%%y}fdiIhkdrCE1NUr@Q?Mm3A zQi-nA<^t)3Qe+1T8cKVTF}@H}PNDq`*k44CARPaQZ|_ih7}e}SJpw872n{VI_iTF0 zX?!_deoety^m{uE@}MdM>erSE0%01BKFiT#Juc0{>xme@6V7vSAPIdAV`W21dj{7j zbW4{?7E>cLiXKLm4ak|Hq!51t(DN$H=V9tvRP}@1RJ0wBkBhM2I2N2o0O5TFrmUfu z>ExM2X@0c#H`Pxi`)|}Ik^0`IQDTMlVb%c%b}~_8N;rjWM@U&gJ{oj z8rDRL{7dBD`7@;QfztNclKT@HaE>mGqp!`${1dVwU=)nfEy%it zVGii~630(qq6zhXjT2L8uqi!TPd!GG#X`y-OS&y-XdC(*gBfq}&la(JV4+k^>WzC@ zRrB?*;A7P~J!CgVvtGEl7P{N9BoU5j*jjw8BJoItZY451Q`jtuair)pnvqCVR??jM($w8j(;?F1lTzJL(j_NpR4d8JTw0bx zbDz*;nno7sA>CAg7 z@FO1wvL8Xmzu?phge}G3rASn&`WazSX4T4{RTn)18K}CnD!ES1APu9z+U>wa~OU7d;|13RNL|s47@xx@> zMH(GP#w#R?A{xI#8t{#R7fRDoXsxl-?;y#6)VV7~_N0j~@gfr+J@D`}BzyGtK*n0s zx{kOrIGvB$SvYD+%Z+Gif3h7=jaHp)=)Hy-no;dLc>fXGW~1jB)HJ}>nP^c|wX_%J zeycjx7d>0x@Df~fM6c^`eTFW;q2{#Bnluw=-YV)lgOWFpG?;2EpskH)XkUt{z|?y5 z_A7=}!cW2AN^}@N8|u?rH!2=P^WRcG2P!d=W*jH8j#9=2vM`c9d(itgWVwj6=+mzg z)f!6YDzT(K1;2rR4Z5C%6RqjwFKnJoIxWfbH2s@Go8l>I6Rk+0%{!=70QoJUkU3P( zf;v_q<_mJq;Y|P<+QMiDFaUa6uznh5oE;PCZ9WTSE3>bikMoo{|!1crO*OwvZj#&u8V1( z87*_9Wj$%m0xIoFbvjX#y0jw)>$H1nFAki-6D#P=!!vy>8ievXxM+jgy)eQaZ=CTn z5K&2xf8km!+R}p@x{}XqI?|mkFQD3uh(?lMDGoQKE>EFfhKW&deTnlgQ2ZL-^hvHl z!9p6;k7`Acc9Hkdlj=RCl0H)JKjb)2n#*ZQGwEs}%`2ovq2zmo64uhv4fLWH&6`Jt z4QSJHa@VJfgA~<@OmESdk+k~-S+Az3H`Hx6jgO*kE;M`>9i2ljEGVQM`Q+gAH#9nj z(+_ZI3u;_Ka~GIi!O08w7>|a}(eoEJ3Rs)b$R^}9h*mTt8bZA)VAz><#o(lj2?sGY z6Jr-+ZYZwINBLC@-wWRuJW7WCAG|c9+XLzTCYryGCPvWyyQDs+j3*S6NNw)YT^}mh zPfwQ6xZyOm2VJU3IgKg%BXnC)(|6c7hR#-EgDVZTpi_zDwwjh`wZlc4q|oLEWcrN` zh12ZY^v8|DR#U%;G~Arl8q&84)cb(P1&DqM-CsEL5*ZDN|3%N?WY(T0*weXbq&gB? zOY7#-UWb|*meu0TGE?@aSh4wFaFM<+dXOH zS(>nhMnsdv5o#7o`;SrC1u}4=k|ngiA1!E2P5)tg38Le0Jrf80A-{of5T*;%dyd6@ zDb9fII??DURQCc!9->n>Xifml^C6cjWW0fXx>A%4dCVpY6B^NlQaJk7B}IpnT4ZQW zWzFgIOiCI-pWUd}2Kp9ECTFO11ogX0y)IJ6vsApE5}as?74^5KJ1U;lp^f<%@)_Rm z;F^tjKfym@Z9{4zp>QxAYDP4hta?%7x%AVDb`2q;HgwC7MpvOsyQCAVg}}%&aNYxh z9kAJtySp*s8b0}>pa_dUz_%0Wwxj#@6upFIdC^u+`f!2{o+h0gRBtbJnMTS?>SRt! z+LKES`YvO-46|}HYD^9aI*lQRF0}g~g|48{3G^a>Zk3WjHd!+AEhVi$DE~y))96wH zeeb(3twR%BPj{`pP+XW<ln*p9nQli?#09EznmRhr02jJDLMw#HNsB(a>1h|5ex4i$Q-dg4Vn;(> z(mH1f&Ze90bn!8TyVJPybaFG*wx6RspFsJW2bVQG@NzEq-46$WQvYgu*w-{xWbVKjV(1G`|7hzd6pK1J71oX^9lPgpLa zaa-!pflk`d!SN*TqO_${>`o_E)0Q1{X(1Ubq!weS*qSW6l745J-h}#hp_UD4^C-I6 zj*6Yha1{AOP@EIJ_(5APP^pRJ6-jw5rJo5Dpf3%$O*`|bwoV%!nv_1| zQ;BwF^yed7&8T?+-u9q+|FC-^y=y`(mXnb+^;t)q=8(fu8nu+fNIGsu9a_+*p%hes zZmp^IYgDS}k&I41Ff<(o#c(V{z%SgCvA+i0Y)YTZs9k4z(u<~cp*;i1zbUOAN{Kb7 z=MYMjaG)o(m$0=JrE0WPneI2CsNce#x^%7$eW^_rHZ-j*`K};`u@rQadO21Tr1c)O`X;#_ zCcm?Ea}(LDqS?b}wgsignEwNZBH{i3clN;J3_8rma1Wfd#f)RvvM~Tq(x6}SjNE91)cm6G!HNK;N2J;S_ZRG7&{9IGjV4DBAj9G0#83oxr(=` zxcw4sO3+8bA&zE^$U;Nzt;kG3JFUloamHlEk)cmd4Qa0-rCURk+dbSPI!o9{%)wDP7n^I(_J!4NaA+)yBO2sq-JK zX+iFJc-Mkl-@vmoB^F@zU@G~GrV}ZuHeHxPJB-L@DEaA;Fr&TSaqd<#QDRB zJqNdin7afX)|hDx&pwFlhs9&zFax8U5qSig?;<-JiGShNj5c&4>+#fUB=I>kVgx-K zL1CTflo73vVet}L5$1}*Pv{wi&8GC6;5?ShdQz*^)M6^- zJ5ul@Qtim33)yz1{GWL78?{2=_7F=pA^ivfZ4s~?Q)j?!AD(Q&uZs{t_?QfBG%x=L zRyClbdQ{bsBI=Q*9W~a_jT+?n2?O3@=p*D_$M;LHb;A)aRP2D{0m&WiKInP_yYAxB zOH{r@TkU&~-!P*#UH%PLgS4XcRVj94!!ilSA`#|;rI!)34QARsvIa#*aAzZIeKFxE zT<&AfT^MBJ+B<~(LR1Bu72E}&;#3I+Xq(Nz*g`xDLBKN{J&x~jIB*a|rY+N)2yB_#gFqZ~ZT!^C(@eu-rd zaQ7u@Kf>lbe0Wvuc`lW}m?42d{|HMTqtz!||Bkg<^;esYnNrVYRM?92T98W%8el|^w8146 z+9X7ua!gV%@EamDbfOfAhO~{asx|dDq>SF=--^tK(YVeOGn_t}(@QJLYCs#CQ{^v= z1umyx#A}pa!Kny19m3g5_`DTk&O&z^;=D2I0CN2>{}jrDaM}-rK}ZTftBWY|#d|lv z1BMRBa>B}~@ScO|gK=&wQmoKzG+c+Ew=KHQ!M1gX+m0>A@clB%Z)0RUOrFF34d&!y zegPhSMklQ(C_veJoX^3)x0v>-+Wz1#&bgKsKx1rfhspm|3-GMZV zXk=$fx1;!0GCt;$;H%X_>!PI{kuMDh~fd zyA=HS1k*SS$v|WZN9@i4pK6luEQk|#%D3|D0ICr#uXMG zIO&FzW0-jweizU%0HpP<;b?FR0k?7d79Iy7;5yhTOgWDiJMh5+kt=Y0C-UcE_g0iF zg5@q8+=NR z3wDvnzK*>K=oNxm*;srZ7k}c@6I9fqIBg4!NjoWTo09SskLyuZ5mJB_TAB6{SCa8N z86EE9+I9 z4(@p4WDpFGV#gH}9>?iG1pDFiE$BxgD8Bj)ZEe6=6vO%hEPvr?0j$2G^Lv!O!MQZN zjzz&e>eK&``T#G_FUkzYt!Z zX8nO@O6NJZaZI6TL% zP&lQ+HyBeQao8Vc{IKyPp1Zz+olYac2d91EauRlzP~Zyv^VsK%pp(d6hw#G~;)K}4n6no( z&!YZCSl_^#1Pn+*@mH)WLYvyuK&$4==!+5UGbi_E^tKi4GNpSaG}3^U*CKBT1p>OI z(2+5)5VQ0sDj(FC8W+R8Bbon(HYBAm#PlXF4V82xeHrT;(6Dms)js|^41S5SI6S|H z*Z^F+ivGvZ#}|iO;j7IqX}RcCG;u?#>lk_rmDkYrEDl~oSM8=ajN()HxE0GiQNtd; zcO%vgf)?PyY2e7+`)z9!J1zvA4I~zN5ke7owuW&94 zo71544A-8ZFcGWM5g(7G@3Acz8NXqz_3d@Z`7<`Or>#|(WJN_a$*Vt&G9VXA+Fp}# zo6_t`n5o$H5#e7k^$FU&!Rk9W{2c$T;l>Lb4n)Iuhzx~&5j-E_cQH~DakdDjQel{d zf+v^}i{;U1b`=3PFxd-T&SLHkm>O0?hAV(^ZUrjInp{ z^(C$*;#DrTzCunOF1*LhEbw=zNJi(En0*gMiD+^i`gdV_1?#UPHV_l8!t6G-1tKCI z4mYtf6KM~yJ_qjcNX>zbHm&diHVN36fL0HodmH;g&@BLcF2m$9*jYGTh3y&W-$LMd z=qI34AiBN8#@kq(i*wO9oCn8v49mi`I1EjO)dRTRMa!Fb9)$e?$iIpi{!oHZ{~89w zApZ`|y@oacH?$Oi?_eo#rUd_LP?x``uc1MIG3pQcm!d-f4D#Tgf!^sDlmO>g*vDbR z{pzvxe1I#jarhC^KEW^@P0FhYXYwEHdyj)ZFzqe+=VD(v{1eeK5<^2^6oAlkDA#hx z!?<<;qYlGvHw;eW{a&>8LzX+1hrs_Nrrn0id8`jd_(e3lhFuph$s0CjP~?f9NAO}l zdUzn_02+HD=_t%jqV*MA^TUluw2eTs7kHkG>jlWmg7Y_=|9}l&F(Myh3eYbXo^Ron zg}$%w?>QQ0Au$8)A93*&s(!)X1IE;#y(Q4o?)oa|x28xLXi0NrOf#l0e{r5yCrbQ^ zkdT2UX$X6OpAT?85Z}VE?mP;@Af3g}yU6vy@)-QRjIr_99EhJWs1t;yw~>DZ_LtDZ z2kGwUbQq3X(0>mat%iX!Vpd@FCPc5t+HHv3jqa}4a1z~5qgEhl1Y+MEY>j~O5RS31 zjKr?TsCge>6LCKR&9#x(`^eVLpmwdM4h)&-y{uPP}P?U;Vg-CsZf5os*f%Fsmp27Yvq)asYQ>{kt ze?xdGPG)0kEDprLJrovK@WuyzUf}LfcVgHsjNFD>+wgQJymsJ-8z#A8@M*L?j_sH5 zcM>ip@<*EpGs zkzbJb5}xHa{~j&=;_*jV{l@T*$S=X__o(~~yKH%qWmemQc?2>);-3=1U!gEWpp)j>-i9Wld$gr z-n_t=D9}gTc!aa1_?(O`e^8W$(|@YTPr?th)S`SrHB~cujSq2%e2N+mQIdc?5onxP z4Y=FVPu*$h&Ox`KgkfJCmOsGM4E##K?00yOj`sQ0;qmq#aO)LJvT!;B51&+{gHa^nAD~G% z4us-V2+{&DDjY+uAo3y90K{rjpCMSB3BL$*$VS^JG|$4j7_5DUuqaH4M~k~y5rNtv zIC}%#ui$ea?0n&-6}%U!6;n4q3{AkA8%TMM^1JZJLD3_$$wSCfw10>AGz@x)#;Ndr ziVjK8#A8z&uEbXB!N!jz8bFnB5J4>MR1`jK7xB&Ad$i*00g)v{TvARb33LT^fNISc8gM2glCr=1Bn|t3fbLN~g?>&KUY9vQADZ*ZUC=M)IT(e+ECLXln zoug1W(fk-%2hcDJH<_-k%UW?#Noy6}|Sj7_O8LV!S`0)aMZkTxcJTBE@;t4F^ z>|ct86?n#trGCuO;)WG7GG&oeAH`i8?mv!fFW$~ZW>^T)v4!~AfT00QF<_VlpXK4z zOpH2)(j8c{6GQ)me+@=2#(`yMv>1k^n7sbmBE__{mZf4gy&;emyZb*7^y*K6|QQLUV|S@XkU$wY(Nxy z?Z^n>RV#YfkfX=AJZw?pb~*-UpxH45kK*wR%sK{5F6w9De`eIx;9WP!hz@>yY(sr7 zKDFT$E7luPoh!R_&`~TphWq#7PCT!}20LuIQhI!xi^&1h)na-vhMVzs6iFv6 z<&yPSk(?XAtA+T=hlL*5)W2BJ+k#RKN(S^b;8DH6={-id_`D6?5}ftm*|^~Cm#c6& zfITsM;)XkfYy+;Dq0hjdSy;9QBM#u3jcB|J?bo0qmn~~i!_2@o6y@MECS4gvsW8!t z!$y<^G2Vu{0eoo@0MC(A(cyeLj%Q=?emr&@8+Ksqaoo29wj9jZhbK(P&w#^^*G)JY zMSc+NPogk}Luc_>4PH2liW>Adi5_uO#Id;qYojm(KxMe(!ulw_cHoyddN~oT!8JE# zoq zaT?u1Sabz-qHx{9@fwsSkbefdfh}k7$-lV21{*G*K^ewZVUrid#i(z@9zQbDlSe#{w~;e;f-zBvh;avM@Ot4`)j}8u#DEeqQib(6m_WmkZo@IF6}C zoGHVeJWMIYb87g?V9dqZ80zX^sK8wlR#jk=4ao@F*>SG`^^`yhr~F<$o$O=e3ypYY+O8s z`D&cXMh6Y*=x|qy!FIGY;)wuWw&1>EL~ST7!{-jX6v282{3V!S$AtoXVZ|3gOycko z!1E@Q2XVlJ#>M#00(CiVIB@SIc6i~xfb1YzUq*EpmW${Z#_lud%*0Lw#ew;Dh@}=h zpg}7GmN6}(!O(2%R3WCq7dZ%M(M63+D^}%WsTVU$m>92Ae99#0d-tFW6d%f9YXR5cNiAh zu-cB{21$7gfIF}{a~u59f~JjeHC{ismL9OUE%6?y51f01eFeEC!<&k!pmCki!Pu zs8l5bVi;QmPboIXFd~eFrHH$+q8KJCCYRut3CAOt%zSbj) z*s-+;qkV`Jp)vqX0Z#bQCWKucGz)@rdFwDv*kCF_HV=3uOm30nUJPN~KX|(o?i)DI zQ0FG@t3>as_`MwU&f)7~DX|-Q(Xs&F8vz#*IrxKfF`IbJ=%0oTR;bf3nuEzPjB?QZ#H1Sm@LdtquFtcW1{9Ds`uddPW*2hUfYZ_n+22(-zIy1?H+8~ihF7JVlSp= zV+ey!4Z7rDq#jE(0vH&^_tl|~7V3Pw&$G;>N-iP>sPZtV%sY(< zMOb|i=}|m-1;Z=w_eCs=q5E0fDn@A)p5huLinb1%Ef$Q?uo#W?7*LAsdNklw!87ze9+d@q-@=^irU0)501?nGUU4wKSo5 zrBq(CPoj+%ADxzr#&mtqhgU1`q6;?*QEoz44;JL%c?$++B43a4qxe{l*NN6_K_~))6H8+l$JBHL>)dEmhzdIvdGRZkY&N{5#Vr$_%EKpSw9i8$2maFHh#zY> zYoz$ABjtD{fUT917cNwwM@Y(+*{0d(Pb6+)37PX&>u$2~U=Trw$2J}}6)vXXsce)V!YUOyq#;j>KaS&) z73XuLlx8&~J&e|jVG7aFhVcaw&-(&?t60u%isy?6_8dA9<8K*u#%t#K&2&O0+XkCa?XKZGuY_E{#}pHV?U0tj&T-FGVp2fv=C^ zQ5_l|gVlhSSy*Hoo+(c;E*lS+(K8!+bm)|cNorV+;d(k+rQ!V}_~Rg+OGC$lc=i}J z9mapz&>zP{JzOfh?!!j5zlWZ<=9_%K~Y z;Ll8SO24U6!v~ES+l#Qlff?oaz>9yXaW#OZC$WNyof;uhHpMZ*k3kVEcjJ>H9JY&kwatR2 zVL_fx7UEwMR+ON*MTmvNR{R-5p$&(kxMaii66A2q3F93z)_d@+0TniZTko0ibiVLl zr#0|dq-tB~KtsLQka`+X&o6-LX+NAMtn*-|5x4BP!X^h38suY!4(Ykrn-49AZcanF zl0Lp=j@^kL4T8Qj7Cg`FkOTEfam0x)N+beeMrVSWh|ZYK_Fk+7m$9v-%0u^MCS zcqkW*JXofYS?I0@72%=@XG;YZnoFg;WgEtAtDq^ZSr))~11{OIPKP-*?AM~dL+tTJ z55DG962crS(o0}LwL@HZ;Ei)4s8iy4!l=_?hc_r80d1l%>u4f9oUkOMrQoSAXbk6$2uL=o`bq(%vEE& z6T`H!7?{a?t^_??SP+B7j|bv-J&cSB34q;W0&hx6F)bjdeux)T2%ig&6eFGYOQ~p% z`P?Mv#*Mh_tQl3f=EM7y;5d*KlPa4>gLnP;AdXltkw-u;<->Um8rtzrE`GOTtQrlN zBUNLX3q3fwyYMR)p$?eU=w?Aq4zhIE%G)6qJ-K|&#*arZi-SNK>ST)w`BN^g9hdT< zQx5hpz~FV_fzF8M0yxVAeh`m1pkqq|_o?xtG5zbsEGr)MVUK;~6$Bx_4GzyIy zd#W%eAb#L*0n*Q6H*-^G(YXYRPoaO2h@@69(pCH#7Emn4?EkE;O7{)mWt!5y7j+nOtmPmr*`Gb>kZ&?h6Zb zT&E1fJZM#c838P)l>OeN67PrLDo5u4mPg?ANFDc{6MKqL$xe?F)U%;$ME13+9L;!b zS7N;zJ*zR+iwCMCaCIxk=lm0{bnN(F5W`H!afyslV#RW`?E1g6G02QhvvD#tcTe)b z=8Ar|M1w+Bk+gV_8FDR*X4J_SeunE@rpL#sP??AHY^>obBOAMP=&HiUW{l5+-GR|M ztn{LzNticS@J|r`v*GO^Vpin%@rDJ@_%OjFrNbi41Wyi5{ z9CYF7xFl{iC){vDiAWjsQ?YE5a0n}Tko_2_$99HxT6FN?L$-be&{B{0!>BPz3b0v3 zqcPj?MIqX7CC+lK6@9$OGNGpf_jA0pAYFs*CalTDMiXAnMH?%OdAQ`jYAp&qC^F!H zAFIvS8^9e4w)>II{zflOGN^On83SIi<6|wFFgLD&-zHk;2q$zb5_w?MVrl^G4X8{# z-CtqBexpKo)`UO&QX6sy2jf+)qx)$@xcr;Vat^$fhc#||kdL)~xb<*{q2pSzSO}A) zC6c*n7{EI*tq@CCQwXB24O@J8(JZl=b5>suh8S?0)1?u8f;ebKZ2_*>(5nRPTsTpR zcRW~GiZfhC7YoO(W3=o*Io}UQNDp=yag7n04rg8XT#Hvdf(D2C&|io80nF3mcmPuk zLikTMV4N5I_4vn$j#~U^!{Iy>nFYh>O?X0uZ_H?_!f`Ge)v_uM0? z3*c%DlLFWo5vtHof@L23kG%m-lomi`M^%CF18s`nu!MHy#&ZSgU&+BRF3GcT-GH{)QZm=c!44}ntB`5O5Z((8EXqUB zj{7yTr`V7%j)QX^hBDceD{QJh7cnM_@&s%Q)S||Tbp|oXOtqkI0UmIO4ezo`G8#YE zg;G4`#f1_qWw2T-N#SG>zH(s_D~Md_lwy(#O`=%nmL0*>=o-d#K73V$`hE;!CEABB z+-Kpzu~N)+BC7yCD_&xs3D-ehf#TbkywYNy8w2vO(u2?P@sn2y$yHv2v>4<;eH|7$ zF`sKFzT12hF+Rz|5gih0JjM}Bh2<=AsU#`|WV4w`Qb~>l z`+SnAPkQk6|hm+Xu5jBIFwe5dlpGd>N4FrwXj3duVSXv}sf@6T>zWb?O6knqnGp3$ z)Vk?{MT2MTqK9(Hzk(@EE9Np@a^OC$JKR{Vmo?VgjGIAeo~;c@8Os#S6uU6k?0jG{ zB%M>0Q!<^|jz_feUq%k*911iDxbPCkE-xmr^OmpYT^T@gGw%4Y%8W@q6qrO?dD4iM zF8r%Q1yf#}9UQ_`C{CgEPP*|K?+7p6FG9K>zLUH6$BvTF@#R8+ke9ptl{p)R^c%mpmc!r)kj7E(Ynl76AsIned4kgW0E- zgHd{1$-;F#^4V-=!qsd6pDe91g|b0||9N27;RPQ?8Ko+Ba~AWVn;BiX95V@1I?;qo z4?Zx-)*fue@}RH;^9m)JaqN28g%(lVa*JHp!-K^Utajmn63Gtz!|?IE_(X;I+ljC9 z@BtI8Y6NZKwd2*x5s66;#`*7i1RGEH;vOqR9$5o*Txi4WgcWtT#xbB%7DnbH^B6jF z5t@dkY=<~3-u1jAXqu1j({W2LrjfH|{7-`>c68RG)`9Pg0y6I~wd+JH&O}a8pg3qx z(qXR)59x5(gS$HX=SOp++^vlbc!e0oghMgT+cBzGxSD^tmbb$n#$!ycu{qErOm)D3 zO@2i5crSnl3^*4=8zYv4F`MlQ1u{x$VX-c>3PQsb4F?mgu&D>s=*o#cOO}N9I9l`I zAH%*pM2_K^e0-jPI(j^pgFz;=$P=-Z8C2Hh=IC(RDs0JX7L@V)8d1xm$E<7)y6A<| zt7kwJ+ks5@MJ)ksZ9aOtCEI@G$2co~523z8_ItpAGhs;+$C%@_qJ9u#Oj4vX^c~{I zw>nh%F;j<^gIJ+QC?r*3X;=s)RTx?mT64K-L@)MW>#^94&iSaZqfM?13v-Vyv+z8R zSn!-mAkF`B5n}F<_mNX-pI2RYLx-VGyvA%HQ=NHuit{q_X?kIVPUK@z2HJDImVq=a zI%ne*?oeRlW5Y)}v~%OU3C+0Hv*Mv3dUIM2pp^wZeQ0jLVm`@1gP0BNxyG@eI13M1 z#8rF04aF*1bz^zia86HQ+7>L~72=2uS%oq(b;B5BL}#D4VPEFG#+-%;ZI26CxB3YF z)S}T*JjmsCI&8cJIM3RpitNvgFpLNM$TQ=a0RCVzyiW|V?D*Qjd=c~BYJA6s05F-X z$B0b)tjEYK$-nQbkZ-{s8iX9eYJSXht4Zp+zFhhG;4?za!le$gnF`B;o1tkA(pVSH z!dX6OA`?{xw9CSm%%SDrss(q{azAY}*y_Y1`54F6O%3)tp~^*x9sN{TW|dvf$!KpT zmT(fxMBIeU*;vN+zE}rH< zt;Ri@e9wgT0F876Z`2}_0UIw`ulQAVdj*|(Jc5A@DQ_6pzaQixVZpQve&@7mBY4_`UZE?=U@AZ7?%SjhX%g=e&QjO*NdcvvCI$Net+o{t7@ zjN{el!4@4de7K>I zuJf*RqnP8H4<8xuR{*aYF(!ylj3@~}&$G-KllLzdVmdh7xRWoDum}4DTsX)1(Ixze z!Hu`_1-SBR=V;91$$|^V)tgS(bMX?BiYj!qpapa0My$%hr+m-siPGVtOn6!H$Q0{K z4lABUQ3n4sVR;UoHse-~5GI$IJzzO9N0fr5IYOt0vc-X1KLi9{xT0U1;$HH2 zwDYmrjffVTy{MjPG z_p|x<&V{^u@lU$)VR2$sK6DOT(cqdLziCj#6`V#`YrO`G7<=TSfa@Tw;1&x%X+Rdq zuR%Oyz0jcfU6?m{D;SuV^q;9YFh2t=QpkCtA+{OmAqtwXsF z**c8zi8nsv!52DcUD%_=4hORGgJj`p*8H9E0KgNt@ymEX2Y1?sX3eqiapFF%nR^YYP;sYosIS@dLa#E+J2oAKdo z1HNI~d5SH0Qjhk$wsqpM`CW?@PE63E)P*uFsy(=@Lwi3a8gMTlz?*`CRZ1A!n6Z`F zSDrYZ1RrJ>JMpsQ=%EuN`4LyvE@{5F%43y}7d;|0Px6WoaGAwc-ghi@G2_QMg1J5y zKG6ybH#Q$LxjNIpWD^F4{YC8JW?IRehs{o?HE8aZB-qk}q*lsdy$*i2(6&4yMxCJj zK0IFz;hq*v8S(0|$p;4`e`XR)(v|Ru1&s@^!HW9|khIEA7+tSr8>a=$ znb9)gVZY!a9;+`580i-vF)<)T$?C!N@#k<~JXD>7DCaz)*)pNDR2p2mxqyno@sL4%1_ z9N-2+oA7wM?bw+QqZ2l+#96v!<;9C!?%47R)H&ftG=);Qwqm~bezP$DY~)+%lTF8H zq?bwNYn=&;LLv_>D3D_cnigS+Rm`Fs7$+5>n@!pSKeFObNZ7V^0aT|{v#=ho@H*6C zi5oXlb;`DU^zz_hKB6A5n)4(RlFTZ5P|b77P8e=iaiRg!iFOq5&SmR@28+3{%EQwZ z{E#cL`m0=ctau_1d~VEJ8lla3kMr8=tV696FX-`r3jw_-@(MqxQ}WJQZn4qB;zkdH zh#)TJsQt(?;=3SBTw#T9)Qqpg=+59jj3>FRBP3J?^VlOyc*rlAw~kLpyyHwG>(SVU zTROmpZ#k$2gi&+_@PZK)0eFmPmRjxeeR#l#k32Z8M@O!hw0OlK^)=U$|FB=thB0}l z%hg04&e#QC{mtwX$0(*@wP?ks2k3F13y1XBz*Zb~RC3+KGNl9mv0L7WZH!mBW=}-} zj=C#6cuxyw$y^`QY*sXkoewdcibgOuBx0*cu4pchVtYo&!A#C7sp%v3Kl7%pvm?pBgaPjU{^Iy6~3{zi{l;%E=*PI1{-gD)#n>vcR3Gec=FiK~((}<}aEHvO*rYjBD@4+C0cp5oYH1vyNI68oDQe*g# zS(=`>;$sV(iu*78IKn3jai%cfqFdq*cXEBNl{~@`Z-@&YaXxY(EgyfRp6C&e#3II! zt8{QNW5!~W3rlo}Iq{zs7aYPyGAiRewb_B9lnV2rRxJ9BbokPPK1@D)@x1|eSomY> zjvvEKn9b6m32pqs30QsDU=rIo0||B>dbpwt-~%%@1kuNWc42hmHi`oL$0`#0_iSid zD50i5dvUGshcJMz2|{PWZa+pEv6b;QJF8h-V1Eu1bb2)P%TIKERC3~GhbLFTe*9>Z z?r$>SZJuAfn5>vmb~w?C(FF?u%q^sHG~?Te8f;7H5gQpx^O(30)1tjwnh}_>X`vVP zvXLH>**UC3#3>ym*}Q9*A9lgdE-7YKdBi;8xQ_S`<@ zhwz65Ekn3zmRS8J^BzHzv3|!+W)nuUADf*6VTlY^!hl&kQ6nw56&6bQc1U=VFN0Fe zZSbQpqe14J^_a)HFdw<*MoS%zx$!LTUymRq<{#2{9kR93fP>7b8Znnu4$xt9&D=Q{TPDZge3FoX8ab$|14O=rms{C+-$+nkm%imgZR*l zu>maOe9iT^QS6IzjacoM_Jm7*oHgRjfOIP`F{Nfi{7ZCT z>yqdEIA}sOI||HrK7_|CSQ|z^D>4f3s!jTmuG{c>AzIs!SAcvQd|{loic^R`^U)CM zSl|v~Zfe}VHDg;ySh&x_!eKtiP3~OL7hsAFUl+h*!=$jUnjgJfO|yrA+k!%9ZAF)$*k3s!kqIt8{$Sfi z3QctiVk`GM@ZVw_Qh-lvqIT`G!&``{OvSUkfwhAI)OFx$7=7$`IE*h?Hsym{tT-1E zs8AD@GJjGbo?=pwy{RnCm*AXBJVNPi3@b%02kjCN69UChvrnK<>=w+_vGb5en6c2F zu(0m;7l?9dE5L0Vel0|i9cK$M#Q_JidAzmx7(hEB%;d1T%6p9s_Ccxf`vowcO`(3Y zFhTL-T~f)8m zgbA;@BBljG_>o<;A+c4Q4B`i?>|e7*^rj#)+df?3xa5wG zLb()%@hJm-wzYG^T_LtQ#LdgpO1lC~vx{?kD!Xw)LT-4qa@dCL+c3-nx zHL?i#cAP81c!zvtzbJQ%o@T0(&l9uZpVSIuZfa3#O&&4B8kA?@r(;L=(;?Bt#)ME} zmN>~s?>&ABGad_I7hC>P`pz9j2nLCGY^mzN?aw+4aHF*z*SWcaMFbBl20Z9P2A8fr zA>!;lykf$$J`qowc(IIe2)|P=#c%vihgV!8q;kx^qQzfsK`bonGU8&L=rm(#R*hN8 z45FR|JA!za_iqqW*}@bM!k@9nKU@O)P@Sq|-(t+bx}jdQE%tbDR$@-c?nf?D2Lbfv zh6q18acp6Ox=~Qa41*w5uKTXLplC%#@2!R3i3|C-%#4s0V_f)6EA88twK(d+6FR)a zIvUp(PIToAfwu~Wtmwq9P8+VWPr!~CpD)3tpgft@qC9Eq zV29hwcBJJBbHcpkWfm#eJ8H({9Qr~ig#Ut#4wc&lW$bWnig@KJ9DtIk8 zor4W#oXo~_PFPvkVwBd!@kUI~LJnI~_$MaZ$cBdrLzQ$7vALA_y#~2xZo@!kO077d z7W-Tiwp8)LR-sqw*_qK@lZ_p`bhGh<84dWDL^H1D;GRWTG7d7#|L_QNgXd2+Y+_dc zGhn%R%_>&&kJ+`wo?-4U$dN+{in7JCu|6Bmn~|L@PuiFn7!C!g&#cNpHGe*PK7xl>E6hZRJe+{DT|CDhlo{sE>KcK=rD+;*@*M^tX;uq4Y#lrn)E_|E{bJ2yZ=4z4K`lK8nUvMu8 z7xg*VWCHlDa3-W>W1&gL;X0G~?2}}G!R?tWC#7b5gbIabe5Aq@GhR`ll$}xBaA86q zN5Z~8H5!j|D})(Fz8`Lh;<>Yl%ZN3&4a`wE&_^R3uivJS;Y%9yav(hq+u5PSyMR5| zxya`IrN%*x>EQ>){jwn;a23A5w|^@-dY#VEjGb z)xSAt%Jafpu2l}Tm}143InpK9JV%)M!`WENf+Ks?Owt-t#e{qo-ef5=Q%r;7Gx0Q+ zBmhi z!Nrg%-Oc}HN*JG%g=-wlvvAocT^KB3+{h9cPM?Jp28_-U`G$Yjs7L2asJX1m5D@!r zhV;Dj=iUtiMr7b$riC+5Yrydgky%3-LL3$H@o`KvWeEQ{oAnEJA!gtiHio6+N4EZ^ zG^u~m@rMCVW=N4ZIYXRRJ2C`*JNQ@(P9+&2J&y9=#s0^j!DLvMt zO98$0I|X%n`B}$KWV1eC)#JCzCmn+M^Ym&MUUciA!MefqbIw+ z(&cQ=LFxEKj}_@sKHN)3C4ZlKiu##&)F2G-vn(ZNVx$49GO@sb?=ulJ;N46)b?VJb ztmL$qDQ1}5OgzcpFiY$f+p`4lH_Dc|{wN!dF}TV`QwHR0U^e0PY>eYQ$TQB8be5Q? znRC8p!u%}pwr${Se9hfa*_X~U$ib>HZ?{KNb=Yd%84eKna2zJgQT<+Ue>{FwNjhbq_ zZxcnYm~9&z-|T3?ao8@$F>zyEA{RD0e$Eqn-o-qL0IxH{?7#|*v~1^SaLfq@`+QvR zu*U6zQ-d@YOd2e3A(yXl$#E;IQ~K;JhaAu}+aZ-^XNM>+Ef`#Bgt2MC*YNQIsb+A_ z#eC4y8ybvqiu0!ryTLW!-`%Fc9tU=7@OCQ3K9~9l|1R@yd_P_dx;oJ$9~ana$j7O< zP@iqIZqe)-ctmhvF1=6(8!Os+++ux-ZE{|CSiSO!yp_e~kQC!|RWD`_u9QtaJZQjq zA09Wz(I0IM;=AC5(U*OUdZ@iPrbl-^$5;tSKze$uSoLb?DArjat0uMwoj8xrt8;yBnvqqT}-?u;-suNag_7 z=y2GBO*&ETrs;6Wjl1lXatk-Y9`dS`hB7rD&0Kgq9}l|FlDR~e_@?H$ge_#oW0Dpv z7$q{><`zwSs9T(AY$|?1hX>dIsm0%^Ct{BWKTAFCPSRpDyZ*FdkDSOOkQ%YSJfhFm zcmO>fW}lJ)Pk7PBfJeMI!Mze*Y}5-g&qmD=sjnH_UC5mjKD=PSNgpmU1Lns8_Kl=a zp<2#uBU~e!x%APX{{+wx$K1ybsnEPF)$BRBOT7T|CZw`HoQJHP?iy`L% z`;<)hfOT-LRRfY5_6Lw)F*<_?6Qx_nzCZGF3-^Rv?MEdjU?R zYHAl(xM8ekXd1>u8@>wT1sfKJ#UX4@)!JOqsBKtPfKHs&3$WRa=lLK+cIFqN0YmH5 z_xy8q;O{EH^LD&nAZKB*!;1Yg!wWAT z0`#!snF4&vHh{1I#RtP!lRC%vCIhOlSZ)RvO2^icVlXYUyi5kUZ#iy;F}DgkN-*RU z4#%+eJX-NNw^wlg8Pwds;)}SI#IEZ&QYSGwfy#!7S@$KTHcRm7xHnrT9(y$LcE?1$ zR*9FoB__8|tmvJn_iUoiONjDCQYLfZ@rTEnNPJHl9>5kqRGg_ z;VFqZV-roMC7QgK_+v(5`;9t*6AgAGzD$>6Fh{5oFQ+A{@)L&i z#N(z!aZciXd*YBL5pXAF7!sQUiAmPPn!?0BSE8gWF(Qzd7EcT=N_0P&m>5koIG>nY zm2g~6G(DYo>EFb=7ZM-bNp!lBXs0B0Tu&UmmpFeb(W-uOa5Ay|!DRGq;*G{hTis-j zX369O$svy?KW?0y(I)9>mR!&wnS4C?*)z#_o8*JtlAm-)&U!w1uyZo|#boHYDc7#HulWWH$KcAQ!I4+s?VRGh# znmu&G{a_>*c zBa4zR{*tV>Ecw~*$-66)Ef**M;P>4uO&(mAY_TFae`C_JDmipZa?xMO9@~?f)+ImP zm3(wl^5)*;ovq2(!DRbg$$yR{C+fGe#>B&%j^3ANIL7zlU z@+VVrOK!50H909idBvXmTrY2wwwRL5eaUxh$uEM*yUwJ&Ao-U!S+68n8AvvbBum1{ zs#tPPQF2y1dA=lhqAEEmk{oj)SrAJ`PbD|RlN-(^4^$`rxRAVZGP&zg^3AhJ^FPV0 z7m}Xq$$giS+ixb{{3qG@PIAw+b4eY*WHefxXlpAP_%yxPhK_ck^H0&U&(fL>bl^E^)QRrvPH#Lzy?fG-F4VghHSS7{ zdXu{w6~0JodeGLswD@_N*Pm1`(BhY>NS;$Ceo3QY4&6q_$dwH_he0@ zo>M9CId%Gka=xU-(`dsCD*cQ;m`PuKK{vjp!Y^sUxAf2qS~`omeMNtLN5f~*lJ9B8 z*R=2lI`j=~|B-6Hr4zI1yYFbi&y@I{?#!X(KT^&-YVi|&^DAxqnOgovcjwUN-|5YH zbYcN5{)KKVq+P#K*&^CKpEfO~6~EDtCG_#{bbSeRTR`c5(2WK3>ryfaC^UH&J3OeY;uS@HJ%%HQ7LKZl#VJY4A4cwTXIfr>>i+ z{SJC?3)S06-Ypc_Nz=E|`d!5D0q?h)e%(gi-E{wUdTS5u+fLCv^v(|YW-qnfNl)%0 z+DYg3(WzbJ+fSa|WIRCo_E6?Qn!cCT9g++9{Z^)ZbmuUQ-%mfM(fR#Uokj}}&?`si z*@KjJgsKnHvq$N`AqpI&S%>MbV>CF8zC2D39ib8FdNG%pXlZ9IZPC&{x%9A3F8)eK zU*?fYM+frgZyjCEqt<#FsiF7vWYJJMf1giJ8R$$tWgBRLmL?dfxsL7`X@icondpv= z9x>BEJ-uh9>3aIsOpEpOv6(jM=@l~_*Hc?Fh4j?iO!xHkf|-UHXs(&I8|a*wV4&$1 zdf!ObER<)YFRj$jL~bihFj0LQZ8uR@8+JNSg&I02V39ZNcCpfR2d%c!I49k*(m^LpvC%~*S#9*Ji(1&}V;8+^r}ZwHXD6+T z7TGE2qFHwGxoEVVGF|eq3te=-o!)d&9XoY$Q3pFc?xN{-deKG2cKXdl;~jL}MR5oH z=B5vv^oWP7PFm-o2V8X3Loe{Jcxi--dU|P+i@JDej*B{a<--Sfsji!T_tI=P-SQH- zX`_!8d*}^6k%w;jX}p(y4bWmQ1p~CoOO*lo!Ap4o8ttX81Jv0|?E=)yOPBrB%1dYb zG}udz1ZcjOz6_AdOR)eo^3m`h4fWCXAbsg0XOMpOkv&Lre6%M>Klx}@kmmbnOpvzt zXmpU=KKeOG5BSL+r15_07owehIuxR)pK3#-_^EqX-e1`(Ob-UANto^nP<4pv1Smg5 zj|E5_qSpgd6QT_P8W^S<0kVc^Qjo?LkTFOF1@u6OIv2{vzFtTpLiA!G4GU3i0rd}& zxqzMz(fR@!5TdmO^ihZ$1+*tb&lJ+XAxbNxQDN#*M7zSYrijYJJR8>qL6v|5*4;0ee5-KjF z`6YC&kfx-*zEDCZ3hA*DI#oy=_yvT8^ic_Q=Z`I+xkWUtl*)=IUP`@+X=EAwT1?Bz zC@uB%STVg_M$3xnRw<1rroT$*zG8Z*l$=G>q?8U7(c`6*T}0zc>3k8XN@-{@wJjq} zF)b>iE+ynIqm?D}PnrCoYh`qr-`ZJ5+2>lzQ z4pACaPCcTupq!qK(z0@TEJ~l0(|u8DTTYiF6pT?t>iTem-j9(dLhr=L8=<)|Du|FJ zMpgWo<#a7V)5@tqly;U=yC~(9Q{O14i z?_=fLE2m_HdZ#{TXgOVp(97j?HA1b*>1KpZ#Rw7F&rcnp=`p$$p;u#a&5JQQ8KGBV zR2HH4V&sp|tQc7%^h=ELQ{$Euq1R({C_;^5v^7G3D6NRl#wg8+$e2uz(7Y&p#M2k0 zi4iL25sT0hG5Rz@qhmBPLZ8Oyy9iB)(YF!m5~FDmx)`N#5lZLB;W3F)uLv!UQtt?5 zM`>h)9*ohv2>li#dxVr2Jszc*<@9}&jOApH(rJFiD4i&$M`L6tmy4H_)0rrZE~m^W zwJ)dHQF}Qs;6y7Nxr}d7Q!+eIBI) zG3pkj`7yd4p^sCuJT68me$jbqOeV+ZAAahX%**bWd`?-6&PQd0`^D&sa{7@6jbC8O z3!|K}V)VZXQpadg1!eMsSJ1JTyn*z1jQUkjZj72&kb@_soT4$hTuv8abc1IqMvW@y zc8q#d(A5}?t)P>@Vt(?B9pinuP zE2wn^JsqcyDrk9}*7L=2%B-MQvH-3hGfwn=7baC9SQXmX-8V1(n6=%?er)r}nAeO_tL` zaXMd44dZmJoL-946BV>2btn9HjTN+}lG?}Vze-vZr?FL37^itvRIid2R8gx+`npO! z_U$UV7pDPLbSh4Ls>l#II^*vX>-knw z*AK6v`Eh!_ioT9h$10i{r_NP0IZl16=(9MDuaZIgsfu>SXcDpT@~uMT6qxtD+a;6se+D<8-r%X7OLCCViZyS5u2hGFQ_N zmDIL|T$S`&4c)Dz!WwE?MfFdRQc2BDkgbw#)zFMe%Bqpk8&yO5;#65pW8?I5HMNOT zk7}8SCe_qBPHn1bNSwx1(@$~QT}?;gbfuaCaq3z_6>%C}L#1&VQA1{)`WiYAC#t5^ zaVn^mkWSS5LCXlNxB)KITVGStxHm9({n z>Q>U68kxs$YUr^_T3SORE6G(uYb&Y43997fbV8=%%n5p>ik>)0{i>+-Nvg{edV-Et z(yu3EnI&t;&f{D|AH}IrjXYPdn(D^MSWS)N6s@M-ae6TIzXNJ$VVvHtk+~jQBQx8f zhTe=*Sv5Ttr}@=%x`LjsCI_$NYO+>PZ572UsC_jx;w_LGpF{lO(bQ-D5T_H>G$u~D z)r1NfUroPM5YOF(a_U({HA)*x$rUAasf4(@r8Jf|ei`Xw zl0dtZ(>oDbUrx>lT`#BSqV#$NEsWBJ3M%0&z#B6rQ~YO)j`8%x=-Ep8y_~d_^iT!8 zSw*`l=zJ9oh|`MH4ECv^<&|`yh6YyAu@f>NOHRsQ{&m_^mj;TiuF~m2<-__)VMFD?2Fij)is=ERUt{H$h6Z9y`S(({S zdG-}0-d|AW%F3Y@_{maoZ^|J zjC@C_o}%=fpxifA>GHnP^%JGtMCF5NN{h+L^3RnAKUDI+ROln+{0!yl$4cw3l?$IL z6TVfhe5UODPPzAma^(l5(+p+IPs*g3ihho=_Zy}6FUqx9O8$K1jUSYu3zS2%mGVVO z-8staKa^?nlqZ%ezF!sRN~QPj%1^76gA0}C*D7t7C|B1jdzUKeO-h#)%G|ArW|cB% zhcfIhrNM5cWUW%LSNV8@vh9F!b(8YNVdb~2%7CNFqdS!5=}P)8<=;$Y^j@VTN4dUV zQRgb#4k>H$mBB}p&-BXaV@hw6vLHiIEXotvirubcs+5&3Wmul_zE?Ssuk;8gOY};U zu+qz<+$mJfT9nfz%5J+7jwlmcimF_B(5tMhR8)RtLX9#uq_jMxoGnla&nUBtmF4G^ z`en-Si^|TZ^6(X9V1;txs&b)HG5xD-uTl2iQpTQA7DKu3tTL69wDU@@yUHsUm7Dhz z*Jb5s-P%{KD&y|2&A6`IdZ4z|O=ZhNwJUEc0~*)XNhoI?sa;6Qnr5~4-&F>*tX*|a zIrVsLqq?;VTh}hCU)!Q>?WG2_yV}*h-mvz`r)#%1s@>SB_Dtj2hn}tN)wK4H=V})= ztF6<$Hrk?g(f`}K^JpE*|L^~&BqAXyX)Y0&iVBG$J~D)oGS4#4^SteO@9Vk@8AE0g zA~TVMkjz8MROWfM*?S+y@x0G{TD3mwd*An3zu)@(^ZVmvo$I|`@8cY=$9Wu=y)2dz z=8Y$f_qvHZWd@fvXP-7#-ZW*NGar{VeO@r%lsEHVGB+xiy#>wdmCUU|rtACW@ginX z74yz(=4MsXvAC&J-ApfOzN}%6ylyVmGWp7wigirYw@gw!)1j>S^%FCsyh;DmjHqCq z|J)3!Xv#J+UEedco0vA0O{-?6#Rq0!3)8Nenb6AgsczP^F^M(Iv36#1Eo0l8IUkuP zI++=DO~Ec^H`CFU(!_9>@=2Dbd($1`nHeK7B*jQ7jqp2Nlj&w446V2!@=8q&(vzwU~ zGTC~V))BLgA; zn+3y6 (#lzDiX`FVu-db(*4V@l36mt)PkStcspd@#r4Ni^H%nu-36zA;aR%+C2{ zRM^yAU>+K24lOi;N15i|nY*LS`NgKsm!`+}CS|O-z0~v>Z@MlwS0MJvTwaGfgoLysTO*OCoV3NN!gV&iI)69+y>Kprs?yOSvkw>-e~g7F|Tei zo#vWho6S%2%-$^~$9(hrR#WR+(|MbTSzy*|H>($#>^sc0MW)qnro>|N(@xWOiFt9i z+48-K-eaCzW{&MOZI+wY_M4e2O#1_7$4WE$pgFS2%sXWEtu_mOH!IheS%*zztr`1= zY4w8{@uzubo#}qW6j*Os95t*rb&r{|Kbnfi&9)8Z)e~m=Pv()oO#6-I!e8d4pUuX< z&EB8QxRYk^Cez`RDZbgfciLRuY@R)1e%NBXvu4UK=HgkCwACCwX9jOG+s>Pg+s&E_ zruMI9-bM4$4m0+Wxv;|wxoqbBX4+mc?RJ`4S55I##YJYFhtcUQ9O$f0~ZQ zY&c?;TJz8`bJv;AkDIcdU3|jy%Ve|sZB{*CKR;<~W;^$kshP#zK5gbbWUHSwk7cvp zoHNM}+ovy>ygBTwi)PLvw&G>;-lO*174vs4JN}x9&tpHhVanvSmu{N#`E0^%Gv^8W zY>MfW-!8su$`-IyQ%&|~?AbJPc41~)sE}=+ z#f~p*pUi4gU$v{V*-whvdfDwaui5iCtSM$=a@x8jY|&hHc1e3Ax4l=&j(^P7e#3s6 z&(11s^FCp18T;Flw%J>D@Kbij+qTftwpuy6>lwSJydChI?NGs%c)>ne(cXB`uB~J@ zzHB?ZZzsKCpQvJc7PcEduvLrL4%O_#ui3oS?GMH5<_~S75;m%)y;9QFscrkeZcEg$ zSxVa%>e-29>s;$`3?x|)cb+&`5TiewZuVLHuu!n2fJ-uxA+O~XO zd+{SXtG|7}uFW;b4yb2`4Y6N+Z0`=U-+yBJjj%iF+f1?c#;0~vynXdE`*M=)_PJdd zvOhJnAB?nl8rz+tZIdRp-dH=esogW)9%yDinq;%JusbK)YAtP@ukEB(_Rw^jy^U=- z%T8-+ug$TaePM@wV~cjMk1Vh`JK2egY)WVQ;u3qLtDU#huIX-zuCQZz+HY6cF1>Bx zwf57#cE&nezrTHAgY7ZUMsKu>2itR-Y>r{J@-KEslpV9p+GuwCNTl5Mx#Zn$E{tg`E_*@J8C>KnG$k9O%T zJ9?vCaL1-^wzKZqR@>~)+@b7!A)CyTpB zpLSJCx#G{cWpB7@FS=rHy5(=|)#_$7{P5m0iQSu5?v*^kbK^x@-NZyHUek{>&Y%?RqtG z+v~bJP27@CT({;fsewD)($#P1>bG$@o476Q+?r;tYzNn@rTe~<%iPAj(bX+(=T>)j ztvk5Nz1;Ji-Htx)SXWoOzZ>7f{W8#f(A$+6;!gH;rgP zzTxgdf~y(r-b;47V%=9^*C@fIjC7ZhT%9p)K-kS3>+DE(WxVS##+97p_KbDyCc9@R zxCv8T<4JDwG&gp#yEen^p6Z^N?eb1{HRifjGhP30+}hc0&bRJ~d2Z)I*KNML@}2u} zfy?~8GmBiVWv=KFmv4ouvDD>VcWs?3v(XjV z;HqzOZ8y3`TioPLuKiZGd5i15-5uZRy6v9})%lEtUhurdmZsTFM`gb?(Pq*$5*ZQbibHo)p?iL(#*H5@HC*0z{T^D~lxa^8uc5AP=n^#<`Yi`9g_uviJ>xP?g(-phr z%HMW>-FAELxGz&&$Gfho|No<=x_hZ^Mw(lc?kXDB(z-vadkohN_oIjPXz>8i4{$Ft znI0rA3w^VYBP-_~A~_qMW+Q8MHa$#i4qnee=A2A>gy=_k5$f(jKGy zV|2>Ln7kZ%oHO|-_avpCU}AoTKgsQ<_$@zmo+f_*=08L0r+MHx7Cb}4=ehPQ-@ZWc z=ehkNtzY2Hml^XS-3qeqC1$?D$(Pwvh=*Un7UqpYyjFx}g=tij(5ocA#@ZsRF2>oS zoGea`VmwrmSBvvfDJqoU&DW`3lK0=BPANVtP5sxYQ-;QG@Y$QRD^2US=v#)KZ1N(c64go|R>p{0=M2F{T2G$`kc2Q{JI{MdB(@vl89krNnzQsYu@UsZ|N{ zKJUNBxyqD#p957WRhji4@NyMqR3+C345`M=52*7Wc2?!7>ddId@#^&W57R%Sa&_9& zAm4`+ugS#^Nvp}vHTbO-lWQ`qHa%<6{Ua*Zra~QZd<1pa@e!-*5>tl`^?183kA2Mf zx~%?~ujbf69cm2 z=S*wJx1Uq75w#ogdn2wiq)TH$jmXr5R~s|72^$+zq$!^@;m4+2XhQvF^lQq+W<1c0 zq0I?3BX`I9)BzGmQ3s<{xx(f}uQN1ghyRo7xWx7+O8?(AIqZ^O*Ab)py_aM1D`+9J@JFoWS z;~wXn`|?RYOkX1X zsMe2d{W1dm*xQdX{mI#%cKxZ`pNakH)t~MCnbn{4{v7B}sR2AZfOZ3@JbYb09khl6?@V11UdBoodc){3jGn`oIE>N5 z*f@+O!?-exy~B8RIJbxK(Quv~&amNB9nN>d={}r4hV%7syeNJh&P!3;8cwAs3PjN; ziV9KmiK1~7qoU{+#o{Q&M6oZ5MN!;~VtW+NkKkMs)kctQ1f54vbOfVEPHv6T{=N8SV42WR0b0EV*JS8_QF%d>BjNSenLCE|%W0)Q%-NmR7OMh^1dFD}(lz zSf<2sAeL`qITj3_j%8yk7h~BG%k@|e#*!M#-?3zg<7zDV;xMrkiX%rH@5E6cj=FKY z8b{kW%EZwxj!JRF#!)?v(Q(v^V{#mgoycAEKc#6dHd^{!NDIQOmc;1PpTs+m|c|V>8@zjW? zbr8D8(G$CQvMaPZD?|f#wNR zNT5qFI5dG~35-slPXaR&2qmyI2tOvUJb|qVY)W890y`7!fW za4mu33D^WKC6GIjv;hMI0Cz6mznMA%!q+%ko5~-QUibNVEvOSUZi5yF$Zz9(d8Ig#WL@1G5NsLeA*(9bX zQY49Q6L~9%m5EeIVpAgZlGy9tJt^bF&Pkl}@0i5ZL}HS-nMf$8qm#Ii$hah~B{Ct% zUnP-}$k$03x0s!jaf?Msyp+V6B+4f7OA@t`IFOWa*^^0h^)Hyjz$8+W7?H#S$s{C^ zJ(-b7JeHhs!Do`0l|+$bmiebAvn7cd$^4l_v*e5udnS`9nYd(f`v)iUY%+6`DU{5T zWQr%VI+-_<`7xP_$!todW-`Af(>R#}$#hNT?_{ErxslAIWF8FhT{8JY{FKaVA$BM8 zZiu7)DIv}#^J$3d$utd-mQ3pq4~6IuB43DZAqs}*7vjwjQ6c^lA`;@W5K}_53o$=L zuMjIk3=Od{L_&z|Ax4JS6=Fh&eIaIqI22-Gh+`qv`Zo%(EyT?bheJFN=0b?PVbVer z4wEBH`7qChsU4<7m?mK=hUpOIqcA;#c0kY$4bvw~beLgb;=?3_35OXKW^9-VVJ3%} z8fIp&%?~pt%%U*ggjo{i+kfhJL0%BX-~3}1hM5=a^6RSL;GcqBzlB*G=5Uy0Va^1* zu7z0}CMC=||CxiD8r0ii)`z(o)N^6{(@%!k6y`*jZDIZjvn#m5fiRcC{2AtEu**O9 zR2UcR$`s*jmQMm>v;)(m|07mgwMcU{}q6oYFR~cc4KNBJu?_z3%tzpIm*+0u44@cM=T;Y!}V&G`bD@Hrdv=u1kc|#*xM#}FRcRS7Lkk>=YRHxBQ%I) zyaM0KSE>={{U^d35voV15}{Uvh7mrAWPtd``#18hGb@szv?{_E5jF>UzecFzKY4`m z5q^r`znY~HGDnymOq)oA?E$7g;r&M%9nN?uy~9-X?;fU9gtx*Och48jm?8cG^r!S* zf3^D4JCrfcCxw{kYlnyn(>p|e{~{r}`tK{m7hxKQXc^Ayl>x8Hq)Y`## ztuU=Z)C+9;Qo{QpJhw% zsWXjav2QYx2SySaNu!Y*8Of@Vlp00;Q6!9F;3!Uw;^-(Ujn3F6k7o90Qb+U17#fYC z{}_H8!^JVY`Xw#DWa5{c_>!DsX*rg}v78x8)^T(iN5VL+k0bkd`i>`gJn7?kWCBqW zh)f{IMDk8#(nKatr1&I?Ok&F<)=i?-SJeNCOq03x71Jk^Jei79C_IHfr?7Dfy{6J| zDvx~4^{LGHnz*ldZyL`{1!KA%qE8Kg~T_Y9`Zp#My&&E)x6T$#z{S&W&* z7qcldo2+vjLI1_rIgaVorX?uEoSHruPzxF5#)~ z*|mfpzNh>5G+s)srQBW0!ljH^Mzv)WTh57P{IZ<>%W1WOJS*^4vSuWf%mYB8l{(<~IkbNDCeqiQ0s;r~JdXB8) z(0Y2Ur~8jQ@*~+cF#AWo-az>cl=_L^Hn8C*n*2nqja>MN!yD48t(*CI3!iPF&M%za!XLlT^%q)fCFK{+Y-R9PI&LG=Hm+_X zW*a@XlWjY9w-egV;9tq}E17mM{#W95P+$kyeq-tm5`W{l-^jX?slO4klc#r*X%`cA zGGrHdcage_k-O--n;g5jwVQ<9^w`5gd$_uX=sk4Y3wt=dmw|g}x{q6XIk1mT`}lA_ zfA3@cej4nj)B(2dXVw8K9N_VTe0PAjgFJtbl!J^qNV`LDkOPP4dWg!ubMz34f9J#B zdFC)He<$HEFCXUiVMZRN#UG>}X2&0V@dsu8WbYqL`IGX0lKBWT{^W}zJamNZN9b^b zqDR?rgruXqc9e@pNj^%AV_Z7Qf@9P=#v{jBbc`;?dGt8Dj??WpB~GyCIH41iJi&z% zgilcQuZ%GLuZ)oP7t{Wt{@)p4(cc*%>&cAJ;$%ixeKI5DK9v#Lp5jqIoTBw9vY%$f zDH@&Tq0`JiO@q_iJc<=h$+V z=I6+Lj(O*3bdIa%n0bye=ecl>0p}@so*&QC`#hO1u;x6~FW}A-xN4+L z=7-A+y3Fm%thh|ED_prun=8C>g~?ZFdWC&g7<+|>ud?F`m9Fx@Rr+3~{8biRrTbM* zUS-}@p18)*t9*Ek+}9X*jmp>f_8Pse@y9i0UL)&u_Fm)7>v-2`bDa{``SLo=uCwVn z;p^PG&id=Tbb~9`sds~CZ!qizwQjKR1_N$z_y%)tkohKiZ&3Uu<^~OJQs^ebZ&LRr z^KUZnCi`zP+uwDQoi};q7I$y*(JfxMMW0(#zr~bWbi2i-Ta3TOgms2Uca2HZ|_b$)erSVkV=bGBB}IAWmYOlsjNz6S}MP$vMiOO zsr-`4l~fL=;{RKL^Qk9Lplr6`8Az2 z>HMC~)^tv%bHKkvI)9~;n$D$kG8^uslhZ&tPa3itUN$^#C~0`nP{B~#@S))yLjyx~ zLrX(_LuW&CLmxv2!%#y{L!4odA!3L&j5j13rW?i@78s@(RvErEY%;7g>^A&t_|ve< zaK`Y5;i}=3{}_f#24lEsc)*fs$Y#Ni%kq%r2}@4PbC!IT!j@+(r7VRkrLCo}rHf^#rH>`fGQ<+G#9GE#!j`F)v6k7EDVA?7Gc8Lj z^DQeai!JLcD=a@-ez0t_Y_#mQY_%M+?6MpW!r35Pvz+rE-g4dYfXfKkT}F7!WrU}K z@Vq0xqoCtC$E%K)9K{@k9i<#a9d9~HI?6jrJ1RNKI;sVswxg<}zQYeqg3#L0*wMk! z%F!(dy&YX0101~^Lmd4bBOHSragHcQk|WkJ(vj%+(h+e?48l~$1jh`=6vrILbjN(h zY{w$UH;(Te3mq#ROB`z*D;ygfYaE*#KRUJrVP_EbIsEXu!w*M-@K+E{JN$6L;fE_h zxEX}I4nG)2io^3Kpd$+z;bC|XazZx91CKyH$PM{}@JtY106)9}ekc-z5F_no1V7A%888QC!Q7zrtKZK1 zKUII%{9xCDAbbaYSPF|_B`kxrunN`(;in+@Kl=G$Q^pT~8P#t$2jPC*612YrbsPBM zSMbBmAnXppKJdeS@P95l7zF>vs{`=lS6i&#UWs4Lz^E=QZ=ZMxNKk^O}2J`yg}3RJ;ueay*_q@KIHy~&S2JN7r9qf62JJj?1 z5EX=&AS8HRtmh?po*zO%hy>yPR!0VVM|+-sz*x^4<#`i4?@Q17DhN}9FwOI(c;0l+ z``YvT>{oyDclnz??r)QW%|FG@e)Y%wk^5)O2o9bVoIcz0W_q4q{q}zKgFoVL{$c-J z{c(R}PH^NmL0I5&|6o6SA5=fz-~5sLPkaCI z{L}A$ocX~=n;U#~|0Cb$_33=ZiJmtpcq8LH&wo2(f)_M0c%jLj7Z<$WXwUOs`Y_KM z?s@*K7#vIzf2#P?rcW@VdIpoKYcS0^d0x9<*0uJ$ww~9*^ICac(_r>C^1LRV_t`%a zyMgD`3#R%HM1M*8;f-Kzmh!yUJ+EZ2T8jsvSg?Ly`)5)6p-8a0{l)HwLcw}}C0P3f zgZ5?5^V^sH`Eu~v7lZbNAUyw1_1ot>?|$_MpAEiG{OS)r6SVj1(?NT`7Vtbj+^zm_1j$kd@=fi ze)j95LAYOY2JOGq507}>zcokD{&&spd4Buf_2HoXAJuQOdES3lzrDX@4Z^?G5BIl+ zg79zk!+&pCg7)9)hyQ(hFc|!|`r-e!We#@zTmA6=WP2bu?BD8#|F>JF;FN!>AO62@ zUT~5BQT Date: Mon, 16 May 2022 04:22:07 +0800 Subject: [PATCH 157/232] Update zh.json --- stardew-access/i18n/zh.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stardew-access/i18n/zh.json b/stardew-access/i18n/zh.json index 89a24e2..49037b9 100644 --- a/stardew-access/i18n/zh.json +++ b/stardew-access/i18n/zh.json @@ -2,11 +2,11 @@ "warnings.health": "警告!健康状况为 {{value}} 百分!", "warnings.stamina": "警告!耐力为 {{value}} 百分!", "warnings.time": "警告!时间是 {{value}}", - "grandpastory.scene0":"爷爷,临终前。", + "grandpastory.scene0":"爷爷,去世前。", "grandpastory.scene4":"在 JoJa corp. 工作的员工", - "grandpastory.scene5":"员工在他们的隔间里,其中一些人看起来很累,包括你自己。", + "grandpastory.scene5":"员工在他们的办公室里,他们看起来很累,这其中也包括你自己。", "grandpastory.scene6":"你走到办公桌前,找到了爷爷的信。", - "grandpastory.letteropen":"左键打开爷爷的信", + "grandpastory.letteropen":"左方括号打开爷爷的信", "intro.scene3":"前往星露谷物语巴士站", "intro.scene4":"星露谷物语 0.5 英里外" -} \ No newline at end of file +} From 7b81660dd2bc0940d350cadc8a224ee21f3a5d37 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Sun, 15 May 2022 18:32:39 -0400 Subject: [PATCH 158/232] Add shipwreck entrance and exit to static tiles. --- stardew-access/assets/static-tiles.json | 4862 ++++++++++------------- 1 file changed, 2198 insertions(+), 2664 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 599bff2..cde98f3 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -1,2666 +1,2200 @@ { - "farm": - { - "Bus Stop Entrance": - { - "x":[79], - "y":[15,16,17,18], - "type":"door" - }, - "Backwoods Entrance": - { - "x":[40,41], - "y":[0], - "type":"door" - }, - "Cindersap Forest Entrance": - { - "x":[40,41], - "y":[64], - "type":"door" - }, - "Farm Cave Entrance": - { - "x":[34], - "y":[7], - "type":"door" - }, - "Grandpa's Shrine": - { - "x":[8], - "y":[7], - "type":"interactable" - }, - "Water bowl": - { - "x":[54], - "y":[7], - "type":"interactable" - } - }, - "farmcave": - { - "Exit": - { - "x":[8], - "y":[11], - "type":"door" - } - }, - "busstop": - { - "Ticket Machine": - { - "x":[7], - "y":[11], - "type":"interactable" - }, - "Minecart": - { - "x":[4,5], - "y":[3], - "type":"interactable" - }, - "Farm Entrance": - { - "x":[0], - "y":[23], - "type":"door" - }, - "Town Entrance": - { - "x":[34], - "y":[23], - "type":"door" - }, - "Backwoods Entrance": - { - "x":[0], - "y":[6,7,8,9], - "type":"door" - } - }, - "town": - { - "Calender Board": - { - "x":[41], - "y":[56], - "type":"interactable" - }, - "Daily Quest Board": - { - "x":[42], - "y":[56], - "type":"interactable" - }, - "Sewer": - { - "x":[34,35], - "y":[95,96], - "type":"interactable" - }, - "Ice Cream Stand": - { - "x":[88], - "y":[92], - "type":"interactable" - }, - "Minecart": - { - "x":[105,106], - "y":[79], - "type":"interactable" - }, - "Bus Stop Entrance": - { - "x":[0], - "y":[54], - "type":"door" - }, - "Cindersap Forest Entrance": - { - "x":[0], - "y":[90], - "type":"door" - }, - "Beach Entrance": - { - "x":[54], - "y":[109], - "type":"door" - }, - "Mountain Entrance": - { - "x":[81], - "y":[0], - "type":"door" - } - }, - "shed": - { - "Exit": - { - "x":[6], - "y":[13], - "type":"door" - } - }, - "coop": - { - "Hay Hopper": - { - "x":[3], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[7], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[2], - "y":[9], - "type":"door" - } - }, - "big coop": - { - "Hay Hopper": - { - "x":[3], - "y":[3], - "type":"interactable" - }, - "Incubator": - { - "x":[2], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[7], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[2], - "y":[9], - "type":"door" - } - }, - "coop2": - { - "Hay Hopper": - { - "x":[3], - "y":[3], - "type":"interactable" - }, - "Incubator": - { - "x":[2], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[7], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[2], - "y":[9], - "type":"door" - } - }, - "deluxe coop": - { - "Hay Hopper": - { - "x":[3], - "y":[3], - "type":"interactable" - }, - "Incubator": - { - "x":[2], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[7], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 9": - { - "x":[14], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 10": - { - "x":[15], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 11": - { - "x":[16], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 12": - { - "x":[17], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[2], - "y":[9], - "type":"door" - } - }, - "coop3": - { - "Hay Hopper": - { - "x":[3], - "y":[3], - "type":"interactable" - }, - "Incubator": - { - "x":[2], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[7], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 9": - { - "x":[14], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 10": - { - "x":[15], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 11": - { - "x":[16], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 12": - { - "x":[17], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[2], - "y":[9], - "type":"door" - } - }, - "barn": - { - "Hay Hopper": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[11], - "y":[14], - "type":"door" - } - }, - "barn2": - { - "Hay Hopper": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[14], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[15], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[11], - "y":[14], - "type":"door" - } - }, - "big barn": - { - "Hay Hopper": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[14], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[15], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[11], - "y":[14], - "type":"door" - } - }, - "barn3": - { - "Hay Hopper": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[14], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[15], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 9": - { - "x":[16], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 10": - { - "x":[17], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 11": - { - "x":[18], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 12": - { - "x":[19], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[11], - "y":[14], - "type":"door" - } - }, - "deluxe barn": - { - "Hay Hopper": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[14], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[15], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 9": - { - "x":[16], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 10": - { - "x":[17], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 11": - { - "x":[18], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 12": - { - "x":[19], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[11], - "y":[14], - "type":"door" - } - }, - "slime hutch": - { - "Water Trough 1": - { - "x":[16], - "y":[6], - "type":"interactable" - }, - "Water Trough 2": - { - "x":[16], - "y":[7], - "type":"interactable" - }, - "Water Trough 3": - { - "x":[16], - "y":[8], - "type":"interactable" - }, - "Water Trough 4": - { - "x":[16], - "y":[9], - "type":"interactable" - }, - "Exit": - { - "x":[8], - "y":[12], - "type":"door" - } - }, - "adventureguild": - { - "Goals Board": - { - "x":[8], - "y":[10], - "type":"interactable" - }, - "Shop Counter": - { - "x":[5], - "y":[12], - "type":"interactable" - }, - "Gil": - { - "x":[11], - "y":[12], - "type":"npc" - }, - "Exit": - { - "x":[6], - "y":[19], - "type":"door" - } - }, - "caldera": - { - "Rare Chest": - { - "x":[25], - "y":[28], - "type":"chest" - }, - "Forge": - { - "x":[23], - "y":[21], - "type":"interactable" - }, - "Volcano Dungeon 0 Entrance": - { - "x":[11], - "y":[36], - "type":"door" - }, - "Volcano Dungeon 9 Entrance": - { - "x":[21], - "y":[39], - "type":"door" - } - }, - "volcanodungeon0": - { - "Caldera Entrance": - { - "x":[44], - "y":[50], - "type":"door" - }, - "Volcano Dungeon 1 Entrance": - { - "x":[37], - "y":[5], - "type":"door" - } - }, - "club": - { - "Coin Machine": - { - "x":[12], - "y":[4], - "type":"interactable" - }, - "Shop Counter": - { - "x":[25], - "y":[3], - "type":"interactable" - }, - "Calico Spin Machine": - { - "x":[11,13,15], - "y":[8,10], - "type":"interactable" - }, - "High Stakes Calico Jack Table": - { - "x":[23,24], - "y":[10,11], - "type":"interactable" - }, - "Low Stakes Calico Jack Table": - { - "x":[3], - "y":[7,9], - "type":"interactable" - }, - "Man": - { - "x":[13], - "y":[11], - "type":"npc" - }, - "Welwick": - { - "x":[18], - "y":[9], - "type":"npc" - }, - "Unknown person": - { - "x":[16], - "y":[4], - "type":"npc" - }, - "Stats Checker": - { - "x":[3], - "y":[4], - "type":"interactable" - }, - "Exit": - { - "x":[8], - "y":[12], - "type":"door" - } - }, - "desert": - { - "Bus": - { - "x":[18], - "y":[27], - "type":"interactable" - }, - "Desert Trader": - { - "x":[42], - "y":[23], - "type":"interactable" - }, - "Three Pillars": - { - "x":[34,37,40], - "y":[8,13], - "type":"decoration" - }, - "Three Pillars Center": - { - "x":[37], - "y":[11], - "type":"interactable" - }, - "Skull Cavern Entrance": - { - "x":[8], - "y":[6], - "type":"door" - }, - "Desert Warp Statue": - { - "x":[35], - "y":[43], - "type":"decoration" - }, - "Sand Dragon Skull": - { - "x":[9,10], - "y":[35,36], - "type":"decoration" - } - }, - "fishshop": - { - "Shop Counter": - { - "x":[5], - "y":[5], - "type":"interactable" - }, - "Exit": - { - "x":[5], - "y":[9], - "type":"door" - } - }, - "boattunnel": - { - "Exit": - { - "x":[6], - "y":[11], - "type":"door" - } - }, - "beach": - { - "Town Entrance": - { - "x":[38], - "y":[0], - "type":"door" - }, - "Beach Warp Statue": - { - "x":[20], - "y":[4], - "type":"decoration" - } - }, - "forest": - { - "Farm Entrance": - { - "x":[68], - "y":[0], - "type":"door" - }, - "Town Entrance": - { - "x":[119], - "y":[25], - "type":"door" - }, - "Bridge 1": - { - "x":[77,82], - "y":[49], - "type":"bridge" - }, - "Bridge 2": - { - "x":[87], - "y":[52,56], - "type":"bridge" - }, - "Bridge 3": - { - "x":[65,62], - "y":[70], - "type":"bridge" - }, - "Bridge 4": - { - "x":[41], - "y":[79,82], - "type":"bridge" - }, - "Bridge 5": - { - "x":[38], - "y":[85,87], - "type":"bridge" - }, - "Abandoned House": - { - "x":[34], - "y":[95], - "type":"interactable" - } - }, - "beachnightmarket": - { - "Desert Trader": - { - "x":[14], - "y":[37], - "type":"npc" - }, - "Famous Painter Lupini": - { - "x":[43], - "y":[34], - "type":"npc" - }, - "Fishing Submarine Door": - { - "x":[5], - "y":[34], - "type":"door" - }, - "Travelling Cart": - { - "x":[39], - "y":[30], - "type":"interactable" - }, - "Shrouded Figure": - { - "x":[32], - "y":[34], - "type":"npc" - }, - "Decoration Boat": - { - "x":[19], - "y":[33], - "type":"interactable" - }, - "Magic Shop Boat": - { - "x":[48], - "y":[34], - "type":"interactable" - }, - "Mermaid Boat Door": - { - "x":[58], - "y":[31], - "type":"door" - } - }, - "mermaidhouse": - { - "Exit": - { - "x":[4], - "y":[10], - "type":"door" - }, - "Clam Shell 1": - { - "x":[2], - "y":[6], - "type":"interactable" - }, - "Clam Shell 2": - { - "x":[3], - "y":[6], - "type":"interactable" - }, - "Clam Shell 3": - { - "x":[4], - "y":[6], - "type":"interactable" - }, - "Clam Shell 4": - { - "x":[5], - "y":[6], - "type":"interactable" - }, - "Clam Shell 5": - { - "x":[6], - "y":[6], - "type":"interactable" - } - }, - "submarine": - { - "Exit": - { - "x":[14], - "y":[15], - "type":"door" - }, - "Captain": - { - "x":[2], - "y":[9], - "type":"npc" - } - }, - "cellar": - { - "Exit": - { - "x":[3], - "y":[2], - "type":"door" - } - }, - "communitycenter": - { - "Exit": - { - "x":[32], - "y":[23], - "type":"door" - } - }, - "islandeast": - { - "Banana Shrine": - { - "x":[16], - "y":[26], - "type":"interactable" - }, - "Jungle Parrot Express": - { - "x":[28], - "y":[27], - "type":"interactable" - }, - "Island Hut Entrance": - { - "x":[22], - "y":[10], - "type":"door" - }, - "Island South Entrance": - { - "x":[0], - "y":[46], - "type":"door" - }, - "Island Shrine Entrance": - { - "x":[32], - "y":[30], - "type":"door" - } - }, - "islandhut": - { - "Exit": - { - "x":[7], - "y":[13], - "type":"door" - } - }, - "islandsouth": - { - "Island East Entrance": - { - "x":[34], - "y":[12], - "type":"door" - }, - "Ginger Island Warp Statue": - { - "x":[11], - "y":[11], - "type":"decoration" - }, - "Island West Entrance": - { - "x":[0], - "y":[11], - "type":"door" - }, - "Island North Entrance": - { - "x":[17], - "y":[0], - "type":"door" - }, - "Docks Parrot Express": - { - "x":[6], - "y":[31], - "type":"interactable" - }, - "Return Boat": - { - "x":[19], - "y":[43], - "type":"interactable" - } - }, - "islandwest": - { - "Farm Parrot Express": - { - "x":[74], - "y":[9], - "type":"interactable" - }, - "Bridge 1": - { - "x":[67,62], - "y":[16], - "type":"bridge" - }, - "Qi's Walnut Room Door": - { - "x":[20], - "y":[22], - "type":"door" - }, - "Hole 1": - { - "x":[37], - "y":[87], - "type":"interactable" - }, - "Hole 2": - { - "x":[41], - "y":[86], - "type":"interactable" - }, - "Hole 3": - { - "x":[45], - "y":[86], - "type":"interactable" - }, - "Hole 4": - { - "x":[48], - "y":[87], - "type":"interactable" - }, - "Bridge 2": - { - "x":[55,52], - "y":[80], - "type":"bridge" - }, - "Island Farm House Door": - { - "x":[77], - "y":[39], - "type":"door" - }, - "Island Farm Cave Entrance": - { - "x":[96], - "y":[33], - "type":"door" - }, - "Island South Entrance": - { - "x":[105], - "y":[40], - "type":"door" - } - }, - "islandfarmcave": - { - "Exit": - { - "x":[4], - "y":[10], - "type":"door" - }, - "Gourmand Frog": - { - "x":[5], - "y":[4], - "type":"npc" - } - }, - "islandnorth": - { - "Island South Entrance": - { - "x":[35], - "y":[89], - "type":"door" - }, - "Island Field Office Entrance": - { - "x":[46], - "y":[46], - "type":"door" - }, - "Island North Cave Entrance": - { - "x":[21,22], - "y":[47], - "type":"door" - }, - "Dig Site Parrot Express": - { - "x":[5], - "y":[48], - "type":"interactable" - }, - "Volcano Dungeon Entrance": - { - "x":[40], - "y":[22], - "type":"door" - }, - "Volcano Parrot Express": - { - "x":[60], - "y":[16], - "type":"interactable" - } - }, - "islandfieldoffice": - { - "Exit": - { - "x":[4], - "y":[10], - "type":"door" - }, - "Counter": - { - "x":[8], - "y":[7], - "type":"interactable" - }, - "Island Survey": - { - "x":[5], - "y":[3], - "type":"interactable" - } - }, - "qinutroom": - { - "Exit": - { - "x":[7], - "y":[7], - "type":"door" - }, - "Perfection Tracker": - { - "x":[13], - "y":[4], - "type":"interactable" - }, - "Vending Machine": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Special Order Board": - { - "x":[3], - "y":[3], - "type":"interactable" - } - }, - "islandsoutheast": - { - "Island South East Cave Entrance": - { - "x":[30], - "y":[18], - "type":"door" - } - }, - "islandsoutheastcave": - { - "Exit": - { - "x":[1], - "y":[8], - "type":"door" - } - }, - "islandshrine": - { - "Exit": - { - "x":[13], - "y":[28], - "type":"door" - }, - "Shrine": - { - "x":[24], - "y":[22], - "type":"interactable" - }, - "North Pedestal": - { - "x":[24], - "y":[25], - "type":"interactable" - }, - "East Pedestal": - { - "x":[27], - "y":[27], - "type":"interactable" - }, - "West Pedestal": - { - "x":[21], - "y":[27], - "type":"interactable" - }, - "South Pedestal": - { - "x":[24], - "y":[28], - "type":"interactable" - } - }, - "jojamart": - { - "Exit": - { - "x":[13], - "y":[29], - "type":"door" - }, - "Morris's Kiosk": - { - "x":[21], - "y":[25], - "type":"interactable" - }, - "Shop Counter": - { - "x":[10], - "y":[25], - "type":"interactable" - } - }, - "archaeologyhouse": - { - "Exit": - { - "x":[3], - "y":[14], - "type":"door" - }, - "Counter": - { - "x":[3], - "y":[9], - "type":"interactable" - } - }, - "manorhouse": - { - "Exit": - { - "x":[4], - "y":[11], - "type":"door" - }, - "Town Ledger Book": - { - "x":[2], - "y":[5], - "type":"interactable" - }, - "Marriage Log Book": - { - "x":[3], - "y":[5], - "type":"interactable" - }, - "Lost and Found Box": - { - "x":[4], - "y":[5], - "type":"interactable" - }, - "Mayor's Room Door": - { - "x":[16], - "y":[9], - "type":"door" - }, - "Mayor's Oven": - { - "x":[7], - "y":[4], - "type":"decoration" - }, - "Mayor's Fridge": - { - "x":[9], - "y":[4], - "type":"decoration" - } - }, - "mine": - { - "Mountain Exit": - { - "x":[18], - "y":[13], - "type":"door" - }, - "Minecart": - { - "x":[11,12], - "y":[10], - "type":"interactable" - }, - "Quarry Mine Ladder": - { - "x":[67], - "y":[9], - "type":"door" - }, - "Quarry Exit": - { - "x":[18], - "y":[13], - "type":"door" - } - }, - "mountain": - { - "Mine Entrance": - { - "x":[54], - "y":[5], - "type":"door" - }, - "Mine Bridge": - { - "x":[47], - "y":[7], - "type":"bridge" - }, - "Quarry Bridge": - { - "x":[90], - "y":[26], - "type":"bridge" - }, - "Minecart": - { - "x":[124,125], - "y":[11], - "type":"interactable" - }, - "Quarry Mine Entrance": - { - "x":[103], - "y":[17], - "type":"door" - }, - "Bridge 1": - { - "x":[57], - "y":[30], - "type":"bridge" - }, - "Bridge 2": - { - "x":[61], - "y":[21], - "type":"bridge" - }, - "Mountain Warp Statue": - { - "x":[31], - "y":[20], - "type":"decoration" - }, - "Linus Tent Entrance": - { - "x":[29], - "y":[7], - "type":"door" - }, - "Backwoods Entrance": - { - "x":[0], - "y":[13], - "type":"door" - }, - "Town Entrance": - { - "x":[15], - "y":[40], - "type":"door" - }, - "Railroad Entrance": - { - "x":[9], - "y":[0], - "type":"door" - }, - "Science House Secondary Door": - { - "x":[8], - "y":[20], - "type":"door" - } - }, - "undergroundmine77377": - { - "Grim Reaper Statue": - { - "x":[29,30], - "y":[6], - "type":"interactable" - } - }, - "Tent": - { - "Exit": - { - "x":[2], - "y":[5], - "type":"door" - } - }, - "railroad": - { - "Mountain Entrance": - { - "x":[29], - "y":[61], - "type":"door" - } - }, - "backwoods": - { - "Mountain Entrance": - { - "x":[49], - "y":[14], - "type":"door" - }, - "Farm Entrance": - { - "x":[14], - "y":[39], - "type":"door" - }, - "Bus stop Entrance": - { - "x":[49], - "y":[28,29,30,31,32], - "type":"door" - }, - "Tunnel Entrance": - { - "x":[23], - "y":[29,30,31], - "type":"door" - } - }, - "tunnel": - { - "Exit": - { - "x":[39], - "y":[7,8,9,10,11], - "type":"door" - } - }, - "movietheater": - { - "Exit": - { - "x":[13], - "y":[15], - "type":"door" - }, - "Concessions Counter": - { - "x":[7], - "y":[6], - "type":"interactable" - }, - "Crane Game": - { - "x":[1,2], - "y":[8], - "type":"interactable" - }, - "Theater Door": - { - "x":[13], - "y":[3], - "type":"door" - }, - "Crane Man": - { - "x":[2], - "y":[9], - "type":"npc" - } - }, - "seedshop": - { - "Exit": - { - "x":[6], - "y":[29], - "type":"door" - }, - "Shop Counter": - { - "x":[4], - "y":[18], - "type":"interactable" - }, - "Backpack Upgrade": - { - "x":[7], - "y":[18], - "type":"interactable" - }, - "Shrine of Yoba": - { - "x":[37], - "y":[17], - "type":"decoration" - }, - "Fridge": - { - "x":[39], - "y":[4], - "type":"decoration" - }, - "Abigail's Room Door": - { - "x":[13], - "y":[11], - "type":"door" - }, - "Pierre and Caroline's Room Door": - { - "x":[20], - "y":[11], - "type":"door" - }, - "Living Area Door": - { - "x":[14], - "y":[16], - "type":"door" - } - }, - "sewer": - { - "Exit Ladder": - { - "x":[16], - "y":[10], - "type":"door" - }, - "Statue Of Uncertainty": - { - "x":[8], - "y":[20], - "type":"interactable" - }, - "Mutant Bug Lair": - { - "x":[3], - "y":[19], - "type":"door" - } - }, - "wizardhouse": - { - "Exit": - { - "x":[8], - "y":[24], - "type":"door" - }, - "Basement Door": - { - "x":[4], - "y":[4], - "type":"door" - } - }, - "wizardhousebasement": - { - "Exit Ladder": - { - "x":[4], - "y":[3], - "type":"door" - }, - "Shrine of Illusions": - { - "x":[12], - "y":[4], - "type":"interactable" - } - }, - "woods": - { - "Forest Entrance": - { - "x":[59], - "y":[17], - "type":"door" - }, - "Old Master Cannoli": - { - "x":[8,9], - "y":[7], - "type":"interactable" - } - }, - "harveyroom": - { - "Exit": - { - "x":[6], - "y":[12], - "type":"door" - }, - "Fridge": - { - "x":[21], - "y":[6], - "type":"decoration" - }, - "Oven": - { - "x":[19], - "y":[6], - "type":"decoration" - }, - "Airplane Collection": - { - "x":[6,7], - "y":[3], - "type":"decoration" - }, - "Radio Broadcasting Set": - { - "x":[4,5], - "y":[4], - "type":"decoration" - }, - "Cassette Deck": - { - "x":[8], - "y":[4], - "type":"decoration" - } - }, - "hospital": - { - "Exit": - { - "x":[10], - "y":[19], - "type":"door" - }, - "Harvey's Room Entrance": - { - "x":[10], - "y":[2], - "type":"door" - }, - "Harvey's Room Entrance Door": - { - "x":[10], - "y":[5], - "type":"door" - }, - "Main Area Door": - { - "x":[10], - "y":[13], - "type":"door" - }, - "Counter": - { - "x":[6], - "y":[16], - "type":"interactable" - } - }, - "blacksmith": - { - "Exit": - { - "x":[5], - "y":[19], - "type":"door" - }, - "Counter": - { - "x":[3], - "y":[14], - "type":"interactable" - }, - "Clint's Room Door": - { - "x":[4], - "y":[9], - "type":"door" - }, - "Clint's Furnace": - { - "x":[9,10], - "y":[12], - "type":"decoration" - }, - "Blueprints": - { - "x":[13], - "y":[15,16], - "type":"decoration" - }, - "Anvil": - { - "x":[12,13], - "y":[13], - "type":"decoration" - }, - "Cassette Deck": - { - "x":[2], - "y":[4], - "type":"decoration" - }, - "Clint's Drawer": - { - "x":[5,6], - "y":[4], - "type":"decoration" - } - }, - "animalshop": - { - "Exit": - { - "x":[13], - "y":[19], - "type":"door" - }, - "Counter": - { - "x":[12], - "y":[15], - "type":"interactable" - }, - "Marnie's Room Door": - { - "x":[15], - "y":[12], - "type":"door" - }, - "Jas's Room Door": - { - "x":[6], - "y":[13], - "type":"door" - }, - "Shane's Room Door": - { - "x":[21], - "y":[13], - "type":"door" - }, - "Marnie's Barn Door": - { - "x":[30], - "y":[13], - "type":"door" - }, - "Fridge": - { - "x":[28], - "y":[14], - "type":"decoration" - }, - "Oven": - { - "x":[24], - "y":[14], - "type":"decoration" - }, - "Mega Station": - { - "x":[22], - "y":[5], - "type":"decoration" - }, - "Shane's Radio": - { - "x":[24], - "y":[4], - "type":"decoration" - }, - "Marnie's Dresser": - { - "x":[16], - "y":[4], - "type":"decoration" - }, - "Marnie's Drawer": - { - "x":[17], - "y":[4], - "type":"decoration" - }, - "Jack in the Box": - { - "x":[8], - "y":[5], - "type":"decoration" - }, - "Futan Bear": - { - "x":[2,3], - "y":[4], - "type":"decoration" - }, - "Colouring Book": - { - "x":[5], - "y":[4], - "type":"decoration" - }, - "Paint Set": - { - "x":[5], - "y":[7], - "type":"decoration" - }, - "Jas's Alarm Clock": - { - "x":[8], - "y":[8], - "type":"decoration" - }, - "Jas's Radio": - { - "x":[4], - "y":[9], - "type":"decoration" - }, - "Arts And Craft": - { - "x":[7], - "y":[6], - "type":"decoration" - }, - "Doll House": - { - "x":[6,7], - "y":[4], - "type":"decoration" - } - }, - "saloon": - { - "Exit": - { - "x":[14], - "y":[24], - "type":"door" - }, - "Counter": - { - "x":[14], - "y":[19], - "type":"interactable" - }, - "Journey of the Prairie King Arcade": - { - "x":[33], - "y":[17], - "type":"interactable" - }, - "Junimo Kart Arcade": - { - "x":[35], - "y":[17], - "type":"interactable" - }, - "Joja Vending Machine": - { - "x":[37,38], - "y":[17], - "type":"interactable" - }, - "Jukebox": - { - "x":[1,2], - "y":[17], - "type":"interactable" - }, - "Gus's Room Door": - { - "x":[20], - "y":[9], - "type":"door" - }, - "Dining Room Door": - { - "x":[11], - "y":[9], - "type":"door" - }, - "Living Area Door": - { - "x":[4], - "y":[16], - "type":"door" - }, - "Gus's Radio": - { - "x":[16], - "y":[6], - "type":"decoration" - } - }, - "sciencehouse": - { - "Exit": - { - "x":[6], - "y":[24], - "type":"door" - }, - "Secondary Exit": - { - "x":[3], - "y":[8], - "type":"door" - }, - "Counter": - { - "x":[8], - "y":[19], - "type":"interactable" - }, - "Sebastian's Room Entrance": - { - "x":[12], - "y":[21], - "type":"door" - }, - "Beaker Set": - { - "x":[17], - "y":[17], - "type":"decoration" - }, - "Microscope": - { - "x":[19], - "y":[17], - "type":"decoration" - }, - "Stereo Microscope": - { - "x":[23], - "y":[20], - "type":"decoration" - }, - "Robin and Demetrius's Room Entrance": - { - "x":[13], - "y":[10], - "type":"door" - }, - "Maru's Room Entrance": - { - "x":[7], - "y":[10], - "type":"door" - }, - "Bookshelf": - { - "x":[16,17], - "y":[4], - "type":"decoration" - }, - "Maru's Device": - { - "x":[6], - "y":[6], - "type":"decoration" - }, - "Poster": - { - "x":[6], - "y":[3], - "type":"decoration" - }, - "Computer": - { - "x":[9], - "y":[4], - "type":"decoration" - }, - "Fridge": - { - "x":[27], - "y":[8], - "type":"decoration" - }, - "Oven": - { - "x":[30], - "y":[10], - "type":"decoration" - } - }, - "sebastianroom": - { - "Exit": - { - "x":[1], - "y":[1], - "type":"door" - }, - "Room Door": - { - "x":[1], - "y":[3], - "type":"door" - }, - "Sebastian's Radio": - { - "x":[3], - "y":[4], - "type":"decoration" - }, - "Graphic Novel": - { - "x":[10], - "y":[6], - "type":"decoration" - }, - "Computer": - { - "x":[7], - "y":[4], - "type":"decoration" - } - }, - "samhouse": - { - "Exit": - { - "x":[4], - "y":[23], - "type":"door" - }, - "Radio": - { - "x":[6], - "y":[12], - "type":"decoration" - }, - "Vincent's Room Door": - { - "x":[16], - "y":[18], - "type":"door" - }, - "Sam's Room Door": - { - "x":[12], - "y":[14], - "type":"door" - }, - "Jodi's Room Door": - { - "x":[17], - "y":[6], - "type":"door" - }, - "Sam's Drawer": - { - "x":[7,8], - "y":[12], - "type":"decoration" - }, - "Bookshelf": - { - "x":[18,19], - "y":[12], - "type":"decoration" - }, - "Fridge": - { - "x":[7], - "y":[4], - "type":"decoration" - } - }, - "haleyhouse": - { - "Exit": - { - "x":[2], - "y":[24], - "type":"door" - }, - "Sewing Machine": - { - "x":[12,13], - "y":[23], - "type":"interactable" - }, - "Dye Pots": - { - "x":[17], - "y":[25], - "type":"interactable" - }, - "Emily's Room Door": - { - "x":[16], - "y":[12], - "type":"door" - }, - "Emily's Computer": - { - "x":[22], - "y":[6], - "type":"decoration" - }, - "Emily's Pet Parrot": - { - "x":[14], - "y":[4], - "type":"decoration" - }, - "Magazine": - { - "x":[4], - "y":[22], - "type":"decoration" - }, - "Globe": - { - "x":[8], - "y":[15], - "type":"decoration" - }, - "Fridge": - { - "x":[21], - "y":[15], - "type":"decoration" - }, - "Haley's Room Door": - { - "x":[5], - "y":[13], - "type":"door" - }, - "Futan Bear": - { - "x":[8], - "y":[4], - "type":"decoration" - }, - "Diary": - { - "x":[9], - "y":[5], - "type":"decoration" - }, - "Haley's Camera": - { - "x":[1], - "y":[9], - "type":"decoration" - } - }, - "joshhouse": - { - "Exit": - { - "x":[9], - "y":[24], - "type":"door" - }, - "TV": - { - "x":[15,16], - "y":[20], - "type":"decoration" - }, - "Bookshelf": - { - "x":[17,18], - "y":[16], - "type":"decoration" - }, - "Fridge": - { - "x":[5], - "y":[16], - "type":"decoration" - }, - "Evelyn and George's Room Door": - { - "x":[5], - "y":[9], - "type":"door" - }, - "Alex's Room Door": - { - "x":[10], - "y":[10], - "type":"door" - }, - "Radio": - { - "x":[3], - "y":[4], - "type":"door" - }, - "Magazine": - { - "x":[11], - "y":[4], - "type":"door" - }, - "Alex's Bookshelf": - { - "x":[12,13], - "y":[4], - "type":"decoration" - }, - "Alex's Drawer": - { - "x":[17,18], - "y":[4], - "type":"decoration" - }, - "Dumbbell": - { - "x":[14], - "y":[4], - "type":"door" - }, - "Gridball": - { - "x":[23], - "y":[45], - "type":"door" - }, - "Gridball Helmet": - { - "x":[23], - "y":[6], - "type":"door" - } - }, - "trailer": - { - "Exit": - { - "x":[12], - "y":[9], - "type":"door" - }, - "Penny's Room Door": - { - "x":[6], - "y":[7], - "type":"door" - }, - "Bookshelf": - { - "x":[5,6], - "y":[4], - "type":"decoration" - }, - "Book": - { - "x":[2], - "y":[4], - "type":"decoration" - }, - "Magazine": - { - "x":[1], - "y":[9], - "type":"decoration" - } + "farm": { + "Bus Stop Entrance": { + "x": [ 79 ], + "y": [ 15, 16, 17, 18 ], + "type": "door" + }, + "Backwoods Entrance": { + "x": [ 40, 41 ], + "y": [ 0 ], + "type": "door" + }, + "Cindersap Forest Entrance": { + "x": [ 40, 41 ], + "y": [ 64 ], + "type": "door" + }, + "Farm Cave Entrance": { + "x": [ 34 ], + "y": [ 7 ], + "type": "door" + }, + "Grandpa's Shrine": { + "x": [ 8 ], + "y": [ 7 ], + "type": "interactable" + }, + "Water bowl": { + "x": [ 54 ], + "y": [ 7 ], + "type": "interactable" } -} \ No newline at end of file + }, + "farmcave": { + "Exit": { + "x": [ 8 ], + "y": [ 11 ], + "type": "door" + } + }, + "busstop": { + "Ticket Machine": { + "x": [ 7 ], + "y": [ 11 ], + "type": "interactable" + }, + "Minecart": { + "x": [ 4, 5 ], + "y": [ 3 ], + "type": "interactable" + }, + "Farm Entrance": { + "x": [ 0 ], + "y": [ 23 ], + "type": "door" + }, + "Town Entrance": { + "x": [ 34 ], + "y": [ 23 ], + "type": "door" + }, + "Backwoods Entrance": { + "x": [ 0 ], + "y": [ 6, 7, 8, 9 ], + "type": "door" + } + }, + "town": { + "Calender Board": { + "x": [ 41 ], + "y": [ 56 ], + "type": "interactable" + }, + "Daily Quest Board": { + "x": [ 42 ], + "y": [ 56 ], + "type": "interactable" + }, + "Sewer": { + "x": [ 34, 35 ], + "y": [ 95, 96 ], + "type": "interactable" + }, + "Ice Cream Stand": { + "x": [ 88 ], + "y": [ 92 ], + "type": "interactable" + }, + "Minecart": { + "x": [ 105, 106 ], + "y": [ 79 ], + "type": "interactable" + }, + "Bus Stop Entrance": { + "x": [ 0 ], + "y": [ 54 ], + "type": "door" + }, + "Cindersap Forest Entrance": { + "x": [ 0 ], + "y": [ 90 ], + "type": "door" + }, + "Beach Entrance": { + "x": [ 54 ], + "y": [ 109 ], + "type": "door" + }, + "Mountain Entrance": { + "x": [ 81 ], + "y": [ 0 ], + "type": "door" + } + }, + "shed": { + "Exit": { + "x": [ 6 ], + "y": [ 13 ], + "type": "door" + } + }, + "coop": { + "Hay Hopper": { + "x": [ 3 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [ 6 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 2": { + "x": [ 7 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 3": { + "x": [ 8 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 4": { + "x": [ 9 ], + "y": [ 3 ], + "type": "interactable" + }, + "Exit": { + "x": [ 2 ], + "y": [ 9 ], + "type": "door" + } + }, + "big coop": { + "Hay Hopper": { + "x": [ 3 ], + "y": [ 3 ], + "type": "interactable" + }, + "Incubator": { + "x": [ 2 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [ 6 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 2": { + "x": [ 7 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 3": { + "x": [ 8 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 4": { + "x": [ 9 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 5": { + "x": [ 10 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 6": { + "x": [ 11 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 7": { + "x": [ 12 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 8": { + "x": [ 13 ], + "y": [ 3 ], + "type": "interactable" + }, + "Exit": { + "x": [ 2 ], + "y": [ 9 ], + "type": "door" + } + }, + "coop2": { + "Hay Hopper": { + "x": [ 3 ], + "y": [ 3 ], + "type": "interactable" + }, + "Incubator": { + "x": [ 2 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [ 6 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 2": { + "x": [ 7 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 3": { + "x": [ 8 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 4": { + "x": [ 9 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 5": { + "x": [ 10 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 6": { + "x": [ 11 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 7": { + "x": [ 12 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 8": { + "x": [ 13 ], + "y": [ 3 ], + "type": "interactable" + }, + "Exit": { + "x": [ 2 ], + "y": [ 9 ], + "type": "door" + } + }, + "deluxe coop": { + "Hay Hopper": { + "x": [ 3 ], + "y": [ 3 ], + "type": "interactable" + }, + "Incubator": { + "x": [ 2 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [ 6 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 2": { + "x": [ 7 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 3": { + "x": [ 8 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 4": { + "x": [ 9 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 5": { + "x": [ 10 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 6": { + "x": [ 11 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 7": { + "x": [ 12 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 8": { + "x": [ 13 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 9": { + "x": [ 14 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 10": { + "x": [ 15 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 11": { + "x": [ 16 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 12": { + "x": [ 17 ], + "y": [ 3 ], + "type": "interactable" + }, + "Exit": { + "x": [ 2 ], + "y": [ 9 ], + "type": "door" + } + }, + "coop3": { + "Hay Hopper": { + "x": [ 3 ], + "y": [ 3 ], + "type": "interactable" + }, + "Incubator": { + "x": [ 2 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [ 6 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 2": { + "x": [ 7 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 3": { + "x": [ 8 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 4": { + "x": [ 9 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 5": { + "x": [ 10 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 6": { + "x": [ 11 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 7": { + "x": [ 12 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 8": { + "x": [ 13 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 9": { + "x": [ 14 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 10": { + "x": [ 15 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 11": { + "x": [ 16 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 12": { + "x": [ 17 ], + "y": [ 3 ], + "type": "interactable" + }, + "Exit": { + "x": [ 2 ], + "y": [ 9 ], + "type": "door" + } + }, + "barn": { + "Hay Hopper": { + "x": [ 6 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [ 8 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 2": { + "x": [ 9 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 3": { + "x": [ 10 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 4": { + "x": [ 11 ], + "y": [ 3 ], + "type": "interactable" + }, + "Exit": { + "x": [ 11 ], + "y": [ 14 ], + "type": "door" + } + }, + "barn2": { + "Hay Hopper": { + "x": [ 6 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [ 8 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 2": { + "x": [ 9 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 3": { + "x": [ 10 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 4": { + "x": [ 11 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 5": { + "x": [ 12 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 6": { + "x": [ 13 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 7": { + "x": [ 14 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 8": { + "x": [ 15 ], + "y": [ 3 ], + "type": "interactable" + }, + "Exit": { + "x": [ 11 ], + "y": [ 14 ], + "type": "door" + } + }, + "big barn": { + "Hay Hopper": { + "x": [ 6 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [ 8 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 2": { + "x": [ 9 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 3": { + "x": [ 10 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 4": { + "x": [ 11 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 5": { + "x": [ 12 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 6": { + "x": [ 13 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 7": { + "x": [ 14 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 8": { + "x": [ 15 ], + "y": [ 3 ], + "type": "interactable" + }, + "Exit": { + "x": [ 11 ], + "y": [ 14 ], + "type": "door" + } + }, + "barn3": { + "Hay Hopper": { + "x": [ 6 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [ 8 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 2": { + "x": [ 9 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 3": { + "x": [ 10 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 4": { + "x": [ 11 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 5": { + "x": [ 12 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 6": { + "x": [ 13 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 7": { + "x": [ 14 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 8": { + "x": [ 15 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 9": { + "x": [ 16 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 10": { + "x": [ 17 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 11": { + "x": [ 18 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 12": { + "x": [ 19 ], + "y": [ 3 ], + "type": "interactable" + }, + "Exit": { + "x": [ 11 ], + "y": [ 14 ], + "type": "door" + } + }, + "deluxe barn": { + "Hay Hopper": { + "x": [ 6 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [ 8 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 2": { + "x": [ 9 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 3": { + "x": [ 10 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 4": { + "x": [ 11 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 5": { + "x": [ 12 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 6": { + "x": [ 13 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 7": { + "x": [ 14 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 8": { + "x": [ 15 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 9": { + "x": [ 16 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 10": { + "x": [ 17 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 11": { + "x": [ 18 ], + "y": [ 3 ], + "type": "interactable" + }, + "Feeding Bench 12": { + "x": [ 19 ], + "y": [ 3 ], + "type": "interactable" + }, + "Exit": { + "x": [ 11 ], + "y": [ 14 ], + "type": "door" + } + }, + "slime hutch": { + "Water Trough 1": { + "x": [ 16 ], + "y": [ 6 ], + "type": "interactable" + }, + "Water Trough 2": { + "x": [ 16 ], + "y": [ 7 ], + "type": "interactable" + }, + "Water Trough 3": { + "x": [ 16 ], + "y": [ 8 ], + "type": "interactable" + }, + "Water Trough 4": { + "x": [ 16 ], + "y": [ 9 ], + "type": "interactable" + }, + "Exit": { + "x": [ 8 ], + "y": [ 12 ], + "type": "door" + } + }, + "adventureguild": { + "Goals Board": { + "x": [ 8 ], + "y": [ 10 ], + "type": "interactable" + }, + "Shop Counter": { + "x": [ 5 ], + "y": [ 12 ], + "type": "interactable" + }, + "Gil": { + "x": [ 11 ], + "y": [ 12 ], + "type": "npc" + }, + "Exit": { + "x": [ 6 ], + "y": [ 19 ], + "type": "door" + } + }, + "caldera": { + "Rare Chest": { + "x": [ 25 ], + "y": [ 28 ], + "type": "chest" + }, + "Forge": { + "x": [ 23 ], + "y": [ 21 ], + "type": "interactable" + }, + "Volcano Dungeon 0 Entrance": { + "x": [ 11 ], + "y": [ 36 ], + "type": "door" + }, + "Volcano Dungeon 9 Entrance": { + "x": [ 21 ], + "y": [ 39 ], + "type": "door" + } + }, + "volcanodungeon0": { + "Caldera Entrance": { + "x": [ 44 ], + "y": [ 50 ], + "type": "door" + }, + "Volcano Dungeon 1 Entrance": { + "x": [ 37 ], + "y": [ 5 ], + "type": "door" + } + }, + "club": { + "Coin Machine": { + "x": [ 12 ], + "y": [ 4 ], + "type": "interactable" + }, + "Shop Counter": { + "x": [ 25 ], + "y": [ 3 ], + "type": "interactable" + }, + "Calico Spin Machine": { + "x": [ 11, 13, 15 ], + "y": [ 8, 10 ], + "type": "interactable" + }, + "High Stakes Calico Jack Table": { + "x": [ 23, 24 ], + "y": [ 10, 11 ], + "type": "interactable" + }, + "Low Stakes Calico Jack Table": { + "x": [ 3 ], + "y": [ 7, 9 ], + "type": "interactable" + }, + "Man": { + "x": [ 13 ], + "y": [ 11 ], + "type": "npc" + }, + "Welwick": { + "x": [ 18 ], + "y": [ 9 ], + "type": "npc" + }, + "Unknown person": { + "x": [ 16 ], + "y": [ 4 ], + "type": "npc" + }, + "Stats Checker": { + "x": [ 3 ], + "y": [ 4 ], + "type": "interactable" + }, + "Exit": { + "x": [ 8 ], + "y": [ 12 ], + "type": "door" + } + }, + "desert": { + "Bus": { + "x": [ 18 ], + "y": [ 27 ], + "type": "interactable" + }, + "Desert Trader": { + "x": [ 42 ], + "y": [ 23 ], + "type": "interactable" + }, + "Three Pillars": { + "x": [ 34, 37, 40 ], + "y": [ 8, 13 ], + "type": "decoration" + }, + "Three Pillars Center": { + "x": [ 37 ], + "y": [ 11 ], + "type": "interactable" + }, + "Skull Cavern Entrance": { + "x": [ 8 ], + "y": [ 6 ], + "type": "door" + }, + "Desert Warp Statue": { + "x": [ 35 ], + "y": [ 43 ], + "type": "decoration" + }, + "Sand Dragon Skull": { + "x": [ 9, 10 ], + "y": [ 35, 36 ], + "type": "decoration" + } + }, + "fishshop": { + "Shop Counter": { + "x": [ 5 ], + "y": [ 5 ], + "type": "interactable" + }, + "Exit": { + "x": [ 5 ], + "y": [ 9 ], + "type": "door" + } + }, + "boattunnel": { + "Exit": { + "x": [ 6 ], + "y": [ 11 ], + "type": "door" + } + }, + "beach": { + "Town Entrance": { + "x": [ 38 ], + "y": [ 0 ], + "type": "door" + }, + "Beach Warp Statue": { + "x": [ 20 ], + "y": [ 4 ], + "type": "decoration" + } + }, + "forest": { + "Farm Entrance": { + "x": [ 68 ], + "y": [ 0 ], + "type": "door" + }, + "Town Entrance": { + "x": [ 119 ], + "y": [ 25 ], + "type": "door" + }, + "Bridge 1": { + "x": [ 77, 82 ], + "y": [ 49 ], + "type": "bridge" + }, + "Bridge 2": { + "x": [ 87 ], + "y": [ 52, 56 ], + "type": "bridge" + }, + "Bridge 3": { + "x": [ 65, 62 ], + "y": [ 70 ], + "type": "bridge" + }, + "Bridge 4": { + "x": [ 41 ], + "y": [ 79, 82 ], + "type": "bridge" + }, + "Bridge 5": { + "x": [ 38 ], + "y": [ 85, 87 ], + "type": "bridge" + }, + "Abandoned House": { + "x": [ 34 ], + "y": [ 95 ], + "type": "interactable" + } + }, + "beachnightmarket": { + "Desert Trader": { + "x": [ 14 ], + "y": [ 37 ], + "type": "npc" + }, + "Famous Painter Lupini": { + "x": [ 43 ], + "y": [ 34 ], + "type": "npc" + }, + "Fishing Submarine Door": { + "x": [ 5 ], + "y": [ 34 ], + "type": "door" + }, + "Travelling Cart": { + "x": [ 39 ], + "y": [ 30 ], + "type": "interactable" + }, + "Shrouded Figure": { + "x": [ 32 ], + "y": [ 34 ], + "type": "npc" + }, + "Decoration Boat": { + "x": [ 19 ], + "y": [ 33 ], + "type": "interactable" + }, + "Magic Shop Boat": { + "x": [ 48 ], + "y": [ 34 ], + "type": "interactable" + }, + "Mermaid Boat Door": { + "x": [ 58 ], + "y": [ 31 ], + "type": "door" + } + }, + "mermaidhouse": { + "Exit": { + "x": [ 4 ], + "y": [ 10 ], + "type": "door" + }, + "Clam Shell 1": { + "x": [ 2 ], + "y": [ 6 ], + "type": "interactable" + }, + "Clam Shell 2": { + "x": [ 3 ], + "y": [ 6 ], + "type": "interactable" + }, + "Clam Shell 3": { + "x": [ 4 ], + "y": [ 6 ], + "type": "interactable" + }, + "Clam Shell 4": { + "x": [ 5 ], + "y": [ 6 ], + "type": "interactable" + }, + "Clam Shell 5": { + "x": [ 6 ], + "y": [ 6 ], + "type": "interactable" + } + }, + "submarine": { + "Exit": { + "x": [ 14 ], + "y": [ 15 ], + "type": "door" + }, + "Captain": { + "x": [ 2 ], + "y": [ 9 ], + "type": "npc" + } + }, + "cellar": { + "Exit": { + "x": [ 3 ], + "y": [ 2 ], + "type": "door" + } + }, + "communitycenter": { + "Exit": { + "x": [ 32 ], + "y": [ 23 ], + "type": "door" + } + }, + "islandeast": { + "Banana Shrine": { + "x": [ 16 ], + "y": [ 26 ], + "type": "interactable" + }, + "Jungle Parrot Express": { + "x": [ 28 ], + "y": [ 27 ], + "type": "interactable" + }, + "Island Hut Entrance": { + "x": [ 22 ], + "y": [ 10 ], + "type": "door" + }, + "Island South Entrance": { + "x": [ 0 ], + "y": [ 46 ], + "type": "door" + }, + "Island Shrine Entrance": { + "x": [ 32 ], + "y": [ 30 ], + "type": "door" + } + }, + "islandhut": { + "Exit": { + "x": [ 7 ], + "y": [ 13 ], + "type": "door" + } + }, + "islandsouth": { + "Island East Entrance": { + "x": [ 34 ], + "y": [ 12 ], + "type": "door" + }, + "Ginger Island Warp Statue": { + "x": [ 11 ], + "y": [ 11 ], + "type": "decoration" + }, + "Island West Entrance": { + "x": [ 0 ], + "y": [ 11 ], + "type": "door" + }, + "Island North Entrance": { + "x": [ 17 ], + "y": [ 0 ], + "type": "door" + }, + "Docks Parrot Express": { + "x": [ 6 ], + "y": [ 31 ], + "type": "interactable" + }, + "Return Boat": { + "x": [ 19 ], + "y": [ 43 ], + "type": "interactable" + } + }, + "islandwest": { + "Farm Parrot Express": { + "x": [ 74 ], + "y": [ 9 ], + "type": "interactable" + }, + "Bridge 1": { + "x": [ 67, 62 ], + "y": [ 16 ], + "type": "bridge" + }, + "Qi's Walnut Room Door": { + "x": [ 20 ], + "y": [ 22 ], + "type": "door" + }, + "Door to Shipwreck interior": { + "x": [ 60 ], + "y": [ 92 ], + "type": "door" + }, + "Hole 1": { + "x": [ 37 ], + "y": [ 87 ], + "type": "interactable" + }, + "Hole 2": { + "x": [ 41 ], + "y": [ 86 ], + "type": "interactable" + }, + "Hole 3": { + "x": [ 45 ], + "y": [ 86 ], + "type": "interactable" + }, + "Hole 4": { + "x": [ 48 ], + "y": [ 87 ], + "type": "interactable" + }, + "Bridge 2": { + "x": [ 55, 52 ], + "y": [ 80 ], + "type": "bridge" + }, + "Island Farm House Door": { + "x": [ 77 ], + "y": [ 39 ], + "type": "door" + }, + "Island Farm Cave Entrance": { + "x": [ 96 ], + "y": [ 33 ], + "type": "door" + }, + "Island South Entrance": { + "x": [ 105 ], + "y": [ 40 ], + "type": "door" + } + }, + "captainroom": { + "exit": { + "x": [ 0 ], + "y": [ 5 ], + "type": "door" + } + }, + "islandfarmcave": { + "Exit": { + "x": [ 4 ], + "y": [ 10 ], + "type": "door" + }, + "Gourmand Frog": { + "x": [ 5 ], + "y": [ 4 ], + "type": "npc" + } + }, + "islandnorth": { + "Island South Entrance": { + "x": [ 35 ], + "y": [ 89 ], + "type": "door" + }, + "Island Field Office Entrance": { + "x": [ 46 ], + "y": [ 46 ], + "type": "door" + }, + "Island North Cave Entrance": { + "x": [ 21, 22 ], + "y": [ 47 ], + "type": "door" + }, + "Dig Site Parrot Express": { + "x": [ 5 ], + "y": [ 48 ], + "type": "interactable" + }, + "Volcano Dungeon Entrance": { + "x": [ 40 ], + "y": [ 22 ], + "type": "door" + }, + "Volcano Parrot Express": { + "x": [ 60 ], + "y": [ 16 ], + "type": "interactable" + } + }, + "islandfieldoffice": { + "Exit": { + "x": [ 4 ], + "y": [ 10 ], + "type": "door" + }, + "Counter": { + "x": [ 8 ], + "y": [ 7 ], + "type": "interactable" + }, + "Island Survey": { + "x": [ 5 ], + "y": [ 3 ], + "type": "interactable" + } + }, + "qinutroom": { + "Exit": { + "x": [ 7 ], + "y": [ 7 ], + "type": "door" + }, + "Perfection Tracker": { + "x": [ 13 ], + "y": [ 4 ], + "type": "interactable" + }, + "Vending Machine": { + "x": [ 11 ], + "y": [ 3 ], + "type": "interactable" + }, + "Special Order Board": { + "x": [ 3 ], + "y": [ 3 ], + "type": "interactable" + } + }, + "islandsoutheast": { + "Island South East Cave Entrance": { + "x": [ 30 ], + "y": [ 18 ], + "type": "door" + } + }, + "islandsoutheastcave": { + "Exit": { + "x": [ 1 ], + "y": [ 8 ], + "type": "door" + } + }, + "islandshrine": { + "Exit": { + "x": [ 13 ], + "y": [ 28 ], + "type": "door" + }, + "Shrine": { + "x": [ 24 ], + "y": [ 22 ], + "type": "interactable" + }, + "North Pedestal": { + "x": [ 24 ], + "y": [ 25 ], + "type": "interactable" + }, + "East Pedestal": { + "x": [ 27 ], + "y": [ 27 ], + "type": "interactable" + }, + "West Pedestal": { + "x": [ 21 ], + "y": [ 27 ], + "type": "interactable" + }, + "South Pedestal": { + "x": [ 24 ], + "y": [ 28 ], + "type": "interactable" + } + }, + "jojamart": { + "Exit": { + "x": [ 13 ], + "y": [ 29 ], + "type": "door" + }, + "Morris's Kiosk": { + "x": [ 21 ], + "y": [ 25 ], + "type": "interactable" + }, + "Shop Counter": { + "x": [ 10 ], + "y": [ 25 ], + "type": "interactable" + } + }, + "archaeologyhouse": { + "Exit": { + "x": [ 3 ], + "y": [ 14 ], + "type": "door" + }, + "Counter": { + "x": [ 3 ], + "y": [ 9 ], + "type": "interactable" + } + }, + "manorhouse": { + "Exit": { + "x": [ 4 ], + "y": [ 11 ], + "type": "door" + }, + "Town Ledger Book": { + "x": [ 2 ], + "y": [ 5 ], + "type": "interactable" + }, + "Marriage Log Book": { + "x": [ 3 ], + "y": [ 5 ], + "type": "interactable" + }, + "Lost and Found Box": { + "x": [ 4 ], + "y": [ 5 ], + "type": "interactable" + }, + "Mayor's Room Door": { + "x": [ 16 ], + "y": [ 9 ], + "type": "door" + }, + "Mayor's Oven": { + "x": [ 7 ], + "y": [ 4 ], + "type": "decoration" + }, + "Mayor's Fridge": { + "x": [ 9 ], + "y": [ 4 ], + "type": "decoration" + } + }, + "mine": { + "Mountain Exit": { + "x": [ 18 ], + "y": [ 13 ], + "type": "door" + }, + "Minecart": { + "x": [ 11, 12 ], + "y": [ 10 ], + "type": "interactable" + }, + "Quarry Mine Ladder": { + "x": [ 67 ], + "y": [ 9 ], + "type": "door" + }, + "Quarry Exit": { + "x": [ 18 ], + "y": [ 13 ], + "type": "door" + } + }, + "mountain": { + "Mine Entrance": { + "x": [ 54 ], + "y": [ 5 ], + "type": "door" + }, + "Mine Bridge": { + "x": [ 47 ], + "y": [ 7 ], + "type": "bridge" + }, + "Quarry Bridge": { + "x": [ 90 ], + "y": [ 26 ], + "type": "bridge" + }, + "Minecart": { + "x": [ 124, 125 ], + "y": [ 11 ], + "type": "interactable" + }, + "Quarry Mine Entrance": { + "x": [ 103 ], + "y": [ 17 ], + "type": "door" + }, + "Bridge 1": { + "x": [ 57 ], + "y": [ 30 ], + "type": "bridge" + }, + "Bridge 2": { + "x": [ 61 ], + "y": [ 21 ], + "type": "bridge" + }, + "Mountain Warp Statue": { + "x": [ 31 ], + "y": [ 20 ], + "type": "decoration" + }, + "Linus Tent Entrance": { + "x": [ 29 ], + "y": [ 7 ], + "type": "door" + }, + "Backwoods Entrance": { + "x": [ 0 ], + "y": [ 13 ], + "type": "door" + }, + "Town Entrance": { + "x": [ 15 ], + "y": [ 40 ], + "type": "door" + }, + "Railroad Entrance": { + "x": [ 9 ], + "y": [ 0 ], + "type": "door" + }, + "Science House Secondary Door": { + "x": [ 8 ], + "y": [ 20 ], + "type": "door" + } + }, + "undergroundmine77377": { + "Grim Reaper Statue": { + "x": [ 29, 30 ], + "y": [ 6 ], + "type": "interactable" + } + }, + "Tent": { + "Exit": { + "x": [ 2 ], + "y": [ 5 ], + "type": "door" + } + }, + "railroad": { + "Mountain Entrance": { + "x": [ 29 ], + "y": [ 61 ], + "type": "door" + } + }, + "backwoods": { + "Mountain Entrance": { + "x": [ 49 ], + "y": [ 14 ], + "type": "door" + }, + "Farm Entrance": { + "x": [ 14 ], + "y": [ 39 ], + "type": "door" + }, + "Bus stop Entrance": { + "x": [ 49 ], + "y": [ 28, 29, 30, 31, 32 ], + "type": "door" + }, + "Tunnel Entrance": { + "x": [ 23 ], + "y": [ 29, 30, 31 ], + "type": "door" + } + }, + "tunnel": { + "Exit": { + "x": [ 39 ], + "y": [ 7, 8, 9, 10, 11 ], + "type": "door" + } + }, + "movietheater": { + "Exit": { + "x": [ 13 ], + "y": [ 15 ], + "type": "door" + }, + "Concessions Counter": { + "x": [ 7 ], + "y": [ 6 ], + "type": "interactable" + }, + "Crane Game": { + "x": [ 1, 2 ], + "y": [ 8 ], + "type": "interactable" + }, + "Theater Door": { + "x": [ 13 ], + "y": [ 3 ], + "type": "door" + }, + "Crane Man": { + "x": [ 2 ], + "y": [ 9 ], + "type": "npc" + } + }, + "seedshop": { + "Exit": { + "x": [ 6 ], + "y": [ 29 ], + "type": "door" + }, + "Shop Counter": { + "x": [ 4 ], + "y": [ 18 ], + "type": "interactable" + }, + "Backpack Upgrade": { + "x": [ 7 ], + "y": [ 18 ], + "type": "interactable" + }, + "Shrine of Yoba": { + "x": [ 37 ], + "y": [ 17 ], + "type": "decoration" + }, + "Fridge": { + "x": [ 39 ], + "y": [ 4 ], + "type": "decoration" + }, + "Abigail's Room Door": { + "x": [ 13 ], + "y": [ 11 ], + "type": "door" + }, + "Pierre and Caroline's Room Door": { + "x": [ 20 ], + "y": [ 11 ], + "type": "door" + }, + "Living Area Door": { + "x": [ 14 ], + "y": [ 16 ], + "type": "door" + } + }, + "sewer": { + "Exit Ladder": { + "x": [ 16 ], + "y": [ 10 ], + "type": "door" + }, + "Statue Of Uncertainty": { + "x": [ 8 ], + "y": [ 20 ], + "type": "interactable" + }, + "Mutant Bug Lair": { + "x": [ 3 ], + "y": [ 19 ], + "type": "door" + } + }, + "wizardhouse": { + "Exit": { + "x": [ 8 ], + "y": [ 24 ], + "type": "door" + }, + "Basement Door": { + "x": [ 4 ], + "y": [ 4 ], + "type": "door" + } + }, + "wizardhousebasement": { + "Exit Ladder": { + "x": [ 4 ], + "y": [ 3 ], + "type": "door" + }, + "Shrine of Illusions": { + "x": [ 12 ], + "y": [ 4 ], + "type": "interactable" + } + }, + "woods": { + "Forest Entrance": { + "x": [ 59 ], + "y": [ 17 ], + "type": "door" + }, + "Old Master Cannoli": { + "x": [ 8, 9 ], + "y": [ 7 ], + "type": "interactable" + } + }, + "harveyroom": { + "Exit": { + "x": [ 6 ], + "y": [ 12 ], + "type": "door" + }, + "Fridge": { + "x": [ 21 ], + "y": [ 6 ], + "type": "decoration" + }, + "Oven": { + "x": [ 19 ], + "y": [ 6 ], + "type": "decoration" + }, + "Airplane Collection": { + "x": [ 6, 7 ], + "y": [ 3 ], + "type": "decoration" + }, + "Radio Broadcasting Set": { + "x": [ 4, 5 ], + "y": [ 4 ], + "type": "decoration" + }, + "Cassette Deck": { + "x": [ 8 ], + "y": [ 4 ], + "type": "decoration" + } + }, + "hospital": { + "Exit": { + "x": [ 10 ], + "y": [ 19 ], + "type": "door" + }, + "Harvey's Room Entrance": { + "x": [ 10 ], + "y": [ 2 ], + "type": "door" + }, + "Harvey's Room Entrance Door": { + "x": [ 10 ], + "y": [ 5 ], + "type": "door" + }, + "Main Area Door": { + "x": [ 10 ], + "y": [ 13 ], + "type": "door" + }, + "Counter": { + "x": [ 6 ], + "y": [ 16 ], + "type": "interactable" + } + }, + "blacksmith": { + "Exit": { + "x": [ 5 ], + "y": [ 19 ], + "type": "door" + }, + "Counter": { + "x": [ 3 ], + "y": [ 14 ], + "type": "interactable" + }, + "Clint's Room Door": { + "x": [ 4 ], + "y": [ 9 ], + "type": "door" + }, + "Clint's Furnace": { + "x": [ 9, 10 ], + "y": [ 12 ], + "type": "decoration" + }, + "Blueprints": { + "x": [ 13 ], + "y": [ 15, 16 ], + "type": "decoration" + }, + "Anvil": { + "x": [ 12, 13 ], + "y": [ 13 ], + "type": "decoration" + }, + "Cassette Deck": { + "x": [ 2 ], + "y": [ 4 ], + "type": "decoration" + }, + "Clint's Drawer": { + "x": [ 5, 6 ], + "y": [ 4 ], + "type": "decoration" + } + }, + "animalshop": { + "Exit": { + "x": [ 13 ], + "y": [ 19 ], + "type": "door" + }, + "Counter": { + "x": [ 12 ], + "y": [ 15 ], + "type": "interactable" + }, + "Marnie's Room Door": { + "x": [ 15 ], + "y": [ 12 ], + "type": "door" + }, + "Jas's Room Door": { + "x": [ 6 ], + "y": [ 13 ], + "type": "door" + }, + "Shane's Room Door": { + "x": [ 21 ], + "y": [ 13 ], + "type": "door" + }, + "Marnie's Barn Door": { + "x": [ 30 ], + "y": [ 13 ], + "type": "door" + }, + "Fridge": { + "x": [ 28 ], + "y": [ 14 ], + "type": "decoration" + }, + "Oven": { + "x": [ 24 ], + "y": [ 14 ], + "type": "decoration" + }, + "Mega Station": { + "x": [ 22 ], + "y": [ 5 ], + "type": "decoration" + }, + "Shane's Radio": { + "x": [ 24 ], + "y": [ 4 ], + "type": "decoration" + }, + "Marnie's Dresser": { + "x": [ 16 ], + "y": [ 4 ], + "type": "decoration" + }, + "Marnie's Drawer": { + "x": [ 17 ], + "y": [ 4 ], + "type": "decoration" + }, + "Jack in the Box": { + "x": [ 8 ], + "y": [ 5 ], + "type": "decoration" + }, + "Futan Bear": { + "x": [ 2, 3 ], + "y": [ 4 ], + "type": "decoration" + }, + "Colouring Book": { + "x": [ 5 ], + "y": [ 4 ], + "type": "decoration" + }, + "Paint Set": { + "x": [ 5 ], + "y": [ 7 ], + "type": "decoration" + }, + "Jas's Alarm Clock": { + "x": [ 8 ], + "y": [ 8 ], + "type": "decoration" + }, + "Jas's Radio": { + "x": [ 4 ], + "y": [ 9 ], + "type": "decoration" + }, + "Arts And Craft": { + "x": [ 7 ], + "y": [ 6 ], + "type": "decoration" + }, + "Doll House": { + "x": [ 6, 7 ], + "y": [ 4 ], + "type": "decoration" + } + }, + "saloon": { + "Exit": { + "x": [ 14 ], + "y": [ 24 ], + "type": "door" + }, + "Counter": { + "x": [ 14 ], + "y": [ 19 ], + "type": "interactable" + }, + "Journey of the Prairie King Arcade": { + "x": [ 33 ], + "y": [ 17 ], + "type": "interactable" + }, + "Junimo Kart Arcade": { + "x": [ 35 ], + "y": [ 17 ], + "type": "interactable" + }, + "Joja Vending Machine": { + "x": [ 37, 38 ], + "y": [ 17 ], + "type": "interactable" + }, + "Jukebox": { + "x": [ 1, 2 ], + "y": [ 17 ], + "type": "interactable" + }, + "Gus's Room Door": { + "x": [ 20 ], + "y": [ 9 ], + "type": "door" + }, + "Dining Room Door": { + "x": [ 11 ], + "y": [ 9 ], + "type": "door" + }, + "Living Area Door": { + "x": [ 4 ], + "y": [ 16 ], + "type": "door" + }, + "Gus's Radio": { + "x": [ 16 ], + "y": [ 6 ], + "type": "decoration" + } + }, + "sciencehouse": { + "Exit": { + "x": [ 6 ], + "y": [ 24 ], + "type": "door" + }, + "Secondary Exit": { + "x": [ 3 ], + "y": [ 8 ], + "type": "door" + }, + "Counter": { + "x": [ 8 ], + "y": [ 19 ], + "type": "interactable" + }, + "Sebastian's Room Entrance": { + "x": [ 12 ], + "y": [ 21 ], + "type": "door" + }, + "Beaker Set": { + "x": [ 17 ], + "y": [ 17 ], + "type": "decoration" + }, + "Microscope": { + "x": [ 19 ], + "y": [ 17 ], + "type": "decoration" + }, + "Stereo Microscope": { + "x": [ 23 ], + "y": [ 20 ], + "type": "decoration" + }, + "Robin and Demetrius's Room Entrance": { + "x": [ 13 ], + "y": [ 10 ], + "type": "door" + }, + "Maru's Room Entrance": { + "x": [ 7 ], + "y": [ 10 ], + "type": "door" + }, + "Bookshelf": { + "x": [ 16, 17 ], + "y": [ 4 ], + "type": "decoration" + }, + "Maru's Device": { + "x": [ 6 ], + "y": [ 6 ], + "type": "decoration" + }, + "Poster": { + "x": [ 6 ], + "y": [ 3 ], + "type": "decoration" + }, + "Computer": { + "x": [ 9 ], + "y": [ 4 ], + "type": "decoration" + }, + "Fridge": { + "x": [ 27 ], + "y": [ 8 ], + "type": "decoration" + }, + "Oven": { + "x": [ 30 ], + "y": [ 10 ], + "type": "decoration" + } + }, + "sebastianroom": { + "Exit": { + "x": [ 1 ], + "y": [ 1 ], + "type": "door" + }, + "Room Door": { + "x": [ 1 ], + "y": [ 3 ], + "type": "door" + }, + "Sebastian's Radio": { + "x": [ 3 ], + "y": [ 4 ], + "type": "decoration" + }, + "Graphic Novel": { + "x": [ 10 ], + "y": [ 6 ], + "type": "decoration" + }, + "Computer": { + "x": [ 7 ], + "y": [ 4 ], + "type": "decoration" + } + }, + "samhouse": { + "Exit": { + "x": [ 4 ], + "y": [ 23 ], + "type": "door" + }, + "Radio": { + "x": [ 6 ], + "y": [ 12 ], + "type": "decoration" + }, + "Vincent's Room Door": { + "x": [ 16 ], + "y": [ 18 ], + "type": "door" + }, + "Sam's Room Door": { + "x": [ 12 ], + "y": [ 14 ], + "type": "door" + }, + "Jodi's Room Door": { + "x": [ 17 ], + "y": [ 6 ], + "type": "door" + }, + "Sam's Drawer": { + "x": [ 7, 8 ], + "y": [ 12 ], + "type": "decoration" + }, + "Bookshelf": { + "x": [ 18, 19 ], + "y": [ 12 ], + "type": "decoration" + }, + "Fridge": { + "x": [ 7 ], + "y": [ 4 ], + "type": "decoration" + } + }, + "haleyhouse": { + "Exit": { + "x": [ 2 ], + "y": [ 24 ], + "type": "door" + }, + "Sewing Machine": { + "x": [ 12, 13 ], + "y": [ 23 ], + "type": "interactable" + }, + "Dye Pots": { + "x": [ 17 ], + "y": [ 25 ], + "type": "interactable" + }, + "Emily's Room Door": { + "x": [ 16 ], + "y": [ 12 ], + "type": "door" + }, + "Emily's Computer": { + "x": [ 22 ], + "y": [ 6 ], + "type": "decoration" + }, + "Emily's Pet Parrot": { + "x": [ 14 ], + "y": [ 4 ], + "type": "decoration" + }, + "Magazine": { + "x": [ 4 ], + "y": [ 22 ], + "type": "decoration" + }, + "Globe": { + "x": [ 8 ], + "y": [ 15 ], + "type": "decoration" + }, + "Fridge": { + "x": [ 21 ], + "y": [ 15 ], + "type": "decoration" + }, + "Haley's Room Door": { + "x": [ 5 ], + "y": [ 13 ], + "type": "door" + }, + "Futan Bear": { + "x": [ 8 ], + "y": [ 4 ], + "type": "decoration" + }, + "Diary": { + "x": [ 9 ], + "y": [ 5 ], + "type": "decoration" + }, + "Haley's Camera": { + "x": [ 1 ], + "y": [ 9 ], + "type": "decoration" + } + }, + "joshhouse": { + "Exit": { + "x": [ 9 ], + "y": [ 24 ], + "type": "door" + }, + "TV": { + "x": [ 15, 16 ], + "y": [ 20 ], + "type": "decoration" + }, + "Bookshelf": { + "x": [ 17, 18 ], + "y": [ 16 ], + "type": "decoration" + }, + "Fridge": { + "x": [ 5 ], + "y": [ 16 ], + "type": "decoration" + }, + "Evelyn and George's Room Door": { + "x": [ 5 ], + "y": [ 9 ], + "type": "door" + }, + "Alex's Room Door": { + "x": [ 10 ], + "y": [ 10 ], + "type": "door" + }, + "Radio": { + "x": [ 3 ], + "y": [ 4 ], + "type": "door" + }, + "Magazine": { + "x": [ 11 ], + "y": [ 4 ], + "type": "door" + }, + "Alex's Bookshelf": { + "x": [ 12, 13 ], + "y": [ 4 ], + "type": "decoration" + }, + "Alex's Drawer": { + "x": [ 17, 18 ], + "y": [ 4 ], + "type": "decoration" + }, + "Dumbbell": { + "x": [ 14 ], + "y": [ 4 ], + "type": "door" + }, + "Gridball": { + "x": [ 23 ], + "y": [ 45 ], + "type": "door" + }, + "Gridball Helmet": { + "x": [ 23 ], + "y": [ 6 ], + "type": "door" + } + }, + "trailer": { + "Exit": { + "x": [ 12 ], + "y": [ 9 ], + "type": "door" + }, + "Penny's Room Door": { + "x": [ 6 ], + "y": [ 7 ], + "type": "door" + }, + "Bookshelf": { + "x": [ 5, 6 ], + "y": [ 4 ], + "type": "decoration" + }, + "Book": { + "x": [ 2 ], + "y": [ 4 ], + "type": "decoration" + }, + "Magazine": { + "x": [ 1 ], + "y": [ 9 ], + "type": "decoration" + } + } + } \ No newline at end of file From 661d6bb31f20eb60efd48983b0cfd70b7c05da34 Mon Sep 17 00:00:00 2001 From: bradjrenshaw Date: Sun, 15 May 2022 18:58:23 -0400 Subject: [PATCH 159/232] Add secondary entrances connecting various locations (Island North to south and Island North to Volcano Dungeon) --- stardew-access/assets/static-tiles.json | 2059 ++++++++++++----------- 1 file changed, 1042 insertions(+), 1017 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index cde98f3..c0b3908 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -780,6 +780,16 @@ "x": [ 37 ], "y": [ 5 ], "type": "door" + }, + "Island North Entrance": { + "x": [ 31 ], + "y": [ 54 ], + "type": "door" + }, + "Island North Entrance 2": { + "x": [6], + "y": [ 50 ], + "type": "door" } }, "club": { @@ -1099,6 +1109,11 @@ "y": [ 0 ], "type": "door" }, + "Island North Entrance 2": { + "x": [ 27, 28 ], + "y": [ 0 ], + "type": "door" + }, "Docks Parrot Express": { "x": [ 6 ], "y": [ 31 ], @@ -1179,1022 +1194,1032 @@ "type": "door" } }, - "islandfarmcave": { - "Exit": { - "x": [ 4 ], - "y": [ 10 ], - "type": "door" - }, - "Gourmand Frog": { - "x": [ 5 ], - "y": [ 4 ], - "type": "npc" - } + "islandfarmcave": { + "Exit": { + "x": [ 4 ], + "y": [ 10 ], + "type": "door" }, - "islandnorth": { - "Island South Entrance": { - "x": [ 35 ], - "y": [ 89 ], - "type": "door" - }, - "Island Field Office Entrance": { - "x": [ 46 ], - "y": [ 46 ], - "type": "door" - }, - "Island North Cave Entrance": { - "x": [ 21, 22 ], - "y": [ 47 ], - "type": "door" - }, - "Dig Site Parrot Express": { - "x": [ 5 ], - "y": [ 48 ], - "type": "interactable" - }, - "Volcano Dungeon Entrance": { - "x": [ 40 ], - "y": [ 22 ], - "type": "door" - }, - "Volcano Parrot Express": { - "x": [ 60 ], - "y": [ 16 ], - "type": "interactable" - } - }, - "islandfieldoffice": { - "Exit": { - "x": [ 4 ], - "y": [ 10 ], - "type": "door" - }, - "Counter": { - "x": [ 8 ], - "y": [ 7 ], - "type": "interactable" - }, - "Island Survey": { - "x": [ 5 ], - "y": [ 3 ], - "type": "interactable" - } - }, - "qinutroom": { - "Exit": { - "x": [ 7 ], - "y": [ 7 ], - "type": "door" - }, - "Perfection Tracker": { - "x": [ 13 ], - "y": [ 4 ], - "type": "interactable" - }, - "Vending Machine": { - "x": [ 11 ], - "y": [ 3 ], - "type": "interactable" - }, - "Special Order Board": { - "x": [ 3 ], - "y": [ 3 ], - "type": "interactable" - } - }, - "islandsoutheast": { - "Island South East Cave Entrance": { - "x": [ 30 ], - "y": [ 18 ], - "type": "door" - } - }, - "islandsoutheastcave": { - "Exit": { - "x": [ 1 ], - "y": [ 8 ], - "type": "door" - } - }, - "islandshrine": { - "Exit": { - "x": [ 13 ], - "y": [ 28 ], - "type": "door" - }, - "Shrine": { - "x": [ 24 ], - "y": [ 22 ], - "type": "interactable" - }, - "North Pedestal": { - "x": [ 24 ], - "y": [ 25 ], - "type": "interactable" - }, - "East Pedestal": { - "x": [ 27 ], - "y": [ 27 ], - "type": "interactable" - }, - "West Pedestal": { - "x": [ 21 ], - "y": [ 27 ], - "type": "interactable" - }, - "South Pedestal": { - "x": [ 24 ], - "y": [ 28 ], - "type": "interactable" - } - }, - "jojamart": { - "Exit": { - "x": [ 13 ], - "y": [ 29 ], - "type": "door" - }, - "Morris's Kiosk": { - "x": [ 21 ], - "y": [ 25 ], - "type": "interactable" - }, - "Shop Counter": { - "x": [ 10 ], - "y": [ 25 ], - "type": "interactable" - } - }, - "archaeologyhouse": { - "Exit": { - "x": [ 3 ], - "y": [ 14 ], - "type": "door" - }, - "Counter": { - "x": [ 3 ], - "y": [ 9 ], - "type": "interactable" - } - }, - "manorhouse": { - "Exit": { - "x": [ 4 ], - "y": [ 11 ], - "type": "door" - }, - "Town Ledger Book": { - "x": [ 2 ], - "y": [ 5 ], - "type": "interactable" - }, - "Marriage Log Book": { - "x": [ 3 ], - "y": [ 5 ], - "type": "interactable" - }, - "Lost and Found Box": { - "x": [ 4 ], - "y": [ 5 ], - "type": "interactable" - }, - "Mayor's Room Door": { - "x": [ 16 ], - "y": [ 9 ], - "type": "door" - }, - "Mayor's Oven": { - "x": [ 7 ], - "y": [ 4 ], - "type": "decoration" - }, - "Mayor's Fridge": { - "x": [ 9 ], - "y": [ 4 ], - "type": "decoration" - } - }, - "mine": { - "Mountain Exit": { - "x": [ 18 ], - "y": [ 13 ], - "type": "door" - }, - "Minecart": { - "x": [ 11, 12 ], - "y": [ 10 ], - "type": "interactable" - }, - "Quarry Mine Ladder": { - "x": [ 67 ], - "y": [ 9 ], - "type": "door" - }, - "Quarry Exit": { - "x": [ 18 ], - "y": [ 13 ], - "type": "door" - } - }, - "mountain": { - "Mine Entrance": { - "x": [ 54 ], - "y": [ 5 ], - "type": "door" - }, - "Mine Bridge": { - "x": [ 47 ], - "y": [ 7 ], - "type": "bridge" - }, - "Quarry Bridge": { - "x": [ 90 ], - "y": [ 26 ], - "type": "bridge" - }, - "Minecart": { - "x": [ 124, 125 ], - "y": [ 11 ], - "type": "interactable" - }, - "Quarry Mine Entrance": { - "x": [ 103 ], - "y": [ 17 ], - "type": "door" - }, - "Bridge 1": { - "x": [ 57 ], - "y": [ 30 ], - "type": "bridge" - }, - "Bridge 2": { - "x": [ 61 ], - "y": [ 21 ], - "type": "bridge" - }, - "Mountain Warp Statue": { - "x": [ 31 ], - "y": [ 20 ], - "type": "decoration" - }, - "Linus Tent Entrance": { - "x": [ 29 ], - "y": [ 7 ], - "type": "door" - }, - "Backwoods Entrance": { - "x": [ 0 ], - "y": [ 13 ], - "type": "door" - }, - "Town Entrance": { - "x": [ 15 ], - "y": [ 40 ], - "type": "door" - }, - "Railroad Entrance": { - "x": [ 9 ], - "y": [ 0 ], - "type": "door" - }, - "Science House Secondary Door": { - "x": [ 8 ], - "y": [ 20 ], - "type": "door" - } - }, - "undergroundmine77377": { - "Grim Reaper Statue": { - "x": [ 29, 30 ], - "y": [ 6 ], - "type": "interactable" - } - }, - "Tent": { - "Exit": { - "x": [ 2 ], - "y": [ 5 ], - "type": "door" - } - }, - "railroad": { - "Mountain Entrance": { - "x": [ 29 ], - "y": [ 61 ], - "type": "door" - } - }, - "backwoods": { - "Mountain Entrance": { - "x": [ 49 ], - "y": [ 14 ], - "type": "door" - }, - "Farm Entrance": { - "x": [ 14 ], - "y": [ 39 ], - "type": "door" - }, - "Bus stop Entrance": { - "x": [ 49 ], - "y": [ 28, 29, 30, 31, 32 ], - "type": "door" - }, - "Tunnel Entrance": { - "x": [ 23 ], - "y": [ 29, 30, 31 ], - "type": "door" - } - }, - "tunnel": { - "Exit": { - "x": [ 39 ], - "y": [ 7, 8, 9, 10, 11 ], - "type": "door" - } - }, - "movietheater": { - "Exit": { - "x": [ 13 ], - "y": [ 15 ], - "type": "door" - }, - "Concessions Counter": { - "x": [ 7 ], - "y": [ 6 ], - "type": "interactable" - }, - "Crane Game": { - "x": [ 1, 2 ], - "y": [ 8 ], - "type": "interactable" - }, - "Theater Door": { - "x": [ 13 ], - "y": [ 3 ], - "type": "door" - }, - "Crane Man": { - "x": [ 2 ], - "y": [ 9 ], - "type": "npc" - } - }, - "seedshop": { - "Exit": { - "x": [ 6 ], - "y": [ 29 ], - "type": "door" - }, - "Shop Counter": { - "x": [ 4 ], - "y": [ 18 ], - "type": "interactable" - }, - "Backpack Upgrade": { - "x": [ 7 ], - "y": [ 18 ], - "type": "interactable" - }, - "Shrine of Yoba": { - "x": [ 37 ], - "y": [ 17 ], - "type": "decoration" - }, - "Fridge": { - "x": [ 39 ], - "y": [ 4 ], - "type": "decoration" - }, - "Abigail's Room Door": { - "x": [ 13 ], - "y": [ 11 ], - "type": "door" - }, - "Pierre and Caroline's Room Door": { - "x": [ 20 ], - "y": [ 11 ], - "type": "door" - }, - "Living Area Door": { - "x": [ 14 ], - "y": [ 16 ], - "type": "door" - } - }, - "sewer": { - "Exit Ladder": { - "x": [ 16 ], - "y": [ 10 ], - "type": "door" - }, - "Statue Of Uncertainty": { - "x": [ 8 ], - "y": [ 20 ], - "type": "interactable" - }, - "Mutant Bug Lair": { - "x": [ 3 ], - "y": [ 19 ], - "type": "door" - } - }, - "wizardhouse": { - "Exit": { - "x": [ 8 ], - "y": [ 24 ], - "type": "door" - }, - "Basement Door": { - "x": [ 4 ], - "y": [ 4 ], - "type": "door" - } - }, - "wizardhousebasement": { - "Exit Ladder": { - "x": [ 4 ], - "y": [ 3 ], - "type": "door" - }, - "Shrine of Illusions": { - "x": [ 12 ], - "y": [ 4 ], - "type": "interactable" - } - }, - "woods": { - "Forest Entrance": { - "x": [ 59 ], - "y": [ 17 ], - "type": "door" - }, - "Old Master Cannoli": { - "x": [ 8, 9 ], - "y": [ 7 ], - "type": "interactable" - } - }, - "harveyroom": { - "Exit": { - "x": [ 6 ], - "y": [ 12 ], - "type": "door" - }, - "Fridge": { - "x": [ 21 ], - "y": [ 6 ], - "type": "decoration" - }, - "Oven": { - "x": [ 19 ], - "y": [ 6 ], - "type": "decoration" - }, - "Airplane Collection": { - "x": [ 6, 7 ], - "y": [ 3 ], - "type": "decoration" - }, - "Radio Broadcasting Set": { - "x": [ 4, 5 ], - "y": [ 4 ], - "type": "decoration" - }, - "Cassette Deck": { - "x": [ 8 ], - "y": [ 4 ], - "type": "decoration" - } - }, - "hospital": { - "Exit": { - "x": [ 10 ], - "y": [ 19 ], - "type": "door" - }, - "Harvey's Room Entrance": { - "x": [ 10 ], - "y": [ 2 ], - "type": "door" - }, - "Harvey's Room Entrance Door": { - "x": [ 10 ], - "y": [ 5 ], - "type": "door" - }, - "Main Area Door": { - "x": [ 10 ], - "y": [ 13 ], - "type": "door" - }, - "Counter": { - "x": [ 6 ], - "y": [ 16 ], - "type": "interactable" - } - }, - "blacksmith": { - "Exit": { - "x": [ 5 ], - "y": [ 19 ], - "type": "door" - }, - "Counter": { - "x": [ 3 ], - "y": [ 14 ], - "type": "interactable" - }, - "Clint's Room Door": { - "x": [ 4 ], - "y": [ 9 ], - "type": "door" - }, - "Clint's Furnace": { - "x": [ 9, 10 ], - "y": [ 12 ], - "type": "decoration" - }, - "Blueprints": { - "x": [ 13 ], - "y": [ 15, 16 ], - "type": "decoration" - }, - "Anvil": { - "x": [ 12, 13 ], - "y": [ 13 ], - "type": "decoration" - }, - "Cassette Deck": { - "x": [ 2 ], - "y": [ 4 ], - "type": "decoration" - }, - "Clint's Drawer": { - "x": [ 5, 6 ], - "y": [ 4 ], - "type": "decoration" - } - }, - "animalshop": { - "Exit": { - "x": [ 13 ], - "y": [ 19 ], - "type": "door" - }, - "Counter": { - "x": [ 12 ], - "y": [ 15 ], - "type": "interactable" - }, - "Marnie's Room Door": { - "x": [ 15 ], - "y": [ 12 ], - "type": "door" - }, - "Jas's Room Door": { - "x": [ 6 ], - "y": [ 13 ], - "type": "door" - }, - "Shane's Room Door": { - "x": [ 21 ], - "y": [ 13 ], - "type": "door" - }, - "Marnie's Barn Door": { - "x": [ 30 ], - "y": [ 13 ], - "type": "door" - }, - "Fridge": { - "x": [ 28 ], - "y": [ 14 ], - "type": "decoration" - }, - "Oven": { - "x": [ 24 ], - "y": [ 14 ], - "type": "decoration" - }, - "Mega Station": { - "x": [ 22 ], - "y": [ 5 ], - "type": "decoration" - }, - "Shane's Radio": { - "x": [ 24 ], - "y": [ 4 ], - "type": "decoration" - }, - "Marnie's Dresser": { - "x": [ 16 ], - "y": [ 4 ], - "type": "decoration" - }, - "Marnie's Drawer": { - "x": [ 17 ], - "y": [ 4 ], - "type": "decoration" - }, - "Jack in the Box": { - "x": [ 8 ], - "y": [ 5 ], - "type": "decoration" - }, - "Futan Bear": { - "x": [ 2, 3 ], - "y": [ 4 ], - "type": "decoration" - }, - "Colouring Book": { - "x": [ 5 ], - "y": [ 4 ], - "type": "decoration" - }, - "Paint Set": { - "x": [ 5 ], - "y": [ 7 ], - "type": "decoration" - }, - "Jas's Alarm Clock": { - "x": [ 8 ], - "y": [ 8 ], - "type": "decoration" - }, - "Jas's Radio": { - "x": [ 4 ], - "y": [ 9 ], - "type": "decoration" - }, - "Arts And Craft": { - "x": [ 7 ], - "y": [ 6 ], - "type": "decoration" - }, - "Doll House": { - "x": [ 6, 7 ], - "y": [ 4 ], - "type": "decoration" - } - }, - "saloon": { - "Exit": { - "x": [ 14 ], - "y": [ 24 ], - "type": "door" - }, - "Counter": { - "x": [ 14 ], - "y": [ 19 ], - "type": "interactable" - }, - "Journey of the Prairie King Arcade": { - "x": [ 33 ], - "y": [ 17 ], - "type": "interactable" - }, - "Junimo Kart Arcade": { - "x": [ 35 ], - "y": [ 17 ], - "type": "interactable" - }, - "Joja Vending Machine": { - "x": [ 37, 38 ], - "y": [ 17 ], - "type": "interactable" - }, - "Jukebox": { - "x": [ 1, 2 ], - "y": [ 17 ], - "type": "interactable" - }, - "Gus's Room Door": { - "x": [ 20 ], - "y": [ 9 ], - "type": "door" - }, - "Dining Room Door": { - "x": [ 11 ], - "y": [ 9 ], - "type": "door" - }, - "Living Area Door": { - "x": [ 4 ], - "y": [ 16 ], - "type": "door" - }, - "Gus's Radio": { - "x": [ 16 ], - "y": [ 6 ], - "type": "decoration" - } - }, - "sciencehouse": { - "Exit": { - "x": [ 6 ], - "y": [ 24 ], - "type": "door" - }, - "Secondary Exit": { - "x": [ 3 ], - "y": [ 8 ], - "type": "door" - }, - "Counter": { - "x": [ 8 ], - "y": [ 19 ], - "type": "interactable" - }, - "Sebastian's Room Entrance": { - "x": [ 12 ], - "y": [ 21 ], - "type": "door" - }, - "Beaker Set": { - "x": [ 17 ], - "y": [ 17 ], - "type": "decoration" - }, - "Microscope": { - "x": [ 19 ], - "y": [ 17 ], - "type": "decoration" - }, - "Stereo Microscope": { - "x": [ 23 ], - "y": [ 20 ], - "type": "decoration" - }, - "Robin and Demetrius's Room Entrance": { - "x": [ 13 ], - "y": [ 10 ], - "type": "door" - }, - "Maru's Room Entrance": { - "x": [ 7 ], - "y": [ 10 ], - "type": "door" - }, - "Bookshelf": { - "x": [ 16, 17 ], - "y": [ 4 ], - "type": "decoration" - }, - "Maru's Device": { - "x": [ 6 ], - "y": [ 6 ], - "type": "decoration" - }, - "Poster": { - "x": [ 6 ], - "y": [ 3 ], - "type": "decoration" - }, - "Computer": { - "x": [ 9 ], - "y": [ 4 ], - "type": "decoration" - }, - "Fridge": { - "x": [ 27 ], - "y": [ 8 ], - "type": "decoration" - }, - "Oven": { - "x": [ 30 ], - "y": [ 10 ], - "type": "decoration" - } - }, - "sebastianroom": { - "Exit": { - "x": [ 1 ], - "y": [ 1 ], - "type": "door" - }, - "Room Door": { - "x": [ 1 ], - "y": [ 3 ], - "type": "door" - }, - "Sebastian's Radio": { - "x": [ 3 ], - "y": [ 4 ], - "type": "decoration" - }, - "Graphic Novel": { - "x": [ 10 ], - "y": [ 6 ], - "type": "decoration" - }, - "Computer": { - "x": [ 7 ], - "y": [ 4 ], - "type": "decoration" - } - }, - "samhouse": { - "Exit": { - "x": [ 4 ], - "y": [ 23 ], - "type": "door" - }, - "Radio": { - "x": [ 6 ], - "y": [ 12 ], - "type": "decoration" - }, - "Vincent's Room Door": { - "x": [ 16 ], - "y": [ 18 ], - "type": "door" - }, - "Sam's Room Door": { - "x": [ 12 ], - "y": [ 14 ], - "type": "door" - }, - "Jodi's Room Door": { - "x": [ 17 ], - "y": [ 6 ], - "type": "door" - }, - "Sam's Drawer": { - "x": [ 7, 8 ], - "y": [ 12 ], - "type": "decoration" - }, - "Bookshelf": { - "x": [ 18, 19 ], - "y": [ 12 ], - "type": "decoration" - }, - "Fridge": { - "x": [ 7 ], - "y": [ 4 ], - "type": "decoration" - } - }, - "haleyhouse": { - "Exit": { - "x": [ 2 ], - "y": [ 24 ], - "type": "door" - }, - "Sewing Machine": { - "x": [ 12, 13 ], - "y": [ 23 ], - "type": "interactable" - }, - "Dye Pots": { - "x": [ 17 ], - "y": [ 25 ], - "type": "interactable" - }, - "Emily's Room Door": { - "x": [ 16 ], - "y": [ 12 ], - "type": "door" - }, - "Emily's Computer": { - "x": [ 22 ], - "y": [ 6 ], - "type": "decoration" - }, - "Emily's Pet Parrot": { - "x": [ 14 ], - "y": [ 4 ], - "type": "decoration" - }, - "Magazine": { - "x": [ 4 ], - "y": [ 22 ], - "type": "decoration" - }, - "Globe": { - "x": [ 8 ], - "y": [ 15 ], - "type": "decoration" - }, - "Fridge": { - "x": [ 21 ], - "y": [ 15 ], - "type": "decoration" - }, - "Haley's Room Door": { - "x": [ 5 ], - "y": [ 13 ], - "type": "door" - }, - "Futan Bear": { - "x": [ 8 ], - "y": [ 4 ], - "type": "decoration" - }, - "Diary": { - "x": [ 9 ], - "y": [ 5 ], - "type": "decoration" - }, - "Haley's Camera": { - "x": [ 1 ], - "y": [ 9 ], - "type": "decoration" - } - }, - "joshhouse": { - "Exit": { - "x": [ 9 ], - "y": [ 24 ], - "type": "door" - }, - "TV": { - "x": [ 15, 16 ], - "y": [ 20 ], - "type": "decoration" - }, - "Bookshelf": { - "x": [ 17, 18 ], - "y": [ 16 ], - "type": "decoration" - }, - "Fridge": { - "x": [ 5 ], - "y": [ 16 ], - "type": "decoration" - }, - "Evelyn and George's Room Door": { - "x": [ 5 ], - "y": [ 9 ], - "type": "door" - }, - "Alex's Room Door": { - "x": [ 10 ], - "y": [ 10 ], - "type": "door" - }, - "Radio": { - "x": [ 3 ], - "y": [ 4 ], - "type": "door" - }, - "Magazine": { - "x": [ 11 ], - "y": [ 4 ], - "type": "door" - }, - "Alex's Bookshelf": { - "x": [ 12, 13 ], - "y": [ 4 ], - "type": "decoration" - }, - "Alex's Drawer": { - "x": [ 17, 18 ], - "y": [ 4 ], - "type": "decoration" - }, - "Dumbbell": { - "x": [ 14 ], - "y": [ 4 ], - "type": "door" - }, - "Gridball": { - "x": [ 23 ], - "y": [ 45 ], - "type": "door" - }, - "Gridball Helmet": { - "x": [ 23 ], - "y": [ 6 ], - "type": "door" - } - }, - "trailer": { - "Exit": { - "x": [ 12 ], - "y": [ 9 ], - "type": "door" - }, - "Penny's Room Door": { - "x": [ 6 ], - "y": [ 7 ], - "type": "door" - }, - "Bookshelf": { - "x": [ 5, 6 ], - "y": [ 4 ], - "type": "decoration" - }, - "Book": { - "x": [ 2 ], - "y": [ 4 ], - "type": "decoration" - }, - "Magazine": { - "x": [ 1 ], - "y": [ 9 ], - "type": "decoration" - } + "Gourmand Frog": { + "x": [ 5 ], + "y": [ 4 ], + "type": "npc" } - } \ No newline at end of file + }, + "islandnorth": { + "Island South Entrance": { + "x": [ 35 ], + "y": [ 89 ], + "type": "door" + }, + "Island South Entrance 2": { + "x": [ 43, 44 ], + "y": [ 89 ], + "type": "door" + }, + "Island Field Office Entrance": { + "x": [ 46 ], + "y": [ 46 ], + "type": "door" + }, + "Island North Cave Entrance": { + "x": [ 21, 22 ], + "y": [ 47 ], + "type": "door" + }, + "Dig Site Parrot Express": { + "x": [ 5 ], + "y": [ 48 ], + "type": "interactable" + }, + "Volcano Dungeon Entrance": { + "x": [ 40 ], + "y": [ 22 ], + "type": "door" + }, + "Volcano Dungeon Entrance 2": { + "x": [ 12 ], + "y": [ 30 ], + "type": "door" + }, + "Volcano Parrot Express": { + "x": [ 60 ], + "y": [ 16 ], + "type": "interactable" + } + }, + "islandfieldoffice": { + "Exit": { + "x": [ 4 ], + "y": [ 10 ], + "type": "door" + }, + "Counter": { + "x": [ 8 ], + "y": [ 7 ], + "type": "interactable" + }, + "Island Survey": { + "x": [ 5 ], + "y": [ 3 ], + "type": "interactable" + } + }, + "qinutroom": { + "Exit": { + "x": [ 7 ], + "y": [ 7 ], + "type": "door" + }, + "Perfection Tracker": { + "x": [ 13 ], + "y": [ 4 ], + "type": "interactable" + }, + "Vending Machine": { + "x": [ 11 ], + "y": [ 3 ], + "type": "interactable" + }, + "Special Order Board": { + "x": [ 3 ], + "y": [ 3 ], + "type": "interactable" + } + }, + "islandsoutheast": { + "Island South East Cave Entrance": { + "x": [ 30 ], + "y": [ 18 ], + "type": "door" + } + }, + "islandsoutheastcave": { + "Exit": { + "x": [ 1 ], + "y": [ 8 ], + "type": "door" + } + }, + "islandshrine": { + "Exit": { + "x": [ 13 ], + "y": [ 28 ], + "type": "door" + }, + "Shrine": { + "x": [ 24 ], + "y": [ 22 ], + "type": "interactable" + }, + "North Pedestal": { + "x": [ 24 ], + "y": [ 25 ], + "type": "interactable" + }, + "East Pedestal": { + "x": [ 27 ], + "y": [ 27 ], + "type": "interactable" + }, + "West Pedestal": { + "x": [ 21 ], + "y": [ 27 ], + "type": "interactable" + }, + "South Pedestal": { + "x": [ 24 ], + "y": [ 28 ], + "type": "interactable" + } + }, + "jojamart": { + "Exit": { + "x": [ 13 ], + "y": [ 29 ], + "type": "door" + }, + "Morris's Kiosk": { + "x": [ 21 ], + "y": [ 25 ], + "type": "interactable" + }, + "Shop Counter": { + "x": [ 10 ], + "y": [ 25 ], + "type": "interactable" + } + }, + "archaeologyhouse": { + "Exit": { + "x": [ 3 ], + "y": [ 14 ], + "type": "door" + }, + "Counter": { + "x": [ 3 ], + "y": [ 9 ], + "type": "interactable" + } + }, + "manorhouse": { + "Exit": { + "x": [ 4 ], + "y": [ 11 ], + "type": "door" + }, + "Town Ledger Book": { + "x": [ 2 ], + "y": [ 5 ], + "type": "interactable" + }, + "Marriage Log Book": { + "x": [ 3 ], + "y": [ 5 ], + "type": "interactable" + }, + "Lost and Found Box": { + "x": [ 4 ], + "y": [ 5 ], + "type": "interactable" + }, + "Mayor's Room Door": { + "x": [ 16 ], + "y": [ 9 ], + "type": "door" + }, + "Mayor's Oven": { + "x": [ 7 ], + "y": [ 4 ], + "type": "decoration" + }, + "Mayor's Fridge": { + "x": [ 9 ], + "y": [ 4 ], + "type": "decoration" + } + }, + "mine": { + "Mountain Exit": { + "x": [ 18 ], + "y": [ 13 ], + "type": "door" + }, + "Minecart": { + "x": [ 11, 12 ], + "y": [ 10 ], + "type": "interactable" + }, + "Quarry Mine Ladder": { + "x": [ 67 ], + "y": [ 9 ], + "type": "door" + }, + "Quarry Exit": { + "x": [ 18 ], + "y": [ 13 ], + "type": "door" + } + }, + "mountain": { + "Mine Entrance": { + "x": [ 54 ], + "y": [ 5 ], + "type": "door" + }, + "Mine Bridge": { + "x": [ 47 ], + "y": [ 7 ], + "type": "bridge" + }, + "Quarry Bridge": { + "x": [ 90 ], + "y": [ 26 ], + "type": "bridge" + }, + "Minecart": { + "x": [ 124, 125 ], + "y": [ 11 ], + "type": "interactable" + }, + "Quarry Mine Entrance": { + "x": [ 103 ], + "y": [ 17 ], + "type": "door" + }, + "Bridge 1": { + "x": [ 57 ], + "y": [ 30 ], + "type": "bridge" + }, + "Bridge 2": { + "x": [ 61 ], + "y": [ 21 ], + "type": "bridge" + }, + "Mountain Warp Statue": { + "x": [ 31 ], + "y": [ 20 ], + "type": "decoration" + }, + "Linus Tent Entrance": { + "x": [ 29 ], + "y": [ 7 ], + "type": "door" + }, + "Backwoods Entrance": { + "x": [ 0 ], + "y": [ 13 ], + "type": "door" + }, + "Town Entrance": { + "x": [ 15 ], + "y": [ 40 ], + "type": "door" + }, + "Railroad Entrance": { + "x": [ 9 ], + "y": [ 0 ], + "type": "door" + }, + "Science House Secondary Door": { + "x": [ 8 ], + "y": [ 20 ], + "type": "door" + } + }, + "undergroundmine77377": { + "Grim Reaper Statue": { + "x": [ 29, 30 ], + "y": [ 6 ], + "type": "interactable" + } + }, + "Tent": { + "Exit": { + "x": [ 2 ], + "y": [ 5 ], + "type": "door" + } + }, + "railroad": { + "Mountain Entrance": { + "x": [ 29 ], + "y": [ 61 ], + "type": "door" + } + }, + "backwoods": { + "Mountain Entrance": { + "x": [ 49 ], + "y": [ 14 ], + "type": "door" + }, + "Farm Entrance": { + "x": [ 14 ], + "y": [ 39 ], + "type": "door" + }, + "Bus stop Entrance": { + "x": [ 49 ], + "y": [ 28, 29, 30, 31, 32 ], + "type": "door" + }, + "Tunnel Entrance": { + "x": [ 23 ], + "y": [ 29, 30, 31 ], + "type": "door" + } + }, + "tunnel": { + "Exit": { + "x": [ 39 ], + "y": [ 7, 8, 9, 10, 11 ], + "type": "door" + } + }, + "movietheater": { + "Exit": { + "x": [ 13 ], + "y": [ 15 ], + "type": "door" + }, + "Concessions Counter": { + "x": [ 7 ], + "y": [ 6 ], + "type": "interactable" + }, + "Crane Game": { + "x": [ 1, 2 ], + "y": [ 8 ], + "type": "interactable" + }, + "Theater Door": { + "x": [ 13 ], + "y": [ 3 ], + "type": "door" + }, + "Crane Man": { + "x": [ 2 ], + "y": [ 9 ], + "type": "npc" + } + }, + "seedshop": { + "Exit": { + "x": [ 6 ], + "y": [ 29 ], + "type": "door" + }, + "Shop Counter": { + "x": [ 4 ], + "y": [ 18 ], + "type": "interactable" + }, + "Backpack Upgrade": { + "x": [ 7 ], + "y": [ 18 ], + "type": "interactable" + }, + "Shrine of Yoba": { + "x": [ 37 ], + "y": [ 17 ], + "type": "decoration" + }, + "Fridge": { + "x": [ 39 ], + "y": [ 4 ], + "type": "decoration" + }, + "Abigail's Room Door": { + "x": [ 13 ], + "y": [ 11 ], + "type": "door" + }, + "Pierre and Caroline's Room Door": { + "x": [ 20 ], + "y": [ 11 ], + "type": "door" + }, + "Living Area Door": { + "x": [ 14 ], + "y": [ 16 ], + "type": "door" + } + }, + "sewer": { + "Exit Ladder": { + "x": [ 16 ], + "y": [ 10 ], + "type": "door" + }, + "Statue Of Uncertainty": { + "x": [ 8 ], + "y": [ 20 ], + "type": "interactable" + }, + "Mutant Bug Lair": { + "x": [ 3 ], + "y": [ 19 ], + "type": "door" + } + }, + "wizardhouse": { + "Exit": { + "x": [ 8 ], + "y": [ 24 ], + "type": "door" + }, + "Basement Door": { + "x": [ 4 ], + "y": [ 4 ], + "type": "door" + } + }, + "wizardhousebasement": { + "Exit Ladder": { + "x": [ 4 ], + "y": [ 3 ], + "type": "door" + }, + "Shrine of Illusions": { + "x": [ 12 ], + "y": [ 4 ], + "type": "interactable" + } + }, + "woods": { + "Forest Entrance": { + "x": [ 59 ], + "y": [ 17 ], + "type": "door" + }, + "Old Master Cannoli": { + "x": [ 8, 9 ], + "y": [ 7 ], + "type": "interactable" + } + }, + "harveyroom": { + "Exit": { + "x": [ 6 ], + "y": [ 12 ], + "type": "door" + }, + "Fridge": { + "x": [ 21 ], + "y": [ 6 ], + "type": "decoration" + }, + "Oven": { + "x": [ 19 ], + "y": [ 6 ], + "type": "decoration" + }, + "Airplane Collection": { + "x": [ 6, 7 ], + "y": [ 3 ], + "type": "decoration" + }, + "Radio Broadcasting Set": { + "x": [ 4, 5 ], + "y": [ 4 ], + "type": "decoration" + }, + "Cassette Deck": { + "x": [ 8 ], + "y": [ 4 ], + "type": "decoration" + } + }, + "hospital": { + "Exit": { + "x": [ 10 ], + "y": [ 19 ], + "type": "door" + }, + "Harvey's Room Entrance": { + "x": [ 10 ], + "y": [ 2 ], + "type": "door" + }, + "Harvey's Room Entrance Door": { + "x": [ 10 ], + "y": [ 5 ], + "type": "door" + }, + "Main Area Door": { + "x": [ 10 ], + "y": [ 13 ], + "type": "door" + }, + "Counter": { + "x": [ 6 ], + "y": [ 16 ], + "type": "interactable" + } + }, + "blacksmith": { + "Exit": { + "x": [ 5 ], + "y": [ 19 ], + "type": "door" + }, + "Counter": { + "x": [ 3 ], + "y": [ 14 ], + "type": "interactable" + }, + "Clint's Room Door": { + "x": [ 4 ], + "y": [ 9 ], + "type": "door" + }, + "Clint's Furnace": { + "x": [ 9, 10 ], + "y": [ 12 ], + "type": "decoration" + }, + "Blueprints": { + "x": [ 13 ], + "y": [ 15, 16 ], + "type": "decoration" + }, + "Anvil": { + "x": [ 12, 13 ], + "y": [ 13 ], + "type": "decoration" + }, + "Cassette Deck": { + "x": [ 2 ], + "y": [ 4 ], + "type": "decoration" + }, + "Clint's Drawer": { + "x": [ 5, 6 ], + "y": [ 4 ], + "type": "decoration" + } + }, + "animalshop": { + "Exit": { + "x": [ 13 ], + "y": [ 19 ], + "type": "door" + }, + "Counter": { + "x": [ 12 ], + "y": [ 15 ], + "type": "interactable" + }, + "Marnie's Room Door": { + "x": [ 15 ], + "y": [ 12 ], + "type": "door" + }, + "Jas's Room Door": { + "x": [ 6 ], + "y": [ 13 ], + "type": "door" + }, + "Shane's Room Door": { + "x": [ 21 ], + "y": [ 13 ], + "type": "door" + }, + "Marnie's Barn Door": { + "x": [ 30 ], + "y": [ 13 ], + "type": "door" + }, + "Fridge": { + "x": [ 28 ], + "y": [ 14 ], + "type": "decoration" + }, + "Oven": { + "x": [ 24 ], + "y": [ 14 ], + "type": "decoration" + }, + "Mega Station": { + "x": [ 22 ], + "y": [ 5 ], + "type": "decoration" + }, + "Shane's Radio": { + "x": [ 24 ], + "y": [ 4 ], + "type": "decoration" + }, + "Marnie's Dresser": { + "x": [ 16 ], + "y": [ 4 ], + "type": "decoration" + }, + "Marnie's Drawer": { + "x": [ 17 ], + "y": [ 4 ], + "type": "decoration" + }, + "Jack in the Box": { + "x": [ 8 ], + "y": [ 5 ], + "type": "decoration" + }, + "Futan Bear": { + "x": [ 2, 3 ], + "y": [ 4 ], + "type": "decoration" + }, + "Colouring Book": { + "x": [ 5 ], + "y": [ 4 ], + "type": "decoration" + }, + "Paint Set": { + "x": [ 5 ], + "y": [ 7 ], + "type": "decoration" + }, + "Jas's Alarm Clock": { + "x": [ 8 ], + "y": [ 8 ], + "type": "decoration" + }, + "Jas's Radio": { + "x": [ 4 ], + "y": [ 9 ], + "type": "decoration" + }, + "Arts And Craft": { + "x": [ 7 ], + "y": [ 6 ], + "type": "decoration" + }, + "Doll House": { + "x": [ 6, 7 ], + "y": [ 4 ], + "type": "decoration" + } + }, + "saloon": { + "Exit": { + "x": [ 14 ], + "y": [ 24 ], + "type": "door" + }, + "Counter": { + "x": [ 14 ], + "y": [ 19 ], + "type": "interactable" + }, + "Journey of the Prairie King Arcade": { + "x": [ 33 ], + "y": [ 17 ], + "type": "interactable" + }, + "Junimo Kart Arcade": { + "x": [ 35 ], + "y": [ 17 ], + "type": "interactable" + }, + "Joja Vending Machine": { + "x": [ 37, 38 ], + "y": [ 17 ], + "type": "interactable" + }, + "Jukebox": { + "x": [ 1, 2 ], + "y": [ 17 ], + "type": "interactable" + }, + "Gus's Room Door": { + "x": [ 20 ], + "y": [ 9 ], + "type": "door" + }, + "Dining Room Door": { + "x": [ 11 ], + "y": [ 9 ], + "type": "door" + }, + "Living Area Door": { + "x": [ 4 ], + "y": [ 16 ], + "type": "door" + }, + "Gus's Radio": { + "x": [ 16 ], + "y": [ 6 ], + "type": "decoration" + } + }, + "sciencehouse": { + "Exit": { + "x": [ 6 ], + "y": [ 24 ], + "type": "door" + }, + "Secondary Exit": { + "x": [ 3 ], + "y": [ 8 ], + "type": "door" + }, + "Counter": { + "x": [ 8 ], + "y": [ 19 ], + "type": "interactable" + }, + "Sebastian's Room Entrance": { + "x": [ 12 ], + "y": [ 21 ], + "type": "door" + }, + "Beaker Set": { + "x": [ 17 ], + "y": [ 17 ], + "type": "decoration" + }, + "Microscope": { + "x": [ 19 ], + "y": [ 17 ], + "type": "decoration" + }, + "Stereo Microscope": { + "x": [ 23 ], + "y": [ 20 ], + "type": "decoration" + }, + "Robin and Demetrius's Room Entrance": { + "x": [ 13 ], + "y": [ 10 ], + "type": "door" + }, + "Maru's Room Entrance": { + "x": [ 7 ], + "y": [ 10 ], + "type": "door" + }, + "Bookshelf": { + "x": [ 16, 17 ], + "y": [ 4 ], + "type": "decoration" + }, + "Maru's Device": { + "x": [ 6 ], + "y": [ 6 ], + "type": "decoration" + }, + "Poster": { + "x": [ 6 ], + "y": [ 3 ], + "type": "decoration" + }, + "Computer": { + "x": [ 9 ], + "y": [ 4 ], + "type": "decoration" + }, + "Fridge": { + "x": [ 27 ], + "y": [ 8 ], + "type": "decoration" + }, + "Oven": { + "x": [ 30 ], + "y": [ 10 ], + "type": "decoration" + } + }, + "sebastianroom": { + "Exit": { + "x": [ 1 ], + "y": [ 1 ], + "type": "door" + }, + "Room Door": { + "x": [ 1 ], + "y": [ 3 ], + "type": "door" + }, + "Sebastian's Radio": { + "x": [ 3 ], + "y": [ 4 ], + "type": "decoration" + }, + "Graphic Novel": { + "x": [ 10 ], + "y": [ 6 ], + "type": "decoration" + }, + "Computer": { + "x": [ 7 ], + "y": [ 4 ], + "type": "decoration" + } + }, + "samhouse": { + "Exit": { + "x": [ 4 ], + "y": [ 23 ], + "type": "door" + }, + "Radio": { + "x": [ 6 ], + "y": [ 12 ], + "type": "decoration" + }, + "Vincent's Room Door": { + "x": [ 16 ], + "y": [ 18 ], + "type": "door" + }, + "Sam's Room Door": { + "x": [ 12 ], + "y": [ 14 ], + "type": "door" + }, + "Jodi's Room Door": { + "x": [ 17 ], + "y": [ 6 ], + "type": "door" + }, + "Sam's Drawer": { + "x": [ 7, 8 ], + "y": [ 12 ], + "type": "decoration" + }, + "Bookshelf": { + "x": [ 18, 19 ], + "y": [ 12 ], + "type": "decoration" + }, + "Fridge": { + "x": [ 7 ], + "y": [ 4 ], + "type": "decoration" + } + }, + "haleyhouse": { + "Exit": { + "x": [ 2 ], + "y": [ 24 ], + "type": "door" + }, + "Sewing Machine": { + "x": [ 12, 13 ], + "y": [ 23 ], + "type": "interactable" + }, + "Dye Pots": { + "x": [ 17 ], + "y": [ 25 ], + "type": "interactable" + }, + "Emily's Room Door": { + "x": [ 16 ], + "y": [ 12 ], + "type": "door" + }, + "Emily's Computer": { + "x": [ 22 ], + "y": [ 6 ], + "type": "decoration" + }, + "Emily's Pet Parrot": { + "x": [ 14 ], + "y": [ 4 ], + "type": "decoration" + }, + "Magazine": { + "x": [ 4 ], + "y": [ 22 ], + "type": "decoration" + }, + "Globe": { + "x": [ 8 ], + "y": [ 15 ], + "type": "decoration" + }, + "Fridge": { + "x": [ 21 ], + "y": [ 15 ], + "type": "decoration" + }, + "Haley's Room Door": { + "x": [ 5 ], + "y": [ 13 ], + "type": "door" + }, + "Futan Bear": { + "x": [ 8 ], + "y": [ 4 ], + "type": "decoration" + }, + "Diary": { + "x": [ 9 ], + "y": [ 5 ], + "type": "decoration" + }, + "Haley's Camera": { + "x": [ 1 ], + "y": [ 9 ], + "type": "decoration" + } + }, + "joshhouse": { + "Exit": { + "x": [ 9 ], + "y": [ 24 ], + "type": "door" + }, + "TV": { + "x": [ 15, 16 ], + "y": [ 20 ], + "type": "decoration" + }, + "Bookshelf": { + "x": [ 17, 18 ], + "y": [ 16 ], + "type": "decoration" + }, + "Fridge": { + "x": [ 5 ], + "y": [ 16 ], + "type": "decoration" + }, + "Evelyn and George's Room Door": { + "x": [ 5 ], + "y": [ 9 ], + "type": "door" + }, + "Alex's Room Door": { + "x": [ 10 ], + "y": [ 10 ], + "type": "door" + }, + "Radio": { + "x": [ 3 ], + "y": [ 4 ], + "type": "door" + }, + "Magazine": { + "x": [ 11 ], + "y": [ 4 ], + "type": "door" + }, + "Alex's Bookshelf": { + "x": [ 12, 13 ], + "y": [ 4 ], + "type": "decoration" + }, + "Alex's Drawer": { + "x": [ 17, 18 ], + "y": [ 4 ], + "type": "decoration" + }, + "Dumbbell": { + "x": [ 14 ], + "y": [ 4 ], + "type": "door" + }, + "Gridball": { + "x": [ 23 ], + "y": [ 45 ], + "type": "door" + }, + "Gridball Helmet": { + "x": [ 23 ], + "y": [ 6 ], + "type": "door" + } + }, + "trailer": { + "Exit": { + "x": [ 12 ], + "y": [ 9 ], + "type": "door" + }, + "Penny's Room Door": { + "x": [ 6 ], + "y": [ 7 ], + "type": "door" + }, + "Bookshelf": { + "x": [ 5, 6 ], + "y": [ 4 ], + "type": "decoration" + }, + "Book": { + "x": [ 2 ], + "y": [ 4 ], + "type": "decoration" + }, + "Magazine": { + "x": [ 1 ], + "y": [ 9 ], + "type": "decoration" + } + } +} \ No newline at end of file From f85192178fad0b96638170c38c67b9c2ea4b3684 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Mon, 16 May 2022 21:07:08 +0530 Subject: [PATCH 160/232] Read tile pauses while auto walking --- stardew-access/Features/ReadTile.cs | 20 ++++++++++++++++++-- stardew-access/Features/TileViewer.cs | 2 ++ stardew-access/Features/Warnings.cs | 6 +++--- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index a7a5912..79fa5ad 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -34,15 +34,31 @@ namespace stardew_access.Features } ///

/// The HoeDirt to be checked + /// Ignores returning `soil` if empty /// The details about the given HoeDirt - public static string getHoeDirtDetail(HoeDirt dirt) + public static string getHoeDirtDetail(HoeDirt dirt, bool ignoreIfEmpty = false) { string detail; @@ -557,8 +558,10 @@ namespace stardew_access.Features bool isHarvestable = dirt.readyForHarvest(); bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer; - if (isWatered) + if (isWatered && MainClass.Config.WateredToggle) detail = "Watered " + detail; + else if (!isWatered && !MainClass.Config.WateredToggle) + detail = "Unwatered " + detail; if (isFertilized) detail = "Fertilized " + detail; @@ -580,12 +583,14 @@ namespace stardew_access.Features } else { - detail = "Soil"; + detail = (ignoreIfEmpty) ? "" : "Soil"; bool isWatered = dirt.state.Value == HoeDirt.watered; bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer; - if (isWatered) + if (isWatered && MainClass.Config.WateredToggle) detail = "Watered " + detail; + else if (!isWatered && !MainClass.Config.WateredToggle) + detail = "Unwatered " + detail; if (isFertilized) detail = "Fertilized " + detail; @@ -709,7 +714,7 @@ namespace stardew_access.Features } else if (obj is IndoorPot indoorPot) { - toReturn.name = $"{obj.DisplayName}, {getHoeDirtDetail(indoorPot.hoeDirt)}"; + toReturn.name = $"{obj.DisplayName}, {getHoeDirtDetail(indoorPot.hoeDirt, true)}"; } else if (obj is Sign sign) { diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index c77e9df..48e1490 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -23,6 +23,7 @@ namespace stardew_access public KeybindList ReadTileKey { get; set; } = KeybindList.Parse("J"); // Manually trigger read tile for the tile player is *looking at*. public KeybindList ReadStandingTileKey { get; set; } = KeybindList.Parse("LeftAlt + J"); // Manually trigger read tile for the tile player is *standing on*. public Boolean ReadFlooring { get; set; } = false; // Toggle reading floorings. + public Boolean WateredToggle { get; set; } = true; // Toggle speaking watered or unwatered for crops. #endregion #region Tile viewer From d14af408dfcb4df2b91e6b7722aee53f33d683d3 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Thu, 18 Aug 2022 12:37:32 +0530 Subject: [PATCH 184/232] Added command to toggle warnings feature --- stardew-access/CustomCommands.cs | 46 ++++++++++++++++++----------- stardew-access/Features/ReadTile.cs | 3 -- stardew-access/ModConfig.cs | 1 + stardew-access/ModEntry.cs | 6 ++-- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index d6d51b6..7f231ad 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -15,21 +15,14 @@ namespace stardew_access if (helper == null) return; + #region Read Tile helper.ConsoleCommands.Add("readtile", "Toggle read tile feature.", (string commmand, string[] args) => - { - MainClass.Config.ReadTile = !MainClass.Config.ReadTile; - helper.WriteConfig(MainClass.Config); + { + MainClass.Config.ReadTile = !MainClass.Config.ReadTile; + helper.WriteConfig(MainClass.Config); - MainClass.DebugLog("Read Tile is " + (MainClass.Config.ReadTile ? "on" : "off")); - }); - - helper.ConsoleCommands.Add("snapmouse", "Toggle snap mouse feature.", (string commmand, string[] args) => - { - MainClass.Config.SnapMouse = !MainClass.Config.SnapMouse; - helper.WriteConfig(MainClass.Config); - - MainClass.DebugLog("Snap Mouse is " + (MainClass.Config.SnapMouse ? "on" : "off")); - }); + MainClass.DebugLog("Read Tile is " + (MainClass.Config.ReadTile ? "on" : "off")); + }); helper.ConsoleCommands.Add("flooring", "Toggle flooring in read tile.", (string commmand, string[] args) => { @@ -46,6 +39,7 @@ namespace stardew_access MainClass.DebugLog("Watered toggle is " + (MainClass.Config.WateredToggle ? "on" : "off")); }); + #endregion #region Radar Feature helper.ConsoleCommands.Add("radar", "Toggle radar feature.", (string commmand, string[] args) => @@ -482,12 +476,13 @@ namespace stardew_access }); #endregion + #region Other helper.ConsoleCommands.Add("refsr", "Refresh screen reader", (string commmand, string[] args) => - { - MainClass.ScreenReader.InitializeScreenReader(); + { + MainClass.ScreenReader.InitializeScreenReader(); - MainClass.DebugLog("Screen Reader refreshed!"); - }); + MainClass.DebugLog("Screen Reader refreshed!"); + }); helper.ConsoleCommands.Add("refmc", "Refresh mod config", (string commmand, string[] args) => { @@ -510,6 +505,23 @@ namespace stardew_access MainClass.DebugLog("Speaking in percentage is " + (MainClass.Config.HealthNStaminaInPercentage ? "on" : "off")); }); + + helper.ConsoleCommands.Add("snapmouse", "Toggle snap mouse feature.", (string commmand, string[] args) => + { + MainClass.Config.SnapMouse = !MainClass.Config.SnapMouse; + helper.WriteConfig(MainClass.Config); + + MainClass.DebugLog("Snap Mouse is " + (MainClass.Config.SnapMouse ? "on" : "off")); + }); + + helper.ConsoleCommands.Add("warning", "Toggle warnings feature.", (string commmand, string[] args) => + { + MainClass.Config.Warning = !MainClass.Config.Warning; + helper.WriteConfig(MainClass.Config); + + MainClass.DebugLog("Warnings is " + (MainClass.Config.Warning ? "on" : "off")); + }); + #endregion } } } diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 7cdb846..01121b3 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -28,9 +28,6 @@ namespace stardew_access.Features if (this.shouldPause) return; - if (!MainClass.Config.ReadTile) - return; - this.isBusy = true; this.run(); Task.Delay(delay).ContinueWith(_ => { this.isBusy = false; }); diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index 48e1490..76f057d 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -77,6 +77,7 @@ namespace stardew_access public KeybindList TimeNSeasonKey { get; set; } = KeybindList.Parse("Q"); // Narrate the time of day, day and date and season public Boolean VerboseCoordinates { get; set; } = true; public Boolean SnapMouse { get; set; } = true; // Toggles the snap mouse feature + public Boolean Warning { get; set; } = true; // Toggles the warnings feature // TODO add command to toggle warning feature #endregion diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index f9de5d3..03cdc36 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -168,9 +168,11 @@ namespace stardew_access //handle TileCursor update logic TileViewerFeature.update(); - WarningsFeature.update(); + if (Config.Warning) + WarningsFeature.update(); - ReadTileFeature.update(); + if (Config.ReadTile) + ReadTileFeature.update(); if (!RadarFeature.isRunning && Config.Radar) { From 6f1ccdb951ee7a82e913aca98c73634ac81c144e Mon Sep 17 00:00:00 2001 From: Bartholomue Date: Fri, 19 Aug 2022 10:05:23 -0500 Subject: [PATCH 185/232] Updated Static Tiles Updated tile definitions for multiple locations, recategorizing some for easier object selection without clutter. --- stardew-access/assets/static-tiles.json | 4992 +++++++++++------------ 1 file changed, 2290 insertions(+), 2702 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index dc0d299..170ba54 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -1,2705 +1,2293 @@ { - "farm": - { - "Bus Stop Entrance": - { - "x":[79], - "y":[15,16,17,18], - "type":"door" - }, - "Backwoods Entrance": - { - "x":[40,41], - "y":[0], - "type":"door" - }, - "Cindersap Forest Entrance": - { - "x":[40,41], - "y":[64], - "type":"door" - }, - "Farm Cave Entrance": - { - "x":[34], - "y":[7], - "type":"door" - }, - "Grandpa's Shrine": - { - "x":[8], - "y":[7], - "type":"interactable" - } - }, - "farmcave": - { - "Exit": - { - "x":[8], - "y":[11], - "type":"door" - } - }, - "busstop": - { - "Ticket Machine": - { - "x":[7], - "y":[11], - "type":"interactable" - }, - "Minecart": - { - "x":[4,5], - "y":[3], - "type":"interactable" - }, - "Farm Entrance": - { - "x":[0], - "y":[23], - "type":"door" - }, - "Town Entrance": - { - "x":[34], - "y":[23], - "type":"door" - }, - "Backwoods Entrance": - { - "x":[0], - "y":[6,7,8,9], - "type":"door" - } - }, - "town": - { - "Calender Board": - { - "x":[41], - "y":[56], - "type":"interactable" - }, - "Daily Quest Board": - { - "x":[42], - "y":[56], - "type":"interactable" - }, - "Sewer": - { - "x":[34,35], - "y":[95,96], - "type":"interactable" - }, - "Ice Cream Stand": - { - "x":[88], - "y":[92], - "type":"interactable" - }, - "Minecart": - { - "x":[105,106], - "y":[79], - "type":"interactable" - }, - "Bus Stop Entrance": - { - "x":[0], - "y":[54], - "type":"door" - }, - "Cindersap Forest Entrance": - { - "x":[0], - "y":[90], - "type":"door" - }, - "Beach Entrance": - { - "x":[54], - "y":[109], - "type":"door" - }, - "Mountain Entrance": - { - "x":[81], - "y":[0], - "type":"door" - } - }, - "shed": - { - "Exit": - { - "x":[6], - "y":[13], - "type":"door" - } - }, - "coop": - { - "Hay Hopper": - { - "x":[3], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[7], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[2], - "y":[9], - "type":"door" - } - }, - "big coop": - { - "Hay Hopper": - { - "x":[3], - "y":[3], - "type":"interactable" - }, - "Incubator": - { - "x":[2], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[7], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[2], - "y":[9], - "type":"door" - } - }, - "coop2": - { - "Hay Hopper": - { - "x":[3], - "y":[3], - "type":"interactable" - }, - "Incubator": - { - "x":[2], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[7], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[2], - "y":[9], - "type":"door" - } - }, - "deluxe coop": - { - "Hay Hopper": - { - "x":[3], - "y":[3], - "type":"interactable" - }, - "Incubator": - { - "x":[2], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[7], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 9": - { - "x":[14], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 10": - { - "x":[15], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 11": - { - "x":[16], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 12": - { - "x":[17], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[2], - "y":[9], - "type":"door" - } - }, - "coop3": - { - "Hay Hopper": - { - "x":[3], - "y":[3], - "type":"interactable" - }, - "Incubator": - { - "x":[2], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[7], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 9": - { - "x":[14], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 10": - { - "x":[15], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 11": - { - "x":[16], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 12": - { - "x":[17], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[2], - "y":[9], - "type":"door" - } - }, - "barn": - { - "Hay Hopper": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[11], - "y":[14], - "type":"door" - } - }, - "barn2": - { - "Hay Hopper": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[14], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[15], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[11], - "y":[14], - "type":"door" - } - }, - "big barn": - { - "Hay Hopper": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[14], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[15], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[11], - "y":[14], - "type":"door" - } - }, - "barn3": - { - "Hay Hopper": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[14], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[15], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 9": - { - "x":[16], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 10": - { - "x":[17], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 11": - { - "x":[18], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 12": - { - "x":[19], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[11], - "y":[14], - "type":"door" - } - }, - "deluxe barn": - { - "Hay Hopper": - { - "x":[6], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 1": - { - "x":[8], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 2": - { - "x":[9], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 3": - { - "x":[10], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 4": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 5": - { - "x":[12], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 6": - { - "x":[13], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 7": - { - "x":[14], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 8": - { - "x":[15], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 9": - { - "x":[16], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 10": - { - "x":[17], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 11": - { - "x":[18], - "y":[3], - "type":"interactable" - }, - "Feeding Bench 12": - { - "x":[19], - "y":[3], - "type":"interactable" - }, - "Exit": - { - "x":[11], - "y":[14], - "type":"door" - } - }, - "slime hutch": - { - "Water Trough 1": - { - "x":[16], - "y":[6], - "type":"interactable" - }, - "Water Trough 2": - { - "x":[16], - "y":[7], - "type":"interactable" - }, - "Water Trough 3": - { - "x":[16], - "y":[8], - "type":"interactable" - }, - "Water Trough 4": - { - "x":[16], - "y":[9], - "type":"interactable" - }, - "Exit": - { - "x":[8], - "y":[12], - "type":"door" - } - }, - "adventureguild": - { - "Goals Board": - { - "x":[8], - "y":[10], - "type":"interactable" - }, - "Shop Counter": - { - "x":[5], - "y":[12], - "type":"interactable" - }, - "Gil": - { - "x":[11], - "y":[12], - "type":"npc" - }, - "Exit": - { - "x":[6], - "y":[19], - "type":"door" - } - }, - "caldera": - { - "Rare Chest": - { - "x":[25], - "y":[28], - "type":"chest" - }, - "Forge": - { - "x":[23], - "y":[21], - "type":"interactable" - }, - "Volcano Dungeon 0 Entrance": - { - "x":[11], - "y":[36], - "type":"door" - }, - "Volcano Dungeon 9 Entrance": - { - "x":[21], - "y":[39], - "type":"door" - } - }, - "volcanodungeon0": - { - "Island North Entrance 1": - { - "x":[31], - "y":[54], - "type":"door" - }, - "Island North Entrance 2": - { - "x":[6], - "y":[50], - "type":"door" - }, - "Caldera Entrance": - { - "x":[44], - "y":[50], - "type":"door" - }, - "Volcano Dungeon 1 Entrance": - { - "x":[37], - "y":[5], - "type":"door" - } - }, - "club": - { - "Coin Machine": - { - "x":[12], - "y":[4], - "type":"interactable" - }, - "Shop Counter": - { - "x":[25], - "y":[3], - "type":"interactable" - }, - "Calico Spin Machine": - { - "x":[11,13,15], - "y":[8,10], - "type":"interactable" - }, - "High Stakes Calico Jack Table": - { - "x":[23,24], - "y":[10,11], - "type":"interactable" - }, - "Low Stakes Calico Jack Table": - { - "x":[3], - "y":[7,9], - "type":"interactable" - }, - "Man": - { - "x":[13], - "y":[11], - "type":"npc" - }, - "Welwick": - { - "x":[18], - "y":[9], - "type":"npc" - }, - "Unknown person": - { - "x":[16], - "y":[4], - "type":"npc" - }, - "Stats Checker": - { - "x":[3], - "y":[4], - "type":"interactable" - }, - "Exit": - { - "x":[8], - "y":[12], - "type":"door" - } - }, - "desert": - { - "Bus": - { - "x":[18], - "y":[27], - "type":"interactable" - }, - "Desert Trader": - { - "x":[42], - "y":[23], - "type":"interactable" - }, - "Three Pillars": - { - "x":[34,37,40], - "y":[8,13], - "type":"decoration" - }, - "Three Pillars Center": - { - "x":[37], - "y":[11], - "type":"interactable" - }, - "Skull Cavern Entrance": - { - "x":[8], - "y":[6], - "type":"door" - }, - "Desert Warp Statue": - { - "x":[35], - "y":[43], - "type":"decoration" - }, - "Sand Dragon Skull": - { - "x":[9,10], - "y":[35,36], - "type":"decoration" - } - }, - "fishshop": - { - "Shop Counter": - { - "x":[5], - "y":[5], - "type":"interactable" - }, - "Exit": - { - "x":[5], - "y":[9], - "type":"door" - } - }, - "boattunnel": - { - "Exit": - { - "x":[6], - "y":[11], - "type":"door" - } - }, - "beach": - { - "Town Entrance": - { - "x":[38], - "y":[0], - "type":"door" - }, - "Beach Warp Statue": - { - "x":[20], - "y":[4], - "type":"decoration" - } - }, - "forest": - { - "Farm Entrance": - { - "x":[68], - "y":[0], - "type":"door" - }, - "Town Entrance": - { - "x":[119], - "y":[25], - "type":"door" - }, - "Bridge 1": - { - "x":[77,82], - "y":[49], - "type":"bridge" - }, - "Bridge 2": - { - "x":[87], - "y":[52,56], - "type":"bridge" - }, - "Bridge 3": - { - "x":[65,62], - "y":[70], - "type":"bridge" - }, - "Bridge 4": - { - "x":[41], - "y":[79,82], - "type":"bridge" - }, - "Bridge 5": - { - "x":[38], - "y":[85,87], - "type":"bridge" - }, - "Abandoned House": - { - "x":[34], - "y":[95], - "type":"interactable" - } - }, - "beachnightmarket": - { - "Desert Trader": - { - "x":[14], - "y":[37], - "type":"npc" - }, - "Famous Painter Lupini": - { - "x":[43], - "y":[34], - "type":"npc" - }, - "Fishing Submarine Door": - { - "x":[5], - "y":[34], - "type":"door" - }, - "Travelling Cart": - { - "x":[39], - "y":[30], - "type":"interactable" - }, - "Shrouded Figure": - { - "x":[32], - "y":[34], - "type":"npc" - }, - "Decoration Boat": - { - "x":[19], - "y":[33], - "type":"interactable" - }, - "Magic Shop Boat": - { - "x":[48], - "y":[34], - "type":"interactable" - }, - "Mermaid Boat Door": - { - "x":[58], - "y":[31], - "type":"door" - } - }, - "mermaidhouse": - { - "Exit": - { - "x":[4], - "y":[10], - "type":"door" - }, - "Clam Shell 1": - { - "x":[2], - "y":[6], - "type":"interactable" - }, - "Clam Shell 2": - { - "x":[3], - "y":[6], - "type":"interactable" - }, - "Clam Shell 3": - { - "x":[4], - "y":[6], - "type":"interactable" - }, - "Clam Shell 4": - { - "x":[5], - "y":[6], - "type":"interactable" - }, - "Clam Shell 5": - { - "x":[6], - "y":[6], - "type":"interactable" - } - }, - "submarine": - { - "Exit": - { - "x":[14], - "y":[15], - "type":"door" - }, - "Captain": - { - "x":[2], - "y":[9], - "type":"npc" - } - }, - "cellar": - { - "Exit": - { - "x":[3], - "y":[2], - "type":"door" - } - }, - "communitycenter": - { - "Exit": - { - "x":[32], - "y":[23], - "type":"door" - } - }, - "islandeast": - { - "Banana Shrine": - { - "x":[16], - "y":[26], - "type":"interactable" - }, - "Jungle Parrot Express": - { - "x":[28], - "y":[27], - "type":"interactable" - }, - "Island Hut Entrance": - { - "x":[22], - "y":[10], - "type":"door" - }, - "Island South Entrance": - { - "x":[0], - "y":[46], - "type":"door" - }, - "Island Shrine Entrance": - { - "x":[32], - "y":[30], - "type":"door" - } - }, - "islandhut": - { - "Exit": - { - "x":[7], - "y":[13], - "type":"door" - } - }, - "islandsouth": - { - "Island East Entrance": - { - "x":[34], - "y":[12], - "type":"door" - }, - "Ginger Island Warp Statue": - { - "x":[11], - "y":[11], - "type":"decoration" - }, - "Island West Entrance": - { - "x":[0], - "y":[11], - "type":"door" - }, - "Island North Entrance": - { - "x":[17,18,19], - "y":[0], - "type":"door" - }, - "Island North Entrance 2": - { - "x":[27,28], - "y":[0], - "type":"door" - }, - "Docks Parrot Express": - { - "x":[6], - "y":[31], - "type":"interactable" - }, - "Return Boat": - { - "x":[19], - "y":[43], - "type":"interactable" - } - }, - "islandwest": - { - "Farm Parrot Express": - { - "x":[74], - "y":[9], - "type":"interactable" - }, - "Bridge 1": - { - "x":[67,62], - "y":[16], - "type":"bridge" - }, - "Qi's Walnut Room Door": - { - "x":[20], - "y":[22], - "type":"door" - }, - "Door to Shipwreck Interior": - { - "x":[60], - "y":[92], - "type":"door" - }, - "Hole 1": - { - "x":[37], - "y":[87], - "type":"interactable" - }, - "Hole 2": - { - "x":[41], - "y":[86], - "type":"interactable" - }, - "Hole 3": - { - "x":[45], - "y":[86], - "type":"interactable" - }, - "Hole 4": - { - "x":[48], - "y":[87], - "type":"interactable" - }, - "Bridge 2": - { - "x":[55,52], - "y":[80], - "type":"bridge" - }, - "Island Farm House Door": - { - "x":[77], - "y":[39], - "type":"door" - }, - "Island Farm Cave Entrance": - { - "x":[96], - "y":[33], - "type":"door" - }, - "Island South Entrance": - { - "x":[105], - "y":[40], - "type":"door" - } - }, - "captainroom": - { - "Exit": - { - "x":[0], - "y":[5], - "type":"door" - } - }, - "islandfarmcave": - { - "Exit": - { - "x":[4], - "y":[10], - "type":"door" - }, - "Gourmand Frog": - { - "x":[5], - "y":[4], - "type":"npc" - } - }, - "islandnorth": - { - "Island South Entrance": - { - "x":[35,36,37], - "y":[89], - "type":"door" - }, - "Island South Entrance 2": - { - "x":[43,44], - "y":[89], - "type":"door" - }, - "Island Field Office Entrance": - { - "x":[46], - "y":[46], - "type":"door" - }, - "Island North Cave Entrance": - { - "x":[21,22], - "y":[47], - "type":"door" - }, - "Dig Site Parrot Express": - { - "x":[5], - "y":[48], - "type":"interactable" - }, - "Volcano Dungeon Entrance": - { - "x":[39,40,41,42], - "y":[21], - "type":"door" - }, - "Volcano Dungeon Entrance 2": - { - "x":[12], - "y":[31], - "type":"door" - }, - "Volcano Parrot Express": - { - "x":[60], - "y":[16], - "type":"interactable" - } - }, - "islandfieldoffice": - { - "Exit": - { - "x":[4], - "y":[10], - "type":"door" - }, - "Counter": - { - "x":[8], - "y":[7], - "type":"interactable" - }, - "Island Survey": - { - "x":[5], - "y":[3], - "type":"interactable" - } - }, - "qinutroom": - { - "Exit": - { - "x":[7], - "y":[7], - "type":"door" - }, - "Perfection Tracker": - { - "x":[13], - "y":[4], - "type":"interactable" - }, - "Vending Machine": - { - "x":[11], - "y":[3], - "type":"interactable" - }, - "Special Order Board": - { - "x":[3], - "y":[3], - "type":"interactable" - } - }, - "islandsoutheast": - { - "Island South East Cave Entrance": - { - "x":[30], - "y":[18], - "type":"door" - } - }, - "islandsoutheastcave": - { - "Exit": - { - "x":[1], - "y":[8], - "type":"door" - } - }, - "islandshrine": - { - "Exit": - { - "x":[13], - "y":[28], - "type":"door" - }, - "Shrine": - { - "x":[24], - "y":[22], - "type":"interactable" - }, - "North Pedestal": - { - "x":[24], - "y":[25], - "type":"interactable" - }, - "East Pedestal": - { - "x":[27], - "y":[27], - "type":"interactable" - }, - "West Pedestal": - { - "x":[21], - "y":[27], - "type":"interactable" - }, - "South Pedestal": - { - "x":[24], - "y":[28], - "type":"interactable" - } - }, - "jojamart": - { - "Exit": - { - "x":[13], - "y":[29], - "type":"door" - }, - "Morris's Kiosk": - { - "x":[21], - "y":[25], - "type":"interactable" - }, - "Shop Counter": - { - "x":[10], - "y":[25], - "type":"interactable" - } - }, - "archaeologyhouse": - { - "Exit": - { - "x":[3], - "y":[14], - "type":"door" - }, - "Counter": - { - "x":[3], - "y":[9], - "type":"interactable" - } - }, - "manorhouse": - { - "Exit": - { - "x":[4], - "y":[11], - "type":"door" - }, - "Town Ledger Book": - { - "x":[2], - "y":[5], - "type":"interactable" - }, - "Marriage Log Book": - { - "x":[3], - "y":[5], - "type":"interactable" - }, - "Lost and Found Box": - { - "x":[4], - "y":[5], - "type":"interactable" - }, - "Mayor's Room Door": - { - "x":[16], - "y":[9], - "type":"door" - }, - "Mayor's Oven": - { - "x":[7], - "y":[4], - "type":"decoration" - }, - "Mayor's Fridge": - { - "x":[9], - "y":[4], - "type":"decoration" - } - }, - "mine": - { - "Mountain Exit": - { - "x":[18], - "y":[13], - "type":"door" - }, - "Minecart": - { - "x":[11,12], - "y":[10], - "type":"interactable" - }, - "Quarry Mine Ladder": - { - "x":[67], - "y":[9], - "type":"door" - }, - "Quarry Exit": - { - "x":[18], - "y":[13], - "type":"door" - } - }, - "mountain": - { - "Mine Entrance": - { - "x":[54], - "y":[5], - "type":"door" - }, - "Mine Bridge": - { - "x":[47], - "y":[7], - "type":"bridge" - }, - "Quarry Bridge": - { - "x":[90], - "y":[26], - "type":"bridge" - }, - "Minecart": - { - "x":[124,125], - "y":[11], - "type":"interactable" - }, - "Quarry Mine Entrance": - { - "x":[103], - "y":[17], - "type":"door" - }, - "Bridge 1": - { - "x":[57], - "y":[30], - "type":"bridge" - }, - "Bridge 2": - { - "x":[61], - "y":[21], - "type":"bridge" - }, - "Mountain Warp Statue": - { - "x":[31], - "y":[20], - "type":"decoration" - }, - "Linus Tent Entrance": - { - "x":[29], - "y":[7], - "type":"door" - }, - "Backwoods Entrance": - { - "x":[0], - "y":[13], - "type":"door" - }, - "Town Entrance": - { - "x":[15], - "y":[40], - "type":"door" - }, - "Railroad Entrance": - { - "x":[9], - "y":[0], - "type":"door" - }, - "Science House Secondary Door": - { - "x":[8], - "y":[20], - "type":"door" - } - }, - "undergroundmine77377": - { - "Grim Reaper Statue": - { - "x":[29,30], - "y":[6], - "type":"interactable" - } - }, - "Tent": - { - "Exit": - { - "x":[2], - "y":[5], - "type":"door" - } - }, - "railroad": - { - "Mountain Entrance": - { - "x":[29], - "y":[61], - "type":"door" - } - }, - "backwoods": - { - "Mountain Entrance": - { - "x":[49], - "y":[14], - "type":"door" - }, - "Farm Entrance": - { - "x":[14], - "y":[39], - "type":"door" - }, - "Bus stop Entrance": - { - "x":[49], - "y":[28,29,30,31,32], - "type":"door" - }, - "Tunnel Entrance": - { - "x":[23], - "y":[29,30,31], - "type":"door" - } - }, - "tunnel": - { - "Exit": - { - "x":[39], - "y":[7,8,9,10,11], - "type":"door" - } - }, - "movietheater": - { - "Exit": - { - "x":[13], - "y":[15], - "type":"door" - }, - "Concessions Counter": - { - "x":[7], - "y":[6], - "type":"interactable" - }, - "Crane Game": - { - "x":[1,2], - "y":[8], - "type":"interactable" - }, - "Theater Door": - { - "x":[13], - "y":[3], - "type":"door" - }, - "Crane Man": - { - "x":[2], - "y":[9], - "type":"npc" - } - }, - "seedshop": - { - "Exit": - { - "x":[6], - "y":[29], - "type":"door" - }, - "Shop Counter": - { - "x":[4], - "y":[18], - "type":"interactable" - }, - "Backpack Upgrade": - { - "x":[7], - "y":[18], - "type":"interactable" - }, - "Shrine of Yoba": - { - "x":[37], - "y":[17], - "type":"decoration" - }, - "Fridge": - { - "x":[39], - "y":[4], - "type":"decoration" - }, - "Abigail's Room Door": - { - "x":[13], - "y":[11], - "type":"door" - }, - "Pierre and Caroline's Room Door": - { - "x":[20], - "y":[11], - "type":"door" - }, - "Living Area Door": - { - "x":[14], - "y":[16], - "type":"door" - } - }, - "sewer": - { - "Exit Ladder": - { - "x":[16], - "y":[10], - "type":"door" - }, - "Statue Of Uncertainty": - { - "x":[8], - "y":[20], - "type":"interactable" - }, - "Mutant Bug Lair": - { - "x":[3], - "y":[19], - "type":"door" - } - }, - "wizardhouse": - { - "Exit": - { - "x":[8], - "y":[24], - "type":"door" - }, - "Basement Door": - { - "x":[4], - "y":[4], - "type":"door" - } - }, - "wizardhousebasement": - { - "Exit Ladder": - { - "x":[4], - "y":[3], - "type":"door" - }, - "Shrine of Illusions": - { - "x":[12], - "y":[4], - "type":"interactable" - } - }, - "woods": - { - "Forest Entrance": - { - "x":[59], - "y":[17], - "type":"door" - }, - "Old Master Cannoli": - { - "x":[8,9], - "y":[7], - "type":"interactable" - } - }, - "harveyroom": - { - "Exit": - { - "x":[6], - "y":[12], - "type":"door" - }, - "Fridge": - { - "x":[21], - "y":[6], - "type":"decoration" - }, - "Oven": - { - "x":[19], - "y":[6], - "type":"decoration" - }, - "Airplane Collection": - { - "x":[6,7], - "y":[3], - "type":"decoration" - }, - "Radio Broadcasting Set": - { - "x":[4,5], - "y":[4], - "type":"decoration" - }, - "Cassette Deck": - { - "x":[8], - "y":[4], - "type":"decoration" - } - }, - "hospital": - { - "Exit": - { - "x":[10], - "y":[19], - "type":"door" - }, - "Harvey's Room Entrance": - { - "x":[10], - "y":[2], - "type":"door" - }, - "Harvey's Room Entrance Door": - { - "x":[10], - "y":[5], - "type":"door" - }, - "Main Area Door": - { - "x":[10], - "y":[13], - "type":"door" - }, - "Counter": - { - "x":[6], - "y":[16], - "type":"interactable" - } - }, - "blacksmith": - { - "Exit": - { - "x":[5], - "y":[19], - "type":"door" - }, - "Counter": - { - "x":[3], - "y":[14], - "type":"interactable" - }, - "Clint's Room Door": - { - "x":[4], - "y":[9], - "type":"door" - }, - "Clint's Furnace": - { - "x":[9,10], - "y":[12], - "type":"decoration" - }, - "Blueprints": - { - "x":[13], - "y":[15,16], - "type":"decoration" - }, - "Anvil": - { - "x":[12,13], - "y":[13], - "type":"decoration" - }, - "Cassette Deck": - { - "x":[2], - "y":[4], - "type":"decoration" - }, - "Clint's Drawer": - { - "x":[5,6], - "y":[4], - "type":"decoration" - } - }, - "animalshop": - { - "Exit": - { - "x":[13], - "y":[19], - "type":"door" - }, - "Counter": - { - "x":[12], - "y":[15], - "type":"interactable" - }, - "Marnie's Room Door": - { - "x":[15], - "y":[12], - "type":"door" - }, - "Jas's Room Door": - { - "x":[6], - "y":[13], - "type":"door" - }, - "Shane's Room Door": - { - "x":[21], - "y":[13], - "type":"door" - }, - "Marnie's Barn Door": - { - "x":[30], - "y":[13], - "type":"door" - }, - "Fridge": - { - "x":[28], - "y":[14], - "type":"decoration" - }, - "Oven": - { - "x":[24], - "y":[14], - "type":"decoration" - }, - "Mega Station": - { - "x":[22], - "y":[5], - "type":"decoration" - }, - "Shane's Radio": - { - "x":[24], - "y":[4], - "type":"decoration" - }, - "Marnie's Dresser": - { - "x":[16], - "y":[4], - "type":"decoration" - }, - "Marnie's Drawer": - { - "x":[17], - "y":[4], - "type":"decoration" - }, - "Jack in the Box": - { - "x":[8], - "y":[5], - "type":"decoration" - }, - "Futan Bear": - { - "x":[2,3], - "y":[4], - "type":"decoration" - }, - "Colouring Book": - { - "x":[5], - "y":[4], - "type":"decoration" - }, - "Paint Set": - { - "x":[5], - "y":[7], - "type":"decoration" - }, - "Jas's Alarm Clock": - { - "x":[8], - "y":[8], - "type":"decoration" - }, - "Jas's Radio": - { - "x":[4], - "y":[9], - "type":"decoration" - }, - "Arts And Craft": - { - "x":[7], - "y":[6], - "type":"decoration" - }, - "Doll House": - { - "x":[6,7], - "y":[4], - "type":"decoration" - } - }, - "saloon": - { - "Exit": - { - "x":[14], - "y":[24], - "type":"door" - }, - "Counter": - { - "x":[14], - "y":[19], - "type":"interactable" - }, - "Journey of the Prairie King Arcade": - { - "x":[33], - "y":[17], - "type":"interactable" - }, - "Junimo Kart Arcade": - { - "x":[35], - "y":[17], - "type":"interactable" - }, - "Joja Vending Machine": - { - "x":[37,38], - "y":[17], - "type":"interactable" - }, - "Jukebox": - { - "x":[1,2], - "y":[17], - "type":"interactable" - }, - "Gus's Room Door": - { - "x":[20], - "y":[9], - "type":"door" - }, - "Dining Room Door": - { - "x":[11], - "y":[9], - "type":"door" - }, - "Living Area Door": - { - "x":[4], - "y":[16], - "type":"door" - }, - "Gus's Radio": - { - "x":[16], - "y":[6], - "type":"decoration" - } - }, - "sciencehouse": - { - "Exit": - { - "x":[6], - "y":[24], - "type":"door" - }, - "Secondary Exit": - { - "x":[3], - "y":[8], - "type":"door" - }, - "Counter": - { - "x":[8], - "y":[19], - "type":"interactable" - }, - "Sebastian's Room Entrance": - { - "x":[12], - "y":[21], - "type":"door" - }, - "Beaker Set": - { - "x":[17], - "y":[17], - "type":"decoration" - }, - "Microscope": - { - "x":[19], - "y":[17], - "type":"decoration" - }, - "Stereo Microscope": - { - "x":[23], - "y":[20], - "type":"decoration" - }, - "Robin and Demetrius's Room Entrance": - { - "x":[13], - "y":[10], - "type":"door" - }, - "Maru's Room Entrance": - { - "x":[7], - "y":[10], - "type":"door" - }, - "Bookshelf": - { - "x":[16,17], - "y":[4], - "type":"decoration" - }, - "Maru's Device": - { - "x":[6], - "y":[6], - "type":"decoration" - }, - "Poster": - { - "x":[6], - "y":[3], - "type":"decoration" - }, - "Computer": - { - "x":[9], - "y":[4], - "type":"decoration" - }, - "Fridge": - { - "x":[27], - "y":[8], - "type":"decoration" - }, - "Oven": - { - "x":[30], - "y":[10], - "type":"decoration" - } - }, - "sebastianroom": - { - "Exit": - { - "x":[1], - "y":[1], - "type":"door" - }, - "Room Door": - { - "x":[1], - "y":[3], - "type":"door" - }, - "Sebastian's Radio": - { - "x":[3], - "y":[4], - "type":"decoration" - }, - "Graphic Novel": - { - "x":[10], - "y":[6], - "type":"decoration" - }, - "Computer": - { - "x":[7], - "y":[4], - "type":"decoration" - } - }, - "samhouse": - { - "Exit": - { - "x":[4], - "y":[23], - "type":"door" - }, - "Radio": - { - "x":[6], - "y":[12], - "type":"decoration" - }, - "Vincent's Room Door": - { - "x":[16], - "y":[18], - "type":"door" - }, - "Sam's Room Door": - { - "x":[12], - "y":[14], - "type":"door" - }, - "Jodi's Room Door": - { - "x":[17], - "y":[6], - "type":"door" - }, - "Sam's Drawer": - { - "x":[7,8], - "y":[12], - "type":"decoration" - }, - "Bookshelf": - { - "x":[18,19], - "y":[12], - "type":"decoration" - }, - "Fridge": - { - "x":[7], - "y":[4], - "type":"decoration" - } - }, - "haleyhouse": - { - "Exit": - { - "x":[2], - "y":[24], - "type":"door" - }, - "Sewing Machine": - { - "x":[12,13], - "y":[23], - "type":"interactable" - }, - "Dye Pots": - { - "x":[17], - "y":[25], - "type":"interactable" - }, - "Emily's Room Door": - { - "x":[16], - "y":[12], - "type":"door" - }, - "Emily's Computer": - { - "x":[22], - "y":[6], - "type":"decoration" - }, - "Emily's Pet Parrot": - { - "x":[14], - "y":[4], - "type":"decoration" - }, - "Magazine": - { - "x":[4], - "y":[22], - "type":"decoration" - }, - "Globe": - { - "x":[8], - "y":[15], - "type":"decoration" - }, - "Fridge": - { - "x":[21], - "y":[15], - "type":"decoration" - }, - "Haley's Room Door": - { - "x":[5], - "y":[13], - "type":"door" - }, - "Futan Bear": - { - "x":[8], - "y":[4], - "type":"decoration" - }, - "Diary": - { - "x":[9], - "y":[5], - "type":"decoration" - }, - "Haley's Camera": - { - "x":[1], - "y":[9], - "type":"decoration" - } - }, - "joshhouse": - { - "Exit": - { - "x":[9], - "y":[24], - "type":"door" - }, - "TV": - { - "x":[15,16], - "y":[20], - "type":"decoration" - }, - "Bookshelf": - { - "x":[17,18], - "y":[16], - "type":"decoration" - }, - "Fridge": - { - "x":[5], - "y":[16], - "type":"decoration" - }, - "Evelyn and George's Room Door": - { - "x":[5], - "y":[9], - "type":"door" - }, - "Alex's Room Door": - { - "x":[10], - "y":[10], - "type":"door" - }, - "Radio": - { - "x":[3], - "y":[4], - "type":"door" - }, - "Magazine": - { - "x":[11], - "y":[4], - "type":"door" - }, - "Alex's Bookshelf": - { - "x":[12,13], - "y":[4], - "type":"decoration" - }, - "Alex's Drawer": - { - "x":[17,18], - "y":[4], - "type":"decoration" - }, - "Dumbbell": - { - "x":[14], - "y":[4], - "type":"door" - }, - "Gridball": - { - "x":[23], - "y":[45], - "type":"door" - }, - "Gridball Helmet": - { - "x":[23], - "y":[6], - "type":"door" - } - }, - "trailer": - { - "Exit": - { - "x":[12], - "y":[9], - "type":"door" - }, - "Penny's Room Door": - { - "x":[6], - "y":[7], - "type":"door" - }, - "Bookshelf": - { - "x":[5,6], - "y":[4], - "type":"decoration" - }, - "Book": - { - "x":[2], - "y":[4], - "type":"decoration" - }, - "Magazine": - { - "x":[1], - "y":[9], - "type":"decoration" - } + "adventureguild": { + "Exit": { + "x": [6], + "y": [19], + "type": "door" + }, + "Goals Board": { + "x": [8], + "y": [10], + "type": "interactable" + }, + "Shop Counter": { + "x": [5], + "y": [12], + "type": "interactable" + }, + "Gil": { + "x": [11], + "y": [12], + "type": "npc" } + }, + "animalshop": { + "Exit": { + "x": [13], + "y": [19], + "type": "door" + }, + "Shop Counter": { + "x": [12, 13], + "y": [15], + "type": "interactable" + }, + "Marnie's Room Door": { + "x": [15], + "y": [12], + "type": "door" + }, + "Jas's Room Door": { + "x": [6], + "y": [13], + "type": "door" + }, + "Shane's Room Door": { + "x": [21], + "y": [13], + "type": "door" + }, + "Marnie's Barn Door": { + "x": [30], + "y": [13], + "type": "door" + }, + "Fridge": { + "x": [28], + "y": [14], + "type": "decoration" + }, + "Oven": { + "x": [24], + "y": [14], + "type": "decoration" + }, + "Mega Station": { + "x": [22], + "y": [5], + "type": "decoration" + }, + "Shane's Radio": { + "x": [24], + "y": [4], + "type": "decoration" + }, + "Marnie's Dresser": { + "x": [16], + "y": [4], + "type": "decoration" + }, + "Marnie's Drawer": { + "x": [17], + "y": [4], + "type": "decoration" + }, + "Jack in the Box": { + "x": [8], + "y": [5], + "type": "decoration" + }, + "Futan Bear": { + "x": [2, 3], + "y": [4], + "type": "decoration" + }, + "Colouring Book": { + "x": [5], + "y": [4], + "type": "decoration" + }, + "Paint Set": { + "x": [5], + "y": [7], + "type": "decoration" + }, + "Jas's Alarm Clock": { + "x": [8], + "y": [8], + "type": "decoration" + }, + "Jas's Radio": { + "x": [4], + "y": [9], + "type": "decoration" + }, + "Arts And Craft": { + "x": [7], + "y": [6], + "type": "decoration" + }, + "Doll House": { + "x": [6, 7], + "y": [4], + "type": "decoration" + } + }, + "archaeologyhouse": { + "Exit": { + "x": [3], + "y": [14], + "type": "door" + }, + "Gunther's Box": { + "x": [6], + "y": [9], + "type": "other" + }, + "Counter": { + "x": [3], + "y": [9], + "type": "interactable" + } + }, + "backwoods": { + "Mountain Entrance": { + "x": [49], + "y": [14], + "type": "door" + }, + "Farm Entrance": { + "x": [14], + "y": [39], + "type": "door" + }, + "Bus stop Entrance": { + "x": [49], + "y": [28, 29, 30, 31, 32], + "type": "door" + }, + "Tunnel Entrance": { + "x": [23], + "y": [29, 30, 31], + "type": "door" + } + }, + "barn": { + "Hay Hopper": { + "x": [6], + "y": [3], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [8], + "y": [3], + "type": "other" + }, + "Feeding Bench 2": { + "x": [9], + "y": [3], + "type": "other" + }, + "Feeding Bench 3": { + "x": [10], + "y": [3], + "type": "other" + }, + "Feeding Bench 4": { + "x": [11], + "y": [3], + "type": "other" + }, + "Exit": { + "x": [11], + "y": [14], + "type": "door" + } + }, + "barn2": { + "Hay Hopper": { + "x": [6], + "y": [3], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [8], + "y": [3], + "type": "other" + }, + "Feeding Bench 2": { + "x": [9], + "y": [3], + "type": "other" + }, + "Feeding Bench 3": { + "x": [10], + "y": [3], + "type": "other" + }, + "Feeding Bench 4": { + "x": [11], + "y": [3], + "type": "other" + }, + "Feeding Bench 5": { + "x": [12], + "y": [3], + "type": "other" + }, + "Feeding Bench 6": { + "x": [13], + "y": [3], + "type": "other" + }, + "Feeding Bench 7": { + "x": [14], + "y": [3], + "type": "other" + }, + "Feeding Bench 8": { + "x": [15], + "y": [3], + "type": "other" + }, + "Exit": { + "x": [11], + "y": [14], + "type": "door" + } + }, + "barn3": { + "Hay Hopper": { + "x": [6], + "y": [3], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [8], + "y": [3], + "type": "other" + }, + "Feeding Bench 2": { + "x": [9], + "y": [3], + "type": "other" + }, + "Feeding Bench 3": { + "x": [10], + "y": [3], + "type": "other" + }, + "Feeding Bench 4": { + "x": [11], + "y": [3], + "type": "other" + }, + "Feeding Bench 5": { + "x": [12], + "y": [3], + "type": "other" + }, + "Feeding Bench 6": { + "x": [13], + "y": [3], + "type": "other" + }, + "Feeding Bench 7": { + "x": [14], + "y": [3], + "type": "other" + }, + "Feeding Bench 8": { + "x": [15], + "y": [3], + "type": "other" + }, + "Feeding Bench 9": { + "x": [16], + "y": [3], + "type": "other" + }, + "Feeding Bench 10": { + "x": [17], + "y": [3], + "type": "other" + }, + "Feeding Bench 11": { + "x": [18], + "y": [3], + "type": "other" + }, + "Feeding Bench 12": { + "x": [19], + "y": [3], + "type": "other" + }, + "Exit": { + "x": [11], + "y": [14], + "type": "door" + } + }, + "bathhouse_entry": { + "Exit": { + "x": [5], + "y": [9], + "type": "door" + } + }, + "bathhouse_menslocker": { + "Entry Room Door": { + "x": [3], + "y": [27], + "type": "door" + }, + "Pool Entrance": { + "x": [15], + "y": [27], + "type": "door" + } + }, + "bathhouse_pool": { + "Women's Locker Room Door": { + "x": [6], + "y": [1], + "type": "door" + }, + "Men's Locker Room Door": { + "x": [21], + "y": [1], + "type": "door" + } + }, + "bathhouse_womenslocker": { + "Entry Room Door": { + "x": [13], + "y": [27], + "type": "door" + }, + "Pool Entrance": { + "x": [2], + "y": [27], + "type": "door" + } + }, + "beach": { + "Town Entrance": { + "x": [38], + "y": [0], + "type": "door" + }, + "Beach Warp Statue": { + "x": [20], + "y": [4], + "type": "decoration" + }, + "Willy's Barrel": { + "x": [37], + "y": [33], + "type": "other" + } + }, + "beachnightmarket": { + "Fishing Submarine Door": { + "x": [5], + "y": [34], + "type": "door" + }, + "Mermaid Boat Door": { + "x": [58], + "y": [31], + "type": "door" + }, + "Desert Trader": { + "x": [14], + "y": [37], + "type": "npc" + }, + "Famous Painter Lupini": { + "x": [43], + "y": [34], + "type": "npc" + }, + "Travelling Cart": { + "x": [39], + "y": [30], + "type": "interactable" + }, + "Shrouded Figure": { + "x": [32], + "y": [34], + "type": "npc" + }, + "Decoration Boat": { + "x": [19], + "y": [33], + "type": "interactable" + }, + "Magic Shop Boat": { + "x": [48], + "y": [34], + "type": "interactable" + } + }, + "blacksmith": { + "Exit": { + "x": [5], + "y": [19], + "type": "door" + }, + "Shop Counter": { + "x": [3], + "y": [14], + "type": "interactable" + }, + "Clint's Room Door": { + "x": [4], + "y": [9], + "type": "door" + }, + "Clint's Furnace": { + "x": [9, 10], + "y": [12], + "type": "decoration" + }, + "Blueprints": { + "x": [13], + "y": [15, 16], + "type": "decoration" + }, + "Anvil": { + "x": [12, 13], + "y": [13], + "type": "decoration" + }, + "Cassette Deck": { + "x": [2], + "y": [4], + "type": "decoration" + }, + "Clint's Drawer": { + "x": [5, 6], + "y": [4], + "type": "decoration" + } + }, + "boattunnel": { + "Exit": { + "x": [6], + "y": [11], + "type": "door" + } + }, + "busstop": { + "Ticket Machine": { + "x": [7], + "y": [11], + "type": "interactable" + }, + "Minecart": { + "x": [4, 5], + "y": [3], + "type": "interactable" + }, + "Farm Entrance": { + "x": [0], + "y": [23], + "type": "door" + }, + "Town Entrance": { + "x": [34], + "y": [23], + "type": "door" + }, + "Backwoods Entrance": { + "x": [0], + "y": [6, 7, 8, 9], + "type": "door" + } + }, + "caldera": { + "Rare Chest": { + "x": [25], + "y": [28], + "type": "chest" + }, + "Forge": { + "x": [23], + "y": [21], + "type": "interactable" + }, + "Volcano Dungeon 0 Entrance": { + "x": [11], + "y": [36], + "type": "door" + }, + "Volcano Dungeon 9 Entrance": { + "x": [21], + "y": [39], + "type": "door" + } + }, + "captainroom": { + "Exit": { + "x": [0], + "y": [5], + "type": "door" + } + }, + "cellar": { + "Exit": { + "x": [3], + "y": [2], + "type": "door" + } + }, + "club": { + "Coin Machine": { + "x": [12], + "y": [4], + "type": "interactable" + }, + "Shop Counter": { + "x": [25], + "y": [3], + "type": "interactable" + }, + "Calico Spin Machine": { + "x": [11, 13, 15], + "y": [8, 10], + "type": "interactable" + }, + "High Stakes Calico Jack Table": { + "x": [23, 24], + "y": [10, 11], + "type": "interactable" + }, + "Low Stakes Calico Jack Table": { + "x": [3], + "y": [7, 9], + "type": "interactable" + }, + "Man": { + "x": [13], + "y": [11], + "type": "npc" + }, + "Welwick": { + "x": [18], + "y": [9], + "type": "npc" + }, + "Unknown person": { + "x": [16], + "y": [4], + "type": "npc" + }, + "Stats Checker": { + "x": [3], + "y": [4], + "type": "interactable" + }, + "Exit": { + "x": [8], + "y": [12], + "type": "door" + } + }, + "communitycenter": { + "Exit": { + "x": [32], + "y": [23], + "type": "door" + } + }, + "coop": { + "Hay Hopper": { + "x": [3], + "y": [3], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [6], + "y": [3], + "type": "other" + }, + "Feeding Bench 2": { + "x": [7], + "y": [3], + "type": "other" + }, + "Feeding Bench 3": { + "x": [8], + "y": [3], + "type": "other" + }, + "Feeding Bench 4": { + "x": [9], + "y": [3], + "type": "other" + }, + "Exit": { + "x": [2], + "y": [9], + "type": "door" + } + }, + "coop2": { + "Hay Hopper": { + "x": [3], + "y": [3], + "type": "interactable" + }, + "Incubator": { + "x": [2], + "y": [3], + "type": "machine" + }, + "Feeding Bench 1": { + "x": [6], + "y": [3], + "type": "other" + }, + "Feeding Bench 2": { + "x": [7], + "y": [3], + "type": "other" + }, + "Feeding Bench 3": { + "x": [8], + "y": [3], + "type": "other" + }, + "Feeding Bench 4": { + "x": [9], + "y": [3], + "type": "other" + }, + "Feeding Bench 5": { + "x": [10], + "y": [3], + "type": "other" + }, + "Feeding Bench 6": { + "x": [11], + "y": [3], + "type": "other" + }, + "Feeding Bench 7": { + "x": [12], + "y": [3], + "type": "other" + }, + "Feeding Bench 8": { + "x": [13], + "y": [3], + "type": "other" + }, + "Exit": { + "x": [2], + "y": [9], + "type": "door" + } + }, + "coop3": { + "Hay Hopper": { + "x": [3], + "y": [3], + "type": "interactable" + }, + "Incubator": { + "x": [2], + "y": [3], + "type": "machine" + }, + "Feeding Bench 1": { + "x": [6], + "y": [3], + "type": "other" + }, + "Feeding Bench 2": { + "x": [7], + "y": [3], + "type": "other" + }, + "Feeding Bench 3": { + "x": [8], + "y": [3], + "type": "other" + }, + "Feeding Bench 4": { + "x": [9], + "y": [3], + "type": "other" + }, + "Feeding Bench 5": { + "x": [10], + "y": [3], + "type": "other" + }, + "Feeding Bench 6": { + "x": [11], + "y": [3], + "type": "other" + }, + "Feeding Bench 7": { + "x": [12], + "y": [3], + "type": "other" + }, + "Feeding Bench 8": { + "x": [13], + "y": [3], + "type": "other" + }, + "Feeding Bench 9": { + "x": [14], + "y": [3], + "type": "other" + }, + "Feeding Bench 10": { + "x": [15], + "y": [3], + "type": "other" + }, + "Feeding Bench 11": { + "x": [16], + "y": [3], + "type": "other" + }, + "Feeding Bench 12": { + "x": [17], + "y": [3], + "type": "other" + }, + "Exit": { + "x": [2], + "y": [9], + "type": "door" + } + }, + "desert": { + "Bus": { + "x": [18], + "y": [27], + "type": "interactable" + }, + "Desert Trader": { + "x": [42], + "y": [23], + "type": "interactable" + }, + "Three Pillars": { + "x": [34, 37, 40], + "y": [8, 13], + "type": "decoration" + }, + "Three Pillars Center": { + "x": [37], + "y": [11], + "type": "interactable" + }, + "Skull Cavern Entrance": { + "x": [8], + "y": [6], + "type": "door" + }, + "Desert Warp Statue": { + "x": [35], + "y": [43], + "type": "decoration" + }, + "Sand Dragon Skull": { + "x": [9, 10], + "y": [35, 36], + "type": "decoration" + } + }, + "elliotthouse": { + "Exit": { + "x": [3], + "y": [9], + "type": "door" + }, + "Piano Key 1": { + "x": [7], + "y": [4], + "type": "interactable" + }, + "Piano Key 2": { + "x": [8], + "y": [4], + "type": "interactable" + }, + "Piano Key 3": { + "x": [9], + "y": [4], + "type": "interactable" + }, + "Piano Key 4": { + "x": [10], + "y": [4], + "type": "interactable" + } + }, + "farm": { + "Bus Stop Entrance": { + "x": [79], + "y": [15, 16, 17, 18], + "type": "door" + }, + "Backwoods Entrance": { + "x": [40, 41], + "y": [0], + "type": "door" + }, + "Cindersap Forest Entrance": { + "x": [40, 41], + "y": [64], + "type": "door" + }, + "Farm Cave Entrance": { + "x": [34], + "y": [7], + "type": "door" + }, + "Grandpa's Shrine": { + "x": [8], + "y": [7], + "type": "interactable" + }, + "Lumber Pile": { + "x": [60], + "y": [14], + "type": "decoration" + } + }, + "farmcave": { + "Exit": { + "x": [8], + "y": [11], + "type": "door" + } + }, + "fishshop": { + "Shop Counter": { + "x": [4, 5, 6], + "y": [5], + "type": "interactable" + }, + "Exit": { + "x": [5], + "y": [9], + "type": "door" + } + }, + "forest": { + "Farm Entrance": { + "x": [68], + "y": [0], + "type": "door" + }, + "Town Entrance": { + "x": [119], + "y": [25], + "type": "door" + }, + "Bridge 1": { + "x": [77, 82], + "y": [49], + "type": "bridge" + }, + "Bridge 2": { + "x": [87], + "y": [52, 56], + "type": "bridge" + }, + "Bridge 3": { + "x": [65, 62], + "y": [70], + "type": "bridge" + }, + "Bridge 4": { + "x": [41], + "y": [79, 82], + "type": "bridge" + }, + "Bridge 5": { + "x": [38], + "y": [85, 87], + "type": "bridge" + }, + "Abandoned House": { + "x": [34], + "y": [95], + "type": "interactable" + } + }, + "greenhouse": { + "Exit": { + "x": [10], + "y": [23], + "type": "door" + }, + "Water Trough": { + "x": [9, 10], + "y": [7], + "type": "water" + } + }, + "haleyhouse": { + "Exit": { + "x": [2], + "y": [24], + "type": "door" + }, + "Sewing Machine": { + "x": [12, 13], + "y": [23], + "type": "interactable" + }, + "Dye Pots": { + "x": [17], + "y": [25], + "type": "interactable" + }, + "Emily's Room Door": { + "x": [16], + "y": [12], + "type": "door" + }, + "Emily's Computer": { + "x": [22], + "y": [6], + "type": "decoration" + }, + "Emily's Pet Parrot": { + "x": [14], + "y": [4], + "type": "decoration" + }, + "Magazine": { + "x": [4], + "y": [22], + "type": "decoration" + }, + "Globe": { + "x": [8], + "y": [15], + "type": "decoration" + }, + "Fridge": { + "x": [21], + "y": [15], + "type": "decoration" + }, + "Haley's Room Door": { + "x": [5], + "y": [13], + "type": "door" + }, + "Futan Bear": { + "x": [8], + "y": [4], + "type": "decoration" + }, + "Diary": { + "x": [9], + "y": [5], + "type": "decoration" + }, + "Haley's Camera": { + "x": [1], + "y": [9], + "type": "decoration" + } + }, + "harveyroom": { + "Exit": { + "x": [6], + "y": [12], + "type": "door" + }, + "Fridge": { + "x": [21], + "y": [6], + "type": "decoration" + }, + "Oven": { + "x": [19], + "y": [6], + "type": "decoration" + }, + "Airplane Collection": { + "x": [6, 7], + "y": [3], + "type": "decoration" + }, + "Radio Broadcasting Set": { + "x": [4, 5], + "y": [4], + "type": "decoration" + }, + "Cassette Deck": { + "x": [8], + "y": [4], + "type": "decoration" + } + }, + "hospital": { + "Exit": { + "x": [10], + "y": [19], + "type": "door" + }, + "Harvey's Room Entrance": { + "x": [10], + "y": [2], + "type": "door" + }, + "Harvey's Room Entrance Door": { + "x": [10], + "y": [5], + "type": "door" + }, + "Main Area Door": { + "x": [10], + "y": [13], + "type": "door" + }, + "Shop Counter": { + "x": [5, 6, 7], + "y": [16], + "type": "interactable" + } + }, + "islandeast": { + "Banana Shrine": { + "x": [16], + "y": [26], + "type": "interactable" + }, + "Jungle Parrot Express": { + "x": [28], + "y": [27], + "type": "interactable" + }, + "Island Hut Entrance": { + "x": [22], + "y": [10], + "type": "door" + }, + "Island South Entrance": { + "x": [0], + "y": [46], + "type": "door" + }, + "Island Shrine Entrance": { + "x": [32], + "y": [30], + "type": "door" + } + }, + "islandfarmcave": { + "Exit": { + "x": [4], + "y": [10], + "type": "door" + }, + "Gourmand Frog": { + "x": [5], + "y": [4], + "type": "npc" + } + }, + "islandfieldoffice": { + "Exit": { + "x": [4], + "y": [10], + "type": "door" + }, + "Counter": { + "x": [8], + "y": [7], + "type": "interactable" + }, + "Island Survey": { + "x": [5], + "y": [3], + "type": "interactable" + } + }, + "islandhut": { + "Exit": { + "x": [7], + "y": [13], + "type": "door" + } + }, + "islandnorth": { + "Island South Entrance": { + "x": [35, 36, 37], + "y": [89], + "type": "door" + }, + "Island South Entrance 2": { + "x": [43, 44], + "y": [89], + "type": "door" + }, + "Island Field Office Entrance": { + "x": [46], + "y": [46], + "type": "door" + }, + "Island North Cave Entrance": { + "x": [21, 22], + "y": [47], + "type": "door" + }, + "Dig Site Parrot Express": { + "x": [5], + "y": [48], + "type": "interactable" + }, + "Volcano Dungeon Entrance": { + "x": [39, 40, 41, 42], + "y": [21], + "type": "door" + }, + "Volcano Dungeon Entrance 2": { + "x": [12], + "y": [31], + "type": "door" + }, + "Volcano Parrot Express": { + "x": [60], + "y": [16], + "type": "interactable" + } + }, + "islandshrine": { + "Exit": { + "x": [13], + "y": [28], + "type": "door" + }, + "Shrine": { + "x": [24], + "y": [22], + "type": "interactable" + }, + "North Pedestal": { + "x": [24], + "y": [25], + "type": "interactable" + }, + "East Pedestal": { + "x": [27], + "y": [27], + "type": "interactable" + }, + "West Pedestal": { + "x": [21], + "y": [27], + "type": "interactable" + }, + "South Pedestal": { + "x": [24], + "y": [28], + "type": "interactable" + } + }, + "islandsouth": { + "Island East Entrance": { + "x": [34], + "y": [12], + "type": "door" + }, + "Ginger Island Warp Statue": { + "x": [11], + "y": [11], + "type": "decoration" + }, + "Island West Entrance": { + "x": [0], + "y": [11], + "type": "door" + }, + "Island North Entrance": { + "x": [17, 18, 19], + "y": [0], + "type": "door" + }, + "Island North Entrance 2": { + "x": [27, 28], + "y": [0], + "type": "door" + }, + "Docks Parrot Express": { + "x": [6], + "y": [31], + "type": "interactable" + }, + "Return Boat": { + "x": [19], + "y": [43], + "type": "interactable" + } + }, + "islandsoutheast": { + "Island South East Cave Entrance": { + "x": [30], + "y": [18], + "type": "door" + } + }, + "islandsoutheastcave": { + "Exit": { + "x": [1], + "y": [8], + "type": "door" + } + }, + "islandwest": { + "Farm Parrot Express": { + "x": [74], + "y": [9], + "type": "interactable" + }, + "Bridge 1": { + "x": [67, 62], + "y": [16], + "type": "bridge" + }, + "Qi's Walnut Room Door": { + "x": [20], + "y": [22], + "type": "door" + }, + "Door to Shipwreck Interior": { + "x": [60], + "y": [92], + "type": "door" + }, + "Hole 1": { + "x": [37], + "y": [87], + "type": "interactable" + }, + "Hole 2": { + "x": [41], + "y": [86], + "type": "interactable" + }, + "Hole 3": { + "x": [45], + "y": [86], + "type": "interactable" + }, + "Hole 4": { + "x": [48], + "y": [87], + "type": "interactable" + }, + "Bridge 2": { + "x": [55, 52], + "y": [80], + "type": "bridge" + }, + "Island Farm House Door": { + "x": [77], + "y": [39], + "type": "door" + }, + "Island Farm Cave Entrance": { + "x": [96], + "y": [33], + "type": "door" + }, + "Island South Entrance": { + "x": [105], + "y": [40], + "type": "door" + } + }, + "jojamart": { + "Exit": { + "x": [13], + "y": [29], + "type": "door" + }, + "Morris's Kiosk": { + "x": [21], + "y": [25], + "type": "interactable" + }, + "Shop Counter": { + "x": [10], + "y": [25], + "type": "interactable" + } + }, + "joshhouse": { + "Exit": { + "x": [9], + "y": [24], + "type": "door" + }, + "TV": { + "x": [15, 16], + "y": [20], + "type": "decoration" + }, + "Bookshelf": { + "x": [17, 18], + "y": [16], + "type": "decoration" + }, + "Fridge": { + "x": [5], + "y": [16], + "type": "decoration" + }, + "Evelyn and George's Room Door": { + "x": [5], + "y": [9], + "type": "door" + }, + "Alex's Room Door": { + "x": [10], + "y": [10], + "type": "door" + }, + "Radio": { + "x": [3], + "y": [4], + "type": "door" + }, + "Magazine": { + "x": [11], + "y": [4], + "type": "door" + }, + "Alex's Bookshelf": { + "x": [12, 13], + "y": [4], + "type": "decoration" + }, + "Alex's Drawer": { + "x": [17, 18], + "y": [4], + "type": "decoration" + }, + "Dumbbell": { + "x": [14], + "y": [4], + "type": "door" + }, + "Gridball": { + "x": [23], + "y": [45], + "type": "door" + }, + "Gridball Helmet": { + "x": [23], + "y": [6], + "type": "door" + }, + "Evelyn's Stove": { + "x": [3], + "y": [16], + "type": "other" + } + }, + "manorhouse": { + "Exit": { + "x": [4], + "y": [11], + "type": "door" + }, + "Town Ledger Book": { + "x": [2], + "y": [5], + "type": "interactable" + }, + "Marriage Log Book": { + "x": [3], + "y": [5], + "type": "interactable" + }, + "Lost and Found Box": { + "x": [4], + "y": [5], + "type": "interactable" + }, + "Mayor's Room Door": { + "x": [16], + "y": [9], + "type": "door" + }, + "Mayor's Oven": { + "x": [7], + "y": [4], + "type": "decoration" + }, + "Mayor's Fridge": { + "x": [9], + "y": [4], + "type": "decoration" + } + }, + "mermaidhouse": { + "Exit": { + "x": [4], + "y": [10], + "type": "door" + }, + "Clam Shell 1": { + "x": [2], + "y": [6], + "type": "interactable" + }, + "Clam Shell 2": { + "x": [3], + "y": [6], + "type": "interactable" + }, + "Clam Shell 3": { + "x": [4], + "y": [6], + "type": "interactable" + }, + "Clam Shell 4": { + "x": [5], + "y": [6], + "type": "interactable" + }, + "Clam Shell 5": { + "x": [6], + "y": [6], + "type": "interactable" + } + }, + "mine": { + "Mountain Exit": { + "x": [18], + "y": [13], + "type": "door" + }, + "Minecart": { + "x": [11, 12], + "y": [10], + "type": "interactable" + }, + "Quarry Mine Ladder": { + "x": [67], + "y": [9], + "type": "door" + }, + "Quarry Exit": { + "x": [18], + "y": [13], + "type": "door" + } + }, + "mountain": { + "Mine Entrance": { + "x": [54], + "y": [5], + "type": "door" + }, + "Mine Bridge": { + "x": [47], + "y": [7], + "type": "bridge" + }, + "Quarry Bridge": { + "x": [90], + "y": [26], + "type": "bridge" + }, + "Minecart": { + "x": [124, 125], + "y": [11], + "type": "interactable" + }, + "Quarry Mine Entrance": { + "x": [103], + "y": [17], + "type": "door" + }, + "Bridge 1": { + "x": [57], + "y": [30], + "type": "bridge" + }, + "Bridge 2": { + "x": [61], + "y": [21], + "type": "bridge" + }, + "Mountain Warp Statue": { + "x": [31], + "y": [20], + "type": "decoration" + }, + "Linus Tent Entrance": { + "x": [29], + "y": [7], + "type": "door" + }, + "Backwoods Entrance": { + "x": [0], + "y": [13], + "type": "door" + }, + "Town Entrance": { + "x": [15], + "y": [40], + "type": "door" + }, + "Railroad Entrance": { + "x": [9], + "y": [0], + "type": "door" + }, + "Science House Secondary Door": { + "x": [8], + "y": [20], + "type": "door" + } + }, + "movietheater": { + "Exit": { + "x": [13], + "y": [15], + "type": "door" + }, + "Concessions Counter": { + "x": [7], + "y": [6], + "type": "interactable" + }, + "Crane Game": { + "x": [1, 2], + "y": [8], + "type": "interactable" + }, + "Theater Door": { + "x": [13], + "y": [3], + "type": "door" + }, + "Crane Man": { + "x": [2], + "y": [9], + "type": "npc" + } + }, + "qinutroom": { + "Exit": { + "x": [7], + "y": [7], + "type": "door" + }, + "Perfection Tracker": { + "x": [13], + "y": [4], + "type": "interactable" + }, + "Vending Machine": { + "x": [11], + "y": [3], + "type": "interactable" + }, + "Special Order Board": { + "x": [3], + "y": [3], + "type": "interactable" + } + }, + "railroad": { + "Mountain Entrance": { + "x": [29], + "y": [61], + "type": "door" + }, + "Witch's Cave Entrance": { + "x": [54], + "y": [35], + "type": "door" + }, + "Empty Crate": { + "x": [45], + "y": [40], + "type": "other" + }, + "Dumpster": { + "x": [28], + "y": [36], + "type": "other" + }, + "Water": { + "x": [13, 14, 15, 16], + "y": [54, 55, 56], + "type": "water" + } + }, + "saloon": { + "Exit": { + "x": [14], + "y": [24], + "type": "door" + }, + "Shop Counter": { + "x": [14], + "y": [19], + "type": "interactable" + }, + "Journey of the Prairie King Arcade": { + "x": [33], + "y": [17], + "type": "interactable" + }, + "Junimo Kart Arcade": { + "x": [35], + "y": [17], + "type": "interactable" + }, + "Joja Vending Machine": { + "x": [37, 38], + "y": [17], + "type": "interactable" + }, + "Jukebox": { + "x": [1, 2], + "y": [17], + "type": "interactable" + }, + "Gus's Fridge": { + "x": [18], + "y": [16], + "type": "other" + }, + "Gus's Room Door": { + "x": [20], + "y": [9], + "type": "door" + }, + "Dining Room Door": { + "x": [11], + "y": [9], + "type": "door" + }, + "Living Area Door": { + "x": [4], + "y": [16], + "type": "door" + }, + "Gus's Radio": { + "x": [16], + "y": [6], + "type": "decoration" + }, + "Lockbox": { + "x": [28], + "y": [7], + "type": "other" + }, + }, + "samhouse": { + "Exit": { + "x": [4], + "y": [23], + "type": "door" + }, + "Radio": { + "x": [6], + "y": [12], + "type": "decoration" + }, + "Vincent's Room Door": { + "x": [16], + "y": [18], + "type": "door" + }, + "Toy Chest": { + "x": [12], + "y": [21], + "type": "other" + }, + "Sam's Room Door": { + "x": [12], + "y": [14], + "type": "door" + }, + "Jodi's Room Door": { + "x": [17], + "y": [6], + "type": "door" + }, + "Sam's Drawer": { + "x": [7, 8], + "y": [12], + "type": "decoration" + }, + "Bookshelf": { + "x": [18, 19], + "y": [12], + "type": "decoration" + }, + "Fridge": { + "x": [7], + "y": [4], + "type": "decoration" + } + }, + "sandyhouse": { + "Exit": { + "x": [4], + "y": [9], + "type": "door" + }, + "Club Entrance": { + "x": [17], + "y": [1], + "type": "door" + }, + "Shop Counter": { + "x": [2], + "y": [6], + "type": "interactable" + } + }, + "sciencehouse": { + "Exit": { + "x": [6], + "y": [24], + "type": "door" + }, + "Secondary Exit": { + "x": [3], + "y": [8], + "type": "door" + }, + "Shop Counter": { + "x": [8], + "y": [19], + "type": "interactable" + }, + "Robin's Wood Pile": { + "x": [11], + "y": [19], + "type": "other" + }, + "Sebastian's Room Entrance": { + "x": [12], + "y": [21], + "type": "door" + }, + "Beaker Set": { + "x": [17], + "y": [17], + "type": "decoration" + }, + "Microscope": { + "x": [19], + "y": [17], + "type": "decoration" + }, + "Stereo Microscope": { + "x": [23], + "y": [20], + "type": "decoration" + }, + "Robin and Demetrius's Room Entrance": { + "x": [13], + "y": [10], + "type": "door" + }, + "Maru's Room Entrance": { + "x": [7], + "y": [10], + "type": "door" + }, + "Bookshelf": { + "x": [16, 17], + "y": [4], + "type": "decoration" + }, + "Maru's Device": { + "x": [6], + "y": [6], + "type": "decoration" + }, + "Poster": { + "x": [6], + "y": [3], + "type": "decoration" + }, + "Computer": { + "x": [9], + "y": [4], + "type": "decoration" + }, + "Fridge": { + "x": [27], + "y": [8], + "type": "decoration" + }, + "Oven": { + "x": [30], + "y": [10], + "type": "decoration" + } + }, + "sebastianroom": { + "Exit": { + "x": [1], + "y": [1], + "type": "door" + }, + "Room Door": { + "x": [1], + "y": [3], + "type": "door" + }, + "Sebastian's Radio": { + "x": [3], + "y": [4], + "type": "decoration" + }, + "Graphic Novel": { + "x": [10], + "y": [6], + "type": "decoration" + }, + "Computer": { + "x": [7], + "y": [4], + "type": "decoration" + } + }, + "seedshop": { + "Exit": { + "x": [6], + "y": [29], + "type": "door" + }, + "Shop Counter": { + "x": [4, 5], + "y": [18], + "type": "interactable" + }, + "Backpack Upgrade": { + "x": [7], + "y": [18], + "type": "interactable" + }, + "Pierre's Bin": { + "x": [19], + "y": [28], + "type": "interactable" + }, + "Shrine of Yoba": { + "x": [37], + "y": [17], + "type": "decoration" + }, + "Fridge": { + "x": [39], + "y": [4], + "type": "decoration" + }, + "Abigail's Room Door": { + "x": [13], + "y": [11], + "type": "door" + }, + "Pierre and Caroline's Room Door": { + "x": [20], + "y": [11], + "type": "door" + }, + "Living Area Door": { + "x": [14], + "y": [16], + "type": "door" + } + }, + "sewer": { + "Exit Ladder": { + "x": [16], + "y": [10], + "type": "door" + }, + "Statue Of Uncertainty": { + "x": [8], + "y": [20], + "type": "interactable" + }, + "Mutant Bug Lair": { + "x": [3], + "y": [19], + "type": "door" + } + }, + "shed": { + "Exit": { + "x": [6], + "y": [13], + "type": "door" + } + }, + "shed2": { + "Exit": { + "x": [9], + "y": [16], + "type": "door" + } + }, + "skullcave": { + "Exit": { + "x": [7], + "y": [9], + "type": "door" + }, + "Skull Cavern Entrance": { + "x": [3], + "y": [3], + "type": "door" + } + }, + "slime hutch": { + "Water Trough 1": { + "x": [16], + "y": [6], + "type": "other" + }, + "Water Trough 2": { + "x": [16], + "y": [7], + "type": "other" + }, + "Water Trough 3": { + "x": [16], + "y": [8], + "type": "other" + }, + "Water Trough 4": { + "x": [16], + "y": [9], + "type": "other" + }, + "Exit": { + "x": [8], + "y": [12], + "type": "door" + } + }, + "submarine": { + "Exit": { + "x": [14], + "y": [15], + "type": "door" + }, + "Captain": { + "x": [2], + "y": [9], + "type": "npc" + } + }, + "tent": { + "Exit": { + "x": [2], + "y": [5], + "type": "door" + } + }, + "town": { + "Calender Board": { + "x": [41], + "y": [56], + "type": "interactable" + }, + "Daily Quest Board": { + "x": [42], + "y": [56], + "type": "interactable" + }, + "Sewer": { + "x": [34, 35], + "y": [95, 96], + "type": "interactable" + }, + "Ice Cream Stand": { + "x": [88], + "y": [92], + "type": "interactable" + }, + "Minecart": { + "x": [105, 106], + "y": [79], + "type": "interactable" + }, + "Faded Gravestone": { + "x": [42], + "y": [85], + "type": "decoration" + }, + "Mona's Grave": { + "x": [47], + "y": [86], + "type": "decoration" + }, + "Dwarvish Headstone": { + "x": [51], + "y": [86], + "type": "decoration" + }, + "Unknown Gravestone": { + "x": [42], + "y": [90], + "type": "decoration" + }, + "brown box": { + "x": [100], + "y": [66], + "type": "other" + }, + "Joja Mart Trash": { + "x": [110], + "y": [56], + "type": "interactable" + }, + "1 River Road Trash": { + "x": [52], + "y": [63], + "type": "interactable" + }, + "Stardew Saloon Trash": { + "x": [47], + "y": [70], + "type": "interactable" + }, + "Blacksmith Trash": { + "x": [97], + "y": [80], + "type": "interactable" + }, + "1 Willow Lane Trash": { + "x": [13], + "y": [86], + "type": "interactable" + }, + "Manor Trash": { + "x": [56], + "y": [86], + "type": "interactable" + }, + "2 Willow Lane Trash": { + "x": [19], + "y": [89], + "type": "interactable" + }, + "Museum Trash": { + "x": [108], + "y": [91], + "type": "interactable" + }, + "Bus Stop Entrance": { + "x": [0], + "y": [54], + "type": "door" + }, + "Cindersap Forest Entrance": { + "x": [0], + "y": [90], + "type": "door" + }, + "Beach Entrance": { + "x": [54], + "y": [109], + "type": "door" + }, + "Mountain Entrance": { + "x": [81], + "y": [0], + "type": "door" + } + }, + "trailer": { + "Exit": { + "x": [12], + "y": [9], + "type": "door" + }, + "Penny's Room Door": { + "x": [6], + "y": [7], + "type": "door" + }, + "Bookshelf": { + "x": [5, 6], + "y": [4], + "type": "decoration" + }, + "Book": { + "x": [2], + "y": [4], + "type": "decoration" + }, + "Magazine": { + "x": [1], + "y": [9], + "type": "decoration" + }, + "Pam's Kitchen": { + "x": [10], + "y": [6], + "type": "other" + } + }, + "trailer_big": { + "Exit": { + "x": [13], + "y": [25], + "type": "door" + }, + "Pam's Kitchen": { + "x": [10], + "y": [6], + "type": "other" + } + }, + "tunnel": { + "Exit": { + "x": [39], + "y": [7, 8, 9, 10, 11], + "type": "door" + }, + "Safe Panel": { + "x": [17], + "y": [6], + "type": "other" + } + }, + "undergroundmine77377": { + "Grim Reaper Statue": { + "x": [29, 30], + "y": [6], + "type": "interactable" + } + }, + "volcanodungeon0": { + "Island North Entrance 1": { + "x": [31], + "y": [54], + "type": "door" + }, + "Island North Entrance 2": { + "x": [6], + "y": [50], + "type": "door" + }, + "Caldera Entrance": { + "x": [44], + "y": [50], + "type": "door" + }, + "Volcano Dungeon 1 Entrance": { + "x": [37], + "y": [5], + "type": "door" + } + }, + "witchhut": { + "Exit": { + "x": [7], + "y": [15], + "type": "door" + }, + "Wizard's Basement Rune": { + "x": [11], + "y": [11], + "type": "door" + }, + "Dark Shrine of Memory": { + "x": [7], + "y": [4, 5], + "type": "interactable" + }, + "Dark Shrine of Night Terrors": { + "x": [12], + "y": [5, 6], + "type": "interactable" + }, + "Dark Shrine of Selfishness": { + "x": [2], + "y": [5, 6], + "type": "interactable" + } + }, + "witchswamp": { + "Witch's Cave Rune": { + "x": [20], + "y": [42], + "type": "door" + }, + "Witch's Hut Door": { + "x": [20], + "y": [20], + "type": "door" + } + }, + "witchwarpcave": { + "Witch's Swamp Rune": { + "x": [4], + "y": [5], + "type": "door" + }, + "Railroad Entrance": { + "x": [4], + "y": [9], + "type": "door" + } + }, + "wizardhouse": { + "Exit": { + "x": [8], + "y": [24], + "type": "door" + }, + "Basement Door": { + "x": [4], + "y": [4], + "type": "door" + }, + "Magic Buildings Book": { + "x": [2], + "y": [13], + "type": "interactable" + }, + }, + "wizardhousebasement": { + "Exit Ladder": { + "x": [4], + "y": [3], + "type": "door" + }, + "Witch's Hut Rune": { + "x": [2], + "y": [5], + "type": "door" + }, + "Shrine of Illusions": { + "x": [12], + "y": [4], + "type": "interactable" + } + }, + "woods": { + "Forest Entrance": { + "x": [59], + "y": [17], + "type": "door" + }, + "Old Master Cannoli": { + "x": [8, 9], + "y": [7], + "type": "interactable" + } + } } \ No newline at end of file From 17c1c19e90c28df13a1d53abd981c5bdff72da40 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Date: Sat, 20 Aug 2022 22:17:45 +0530 Subject: [PATCH 186/232] Removed trailing commas --- stardew-access/assets/static-tiles.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 170ba54..9123647 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -1685,7 +1685,7 @@ "x": [28], "y": [7], "type": "other" - }, + } }, "samhouse": { "Exit": { @@ -2259,7 +2259,7 @@ "x": [2], "y": [13], "type": "interactable" - }, + } }, "wizardhousebasement": { "Exit Ladder": { @@ -2290,4 +2290,4 @@ "type": "interactable" } } -} \ No newline at end of file +} From 741e6a5219d821d0739b4c5a799128182451e58c Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 20 Aug 2022 22:58:42 +0530 Subject: [PATCH 187/232] Added 'not usable here' to inventories --- stardew-access/Patches/BundleMenuPatches.cs | 4 ++++ stardew-access/Patches/DonationMenuPatches.cs | 7 +++++++ stardew-access/Patches/GameMenuPatches.cs | 19 ++++++++++++------- stardew-access/Patches/MenuPatches.cs | 14 ++++++++++++++ 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/stardew-access/Patches/BundleMenuPatches.cs b/stardew-access/Patches/BundleMenuPatches.cs index 2e34511..fc3dc6c 100644 --- a/stardew-access/Patches/BundleMenuPatches.cs +++ b/stardew-access/Patches/BundleMenuPatches.cs @@ -334,6 +334,10 @@ namespace stardew_access.Patches } toSpeak = $"{item.Stack} {toSpeak}"; + if (!__instance.inventory.highlightMethod(__instance.inventory.actualInventory[currentInventorySlot])) + { + toSpeak = $"{toSpeak} not usable here"; + } } else { diff --git a/stardew-access/Patches/DonationMenuPatches.cs b/stardew-access/Patches/DonationMenuPatches.cs index 159bc25..f4969ca 100644 --- a/stardew-access/Patches/DonationMenuPatches.cs +++ b/stardew-access/Patches/DonationMenuPatches.cs @@ -311,8 +311,15 @@ namespace stardew_access.Patches if (__instance.inventory.actualInventory[i] == null) toSpeak = "Empty slot"; else + { toSpeak = $"{__instance.inventory.actualInventory[i].Stack} {__instance.inventory.actualInventory[i].DisplayName}"; + if (!__instance.inventory.highlightMethod(__instance.inventory.actualInventory[i])) + { + toSpeak = $"{toSpeak} not usable here"; + } + } + if (fieldOfficeMenuQuery != $"{toSpeak}:{i}") { fieldOfficeMenuQuery = $"{toSpeak}:{i}"; diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index 97ce43a..f322daf 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -241,7 +241,7 @@ namespace stardew_access.Patches #endregion #region Narrate hovered item - if (narrateHoveredItemInInventory(__instance.inventory.inventory, __instance.inventory.actualInventory, x, y, hoverPrice: __instance.hoverPrice)) + if (narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y, hoverPrice: __instance.hoverPrice)) { shopMenuQueryKey = ""; return; @@ -396,7 +396,7 @@ namespace stardew_access.Patches #endregion #region Narrate hovered item - if (narrateHoveredItemInInventory(__instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) + if (narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) geodeMenuQueryKey = ""; #endregion } @@ -573,14 +573,14 @@ namespace stardew_access.Patches #endregion #region Narrate hovered item - if (narrateHoveredItemInInventory(__instance.inventory.inventory, __instance.inventory.actualInventory, x, y, true)) + if (narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y, true)) { gameMenuQueryKey = ""; itemGrabMenuQueryKey = ""; return; } - if (narrateHoveredItemInInventory(__instance.ItemsToGrabMenu.inventory, __instance.ItemsToGrabMenu.actualInventory, x, y, true)) + if (narrateHoveredItemInInventory(__instance.ItemsToGrabMenu, __instance.ItemsToGrabMenu.inventory, __instance.ItemsToGrabMenu.actualInventory, x, y, true)) { gameMenuQueryKey = ""; itemGrabMenuQueryKey = ""; @@ -855,7 +855,7 @@ namespace stardew_access.Patches #endregion #region Narrate hovered item - if (narrateHoveredItemInInventory(__instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) + if (narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) { gameMenuQueryKey = ""; craftingPageQueryKey = ""; @@ -1072,7 +1072,7 @@ namespace stardew_access.Patches #endregion #region Narrate hovered item - if (narrateHoveredItemInInventory(__instance.inventory.inventory, __instance.inventory.actualInventory, x, y, true)) + if (narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y, true)) { gameMenuQueryKey = ""; inventoryPageQueryKey = ""; @@ -1174,7 +1174,7 @@ namespace stardew_access.Patches } } - internal static bool narrateHoveredItemInInventory(List inventory, IList actualInventory, int x, int y, bool giveExtraDetails = false, int hoverPrice = -1, int extraItemToShowIndex = -1, int extraItemToShowAmount = -1) + internal static bool narrateHoveredItemInInventory(InventoryMenu inventoryMenu, List inventory, IList actualInventory, int x, int y, bool giveExtraDetails = false, int hoverPrice = -1, int extraItemToShowIndex = -1, int extraItemToShowAmount = -1) { #region Narrate hovered item for (int i = 0; i < inventory.Count; i++) @@ -1272,6 +1272,11 @@ namespace stardew_access.Patches price = $"Sell Price: {hoverPrice} g"; } + if (!inventoryMenu.highlightMethod(actualInventory[i])) + { + name = $"{name} not usable here"; + } + if (giveExtraDetails) { if (stack > 1) diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 5bd970b..40c98bf 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -136,8 +136,15 @@ namespace stardew_access.Patches if (__instance.inventory.actualInventory[i] == null) toSpeak = "Empty slot"; else + { toSpeak = $"{__instance.inventory.actualInventory[i].Stack} {__instance.inventory.actualInventory[i].DisplayName}"; + if (!__instance.inventory.highlightMethod(__instance.inventory.actualInventory[i])) + { + toSpeak = $"{toSpeak} not usable here"; + } + } + if (forgeMenuQuery != $"{toSpeak}:{i}") { forgeMenuQuery = $"{toSpeak}:{i}"; @@ -296,8 +303,15 @@ namespace stardew_access.Patches if (__instance.inventory.actualInventory[i] == null) toSpeak = "Empty slot"; else + { toSpeak = $"{__instance.inventory.actualInventory[i].Stack} {__instance.inventory.actualInventory[i].DisplayName}"; + if (!__instance.inventory.highlightMethod(__instance.inventory.actualInventory[i])) + { + toSpeak = $"{toSpeak} not usable here"; + } + } + if (tailoringMenuQuery != $"{toSpeak}:{i}") { tailoringMenuQuery = $"{toSpeak}:{i}"; From 895f329b8b803906b7f4fce65f934e0002ad92ea Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Thu, 18 Aug 2022 12:18:40 +0530 Subject: [PATCH 188/232] Added watered/unwatered toggle for crops --- stardew-access/CustomCommands.cs | 8 ++++++++ stardew-access/Features/TileInfo.cs | 15 ++++++++++----- stardew-access/ModConfig.cs | 1 + 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index 9f28d13..d6d51b6 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -39,6 +39,14 @@ namespace stardew_access MainClass.DebugLog("Flooring is " + (MainClass.Config.ReadFlooring ? "on" : "off")); }); + helper.ConsoleCommands.Add("watered", "Toggle speaking watered or unwatered for crops.", (string commmand, string[] args) => + { + MainClass.Config.WateredToggle = !MainClass.Config.WateredToggle; + helper.WriteConfig(MainClass.Config); + + MainClass.DebugLog("Watered toggle is " + (MainClass.Config.WateredToggle ? "on" : "off")); + }); + #region Radar Feature helper.ConsoleCommands.Add("radar", "Toggle radar feature.", (string commmand, string[] args) => { diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 00b0b57..ebcdd04 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -543,8 +543,9 @@ namespace stardew_access.Features /// Returns the detail about the HoeDirt i.e. soil, plant, etc. ///
/// The HoeDirt to be checked + /// Ignores returning `soil` if empty /// The details about the given HoeDirt - public static string getHoeDirtDetail(HoeDirt dirt) + public static string getHoeDirtDetail(HoeDirt dirt, bool ignoreIfEmpty = false) { string detail; @@ -557,8 +558,10 @@ namespace stardew_access.Features bool isHarvestable = dirt.readyForHarvest(); bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer; - if (isWatered) + if (isWatered && MainClass.Config.WateredToggle) detail = "Watered " + detail; + else if (!isWatered && !MainClass.Config.WateredToggle) + detail = "Unwatered " + detail; if (isFertilized) detail = "Fertilized " + detail; @@ -580,12 +583,14 @@ namespace stardew_access.Features } else { - detail = "Soil"; + detail = (ignoreIfEmpty) ? "" : "Soil"; bool isWatered = dirt.state.Value == HoeDirt.watered; bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer; - if (isWatered) + if (isWatered && MainClass.Config.WateredToggle) detail = "Watered " + detail; + else if (!isWatered && !MainClass.Config.WateredToggle) + detail = "Unwatered " + detail; if (isFertilized) detail = "Fertilized " + detail; @@ -709,7 +714,7 @@ namespace stardew_access.Features } else if (obj is IndoorPot indoorPot) { - toReturn.name = $"{obj.DisplayName}, {getHoeDirtDetail(indoorPot.hoeDirt)}"; + toReturn.name = $"{obj.DisplayName}, {getHoeDirtDetail(indoorPot.hoeDirt, true)}"; } else if (obj is Sign sign) { diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index c77e9df..48e1490 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -23,6 +23,7 @@ namespace stardew_access public KeybindList ReadTileKey { get; set; } = KeybindList.Parse("J"); // Manually trigger read tile for the tile player is *looking at*. public KeybindList ReadStandingTileKey { get; set; } = KeybindList.Parse("LeftAlt + J"); // Manually trigger read tile for the tile player is *standing on*. public Boolean ReadFlooring { get; set; } = false; // Toggle reading floorings. + public Boolean WateredToggle { get; set; } = true; // Toggle speaking watered or unwatered for crops. #endregion #region Tile viewer From 3083ebca849a6ef760d9d1ded0863afcf9d74802 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Thu, 18 Aug 2022 12:37:32 +0530 Subject: [PATCH 189/232] Added command to toggle warnings feature --- stardew-access/CustomCommands.cs | 46 ++++++++++++++++++----------- stardew-access/Features/ReadTile.cs | 3 -- stardew-access/ModConfig.cs | 1 + stardew-access/ModEntry.cs | 6 ++-- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index d6d51b6..7f231ad 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -15,21 +15,14 @@ namespace stardew_access if (helper == null) return; + #region Read Tile helper.ConsoleCommands.Add("readtile", "Toggle read tile feature.", (string commmand, string[] args) => - { - MainClass.Config.ReadTile = !MainClass.Config.ReadTile; - helper.WriteConfig(MainClass.Config); + { + MainClass.Config.ReadTile = !MainClass.Config.ReadTile; + helper.WriteConfig(MainClass.Config); - MainClass.DebugLog("Read Tile is " + (MainClass.Config.ReadTile ? "on" : "off")); - }); - - helper.ConsoleCommands.Add("snapmouse", "Toggle snap mouse feature.", (string commmand, string[] args) => - { - MainClass.Config.SnapMouse = !MainClass.Config.SnapMouse; - helper.WriteConfig(MainClass.Config); - - MainClass.DebugLog("Snap Mouse is " + (MainClass.Config.SnapMouse ? "on" : "off")); - }); + MainClass.DebugLog("Read Tile is " + (MainClass.Config.ReadTile ? "on" : "off")); + }); helper.ConsoleCommands.Add("flooring", "Toggle flooring in read tile.", (string commmand, string[] args) => { @@ -46,6 +39,7 @@ namespace stardew_access MainClass.DebugLog("Watered toggle is " + (MainClass.Config.WateredToggle ? "on" : "off")); }); + #endregion #region Radar Feature helper.ConsoleCommands.Add("radar", "Toggle radar feature.", (string commmand, string[] args) => @@ -482,12 +476,13 @@ namespace stardew_access }); #endregion + #region Other helper.ConsoleCommands.Add("refsr", "Refresh screen reader", (string commmand, string[] args) => - { - MainClass.ScreenReader.InitializeScreenReader(); + { + MainClass.ScreenReader.InitializeScreenReader(); - MainClass.DebugLog("Screen Reader refreshed!"); - }); + MainClass.DebugLog("Screen Reader refreshed!"); + }); helper.ConsoleCommands.Add("refmc", "Refresh mod config", (string commmand, string[] args) => { @@ -510,6 +505,23 @@ namespace stardew_access MainClass.DebugLog("Speaking in percentage is " + (MainClass.Config.HealthNStaminaInPercentage ? "on" : "off")); }); + + helper.ConsoleCommands.Add("snapmouse", "Toggle snap mouse feature.", (string commmand, string[] args) => + { + MainClass.Config.SnapMouse = !MainClass.Config.SnapMouse; + helper.WriteConfig(MainClass.Config); + + MainClass.DebugLog("Snap Mouse is " + (MainClass.Config.SnapMouse ? "on" : "off")); + }); + + helper.ConsoleCommands.Add("warning", "Toggle warnings feature.", (string commmand, string[] args) => + { + MainClass.Config.Warning = !MainClass.Config.Warning; + helper.WriteConfig(MainClass.Config); + + MainClass.DebugLog("Warnings is " + (MainClass.Config.Warning ? "on" : "off")); + }); + #endregion } } } diff --git a/stardew-access/Features/ReadTile.cs b/stardew-access/Features/ReadTile.cs index 7cdb846..01121b3 100644 --- a/stardew-access/Features/ReadTile.cs +++ b/stardew-access/Features/ReadTile.cs @@ -28,9 +28,6 @@ namespace stardew_access.Features if (this.shouldPause) return; - if (!MainClass.Config.ReadTile) - return; - this.isBusy = true; this.run(); Task.Delay(delay).ContinueWith(_ => { this.isBusy = false; }); diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index 48e1490..76f057d 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -77,6 +77,7 @@ namespace stardew_access public KeybindList TimeNSeasonKey { get; set; } = KeybindList.Parse("Q"); // Narrate the time of day, day and date and season public Boolean VerboseCoordinates { get; set; } = true; public Boolean SnapMouse { get; set; } = true; // Toggles the snap mouse feature + public Boolean Warning { get; set; } = true; // Toggles the warnings feature // TODO add command to toggle warning feature #endregion diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index f9de5d3..03cdc36 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -168,9 +168,11 @@ namespace stardew_access //handle TileCursor update logic TileViewerFeature.update(); - WarningsFeature.update(); + if (Config.Warning) + WarningsFeature.update(); - ReadTileFeature.update(); + if (Config.ReadTile) + ReadTileFeature.update(); if (!RadarFeature.isRunning && Config.Radar) { From 9d2dd976072a2b8bd3aa7247f3df85f414ec1a70 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 20 Aug 2022 22:58:42 +0530 Subject: [PATCH 190/232] Added 'not usable here' to inventories --- stardew-access/Patches/BundleMenuPatches.cs | 4 ++++ stardew-access/Patches/DonationMenuPatches.cs | 7 +++++++ stardew-access/Patches/GameMenuPatches.cs | 19 ++++++++++++------- stardew-access/Patches/MenuPatches.cs | 14 ++++++++++++++ 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/stardew-access/Patches/BundleMenuPatches.cs b/stardew-access/Patches/BundleMenuPatches.cs index 2e34511..fc3dc6c 100644 --- a/stardew-access/Patches/BundleMenuPatches.cs +++ b/stardew-access/Patches/BundleMenuPatches.cs @@ -334,6 +334,10 @@ namespace stardew_access.Patches } toSpeak = $"{item.Stack} {toSpeak}"; + if (!__instance.inventory.highlightMethod(__instance.inventory.actualInventory[currentInventorySlot])) + { + toSpeak = $"{toSpeak} not usable here"; + } } else { diff --git a/stardew-access/Patches/DonationMenuPatches.cs b/stardew-access/Patches/DonationMenuPatches.cs index 159bc25..f4969ca 100644 --- a/stardew-access/Patches/DonationMenuPatches.cs +++ b/stardew-access/Patches/DonationMenuPatches.cs @@ -311,8 +311,15 @@ namespace stardew_access.Patches if (__instance.inventory.actualInventory[i] == null) toSpeak = "Empty slot"; else + { toSpeak = $"{__instance.inventory.actualInventory[i].Stack} {__instance.inventory.actualInventory[i].DisplayName}"; + if (!__instance.inventory.highlightMethod(__instance.inventory.actualInventory[i])) + { + toSpeak = $"{toSpeak} not usable here"; + } + } + if (fieldOfficeMenuQuery != $"{toSpeak}:{i}") { fieldOfficeMenuQuery = $"{toSpeak}:{i}"; diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index 97ce43a..f322daf 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -241,7 +241,7 @@ namespace stardew_access.Patches #endregion #region Narrate hovered item - if (narrateHoveredItemInInventory(__instance.inventory.inventory, __instance.inventory.actualInventory, x, y, hoverPrice: __instance.hoverPrice)) + if (narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y, hoverPrice: __instance.hoverPrice)) { shopMenuQueryKey = ""; return; @@ -396,7 +396,7 @@ namespace stardew_access.Patches #endregion #region Narrate hovered item - if (narrateHoveredItemInInventory(__instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) + if (narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) geodeMenuQueryKey = ""; #endregion } @@ -573,14 +573,14 @@ namespace stardew_access.Patches #endregion #region Narrate hovered item - if (narrateHoveredItemInInventory(__instance.inventory.inventory, __instance.inventory.actualInventory, x, y, true)) + if (narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y, true)) { gameMenuQueryKey = ""; itemGrabMenuQueryKey = ""; return; } - if (narrateHoveredItemInInventory(__instance.ItemsToGrabMenu.inventory, __instance.ItemsToGrabMenu.actualInventory, x, y, true)) + if (narrateHoveredItemInInventory(__instance.ItemsToGrabMenu, __instance.ItemsToGrabMenu.inventory, __instance.ItemsToGrabMenu.actualInventory, x, y, true)) { gameMenuQueryKey = ""; itemGrabMenuQueryKey = ""; @@ -855,7 +855,7 @@ namespace stardew_access.Patches #endregion #region Narrate hovered item - if (narrateHoveredItemInInventory(__instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) + if (narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y)) { gameMenuQueryKey = ""; craftingPageQueryKey = ""; @@ -1072,7 +1072,7 @@ namespace stardew_access.Patches #endregion #region Narrate hovered item - if (narrateHoveredItemInInventory(__instance.inventory.inventory, __instance.inventory.actualInventory, x, y, true)) + if (narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y, true)) { gameMenuQueryKey = ""; inventoryPageQueryKey = ""; @@ -1174,7 +1174,7 @@ namespace stardew_access.Patches } } - internal static bool narrateHoveredItemInInventory(List inventory, IList actualInventory, int x, int y, bool giveExtraDetails = false, int hoverPrice = -1, int extraItemToShowIndex = -1, int extraItemToShowAmount = -1) + internal static bool narrateHoveredItemInInventory(InventoryMenu inventoryMenu, List inventory, IList actualInventory, int x, int y, bool giveExtraDetails = false, int hoverPrice = -1, int extraItemToShowIndex = -1, int extraItemToShowAmount = -1) { #region Narrate hovered item for (int i = 0; i < inventory.Count; i++) @@ -1272,6 +1272,11 @@ namespace stardew_access.Patches price = $"Sell Price: {hoverPrice} g"; } + if (!inventoryMenu.highlightMethod(actualInventory[i])) + { + name = $"{name} not usable here"; + } + if (giveExtraDetails) { if (stack > 1) diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 5bd970b..40c98bf 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -136,8 +136,15 @@ namespace stardew_access.Patches if (__instance.inventory.actualInventory[i] == null) toSpeak = "Empty slot"; else + { toSpeak = $"{__instance.inventory.actualInventory[i].Stack} {__instance.inventory.actualInventory[i].DisplayName}"; + if (!__instance.inventory.highlightMethod(__instance.inventory.actualInventory[i])) + { + toSpeak = $"{toSpeak} not usable here"; + } + } + if (forgeMenuQuery != $"{toSpeak}:{i}") { forgeMenuQuery = $"{toSpeak}:{i}"; @@ -296,8 +303,15 @@ namespace stardew_access.Patches if (__instance.inventory.actualInventory[i] == null) toSpeak = "Empty slot"; else + { toSpeak = $"{__instance.inventory.actualInventory[i].Stack} {__instance.inventory.actualInventory[i].DisplayName}"; + if (!__instance.inventory.highlightMethod(__instance.inventory.actualInventory[i])) + { + toSpeak = $"{toSpeak} not usable here"; + } + } + if (tailoringMenuQuery != $"{toSpeak}:{i}") { tailoringMenuQuery = $"{toSpeak}:{i}"; From 9afeb7f778f6d105995ab7c49928a265208430e8 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 20 Aug 2022 23:28:36 +0530 Subject: [PATCH 191/232] Release 1.3.2 --- stardew-access/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index a82c930..988b529 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.3.1", + "Version": "1.3.2", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From 1bac13bc1440fba399ecd0af887c30a862eb00d7 Mon Sep 17 00:00:00 2001 From: Bartholomue Date: Mon, 22 Aug 2022 06:56:57 -0500 Subject: [PATCH 192/232] Minor Static Tiles Additions Added a boulder to the Railroad as a decoration, helpful for finding a secret note dig spot. Added a 2X1 bench in the desert as furniture, helpful for finding a secret note dig spot. Fixed the size of the pond next to the spa to 3X3 rather than 4X3. --- stardew-access/assets/static-tiles.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 9123647..7f9a6aa 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -792,6 +792,11 @@ "x": [9, 10], "y": [35, 36], "type": "decoration" + }, + "Bench": { + "x": [43, 44], + "y": [54], + "type": "furniture" } }, "elliotthouse": { @@ -1619,8 +1624,13 @@ "y": [36], "type": "other" }, + "Boulder": { + "x": [11], + "y": [38], + "type": "decoration" + }, "Water": { - "x": [13, 14, 15, 16], + "x": [14, 15, 16], "y": [54, 55, 56], "type": "water" } From a3df4e259130aaac942ec530102f98b3dd8f5930 Mon Sep 17 00:00:00 2001 From: Bartholomue Date: Mon, 22 Aug 2022 16:32:42 -0500 Subject: [PATCH 193/232] Joja Mart Definitions Some more static tile tweaks and additions: Desert: Added a bench as furniture, helpful for finding a secret note dig spot. Joja Mart: Added tile definitions for product shelves as decorations. Railroad: Added a boulder as a decoration, helpful for finding a secret note dig spot. Railroad: Added the summit boulder as a decoration. Railroad: Fixed the size of the pond next to the spa to 3X3 rather than 4X3. Railroad: Added a bench as furniture. Railroad: Renamed the empty crate tile as "empty box" to match its examine description. Railroad: Expanded the dumpster definition to span across two tiles, as it's sitting half way across both. --- stardew-access/assets/static-tiles.json | 551 +++++++++++++++++++++++- 1 file changed, 548 insertions(+), 3 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 7f9a6aa..62011c8 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -1320,8 +1320,543 @@ }, "Shop Counter": { "x": [10], - "y": [25], + "y": [23, 24, 25, 26], "type": "interactable" + }, + "Salt": { + "x": [3], + "y": [10, 11], + "type": "decoration" + }, + "Multipurpose Sauce": { + "x": [3], + "y": [12], + "type": "decoration" + }, + "Marinara Sauce": { + "x": [3], + "y": [13], + "type": "decoration" + }, + "Pesto": { + "x": [3], + "y": [14], + "type": "decoration" + }, + "Vegan Sausage": { + "x": [3], + "y": [15, 16], + "type": "decoration" + }, + "Coconut Meat": { + "x": [3], + "y": [17], + "type": "decoration" + }, + "Cowboy Sauce": { + "x": [3], + "y": [18, 19], + "type": "decoration" + }, + "Hoisin Sauce": { + "x": [4], + "y": [10], + "type": "decoration" + }, + "Pretzel Snacks": { + "x": [4], + "y": [11, 12], + "type": "decoration" + }, + "Honey Sauce": { + "x": [4], + "y": [13], + "type": "decoration" + }, + "Yeast": { + "x": [4], + "y": [14], + "type": "decoration" + }, + "Sugar Cones": { + "x": [4], + "y": [15], + "type": "decoration" + }, + "Sugar Stars": { + "x": [4], + "y": [16], + "type": "decoration" + }, + "Gummies": { + "x": [4], + "y": [17], + "type": "decoration" + }, + "Fruit Snacks": { + "x": [4], + "y": [18], + "type": "decoration" + }, + "Marinated Mushrooms": { + "x": [4], + "y": [19], + "type": "decoration" + }, + "Low Fat Beans": { + "x": [7], + "y": [11, 12], + "type": "decoration" + }, + "White Fungus Soda": { + "x": [7], + "y": [13], + "type": "decoration" + }, + "Powdered Breakfast": { + "x": [7], + "y": [14, 15], + "type": "decoration" + }, + "Powdered Wine": { + "x": [7], + "y": [16], + "type": "decoration" + }, + "Canned Scrambled Eggs": { + "x": [7], + "y": [17], + "type": "decoration" + }, + "Canned Fish": { + "x": [7], + "y": [18], + "type": "decoration" + }, + "Canned Meals": { + "x": [7], + "y": [19, 20], + "type": "decoration" + }, + "Protein Bars": { + "x": [8], + "y": [10, 11], + "type": "decoration" + }, + "Protein Shakes": { + "x": [8], + "y": [12], + "type": "decoration" + }, + "Protein Powder": { + "x": [8], + "y": [13, 14], + "type": "decoration" + }, + "Jitter Juice": { + "x": [8], + "y": [15], + "type": "decoration" + }, + "Explosion Juice": { + "x": [8], + "y": [16], + "type": "decoration" + }, + "Blue Alien Energy": { + "x": [8], + "y": [17], + "type": "decoration" + }, + "Hyper Juice": { + "x": [8], + "y": [18], + "type": "decoration" + }, + "Joja Cola Amped": { + "x": [8], + "y": [19, 20], + "type": "decoration" + }, + "Sugar Free Lollipop": { + "x": [11], + "y": [10, 11], + "type": "decoration" + }, + "Drinkable Frosting": { + "x": [11], + "y": [12, 13], + "type": "decoration" + }, + "Cocoa Zeppelins": { + "x": [11], + "y": [14], + "type": "decoration" + }, + "Silly Sauce": { + "x": [11], + "y": [15], + "type": "decoration" + }, + "Grape Nibble Pebbles": { + "x": [11], + "y": [16, 17], + "type": "decoration" + }, + "Strawberry Lollipops": { + "x": [11], + "y": [18], + "type": "decoration" + }, + "Sugar Orbs": { + "x": [11], + "y": [19], + "type": "decoration" + }, + "24 Grain Bread": { + "x": [12], + "y": [10, 11], + "type": "decoration" + }, + "11 Grain Bread": { + "x": [12], + "y": [12], + "type": "decoration" + }, + "Wheat Bread": { + "x": [12], + "y": [13], + "type": "decoration" + }, + "Oat Bread": { + "x": [12], + "y": [14], + "type": "decoration" + }, + "Brown Bread": { + "x": [12], + "y": [15], + "type": "decoration" + }, + "Rye Bread": { + "x": [12], + "y": [16], + "type": "decoration" + }, + "Sourdough Bread": { + "x": [12], + "y": [17], + "type": "decoration" + }, + "Bread": { + "x": [12], + "y": [18], + "type": "decoration" + }, + "Pancakes": { + "x": [12], + "y": [19], + "type": "decoration" + }, + "Maple Sauce": { + "x": [15], + "y": [10], + "type": "decoration" + }, + "Maypul Syrup": { + "x": [15], + "y": [11], + "type": "decoration" + }, + "Cooking Oil": { + "x": [15], + "y": [12], + "type": "decoration" + }, + "Corn Oil": { + "x": [15], + "y": [13], + "type": "decoration" + }, + "Synthetic Butter": { + "x": [15], + "y": [14], + "type": "decoration" + }, + "Powdered Cream": { + "x": [15], + "y": [15], + "type": "decoration" + }, + "Powdered Butter": { + "x": [15], + "y": [16], + "type": "decoration" + }, + "Joja Oil": { + "x": [15], + "y": [17], + "type": "decoration" + }, + "Canola Oil": { + "x": [15], + "y": [18], + "type": "decoration" + }, + "Olive Oil": { + "x": [15], + "y": [19, 20], + "type": "decoration" + }, + "Marshmallows": { + "x": [16], + "y": [10], + "type": "decoration" + }, + "Hair Gel": { + "x": [16], + "y": [11, 12], + "type": "decoration" + }, + "Floss": { + "x": [16], + "y": [13], + "type": "decoration" + }, + "Mouthwash": { + "x": [16], + "y": [14], + "type": "decoration" + }, + "Toothpaste": { + "x": [16], + "y": [15], + "type": "decoration" + }, + "Hand Soap": { + "x": [16], + "y": [16], + "type": "decoration" + }, + "Multipurpose Detergent": { + "x": [16], + "y": [17], + "type": "decoration" + }, + "Laundry Detergent": { + "x": [16], + "y": [18, 19, 20], + "type": "decoration" + }, + "Taco Sauce For Babies": { + "x": [19], + "y": [10], + "type": "decoration" + }, + "Granny's Sauce": { + "x": [19], + "y": [11], + "type": "decoration" + }, + "Very Mild Sauce": { + "x": [19], + "y": [12], + "type": "decoration" + }, + "Mild Sauce": { + "x": [19], + "y": [13], + "type": "decoration" + }, + "Take Me To The Emergency Room Sauce": { + "x": [19], + "y": [14], + "type": "decoration" + }, + "Unbearable Torturous Blaze Sauce": { + "x": [19], + "y": [15], + "type": "decoration" + }, + "Searing Pain Sauce": { + "x": [19], + "y": [16], + "type": "decoration" + }, + "Inferno Sauce": { + "x": [19], + "y": [17], + "type": "decoration" + }, + "Fire Sauce": { + "x": [19], + "y": [18], + "type": "decoration" + }, + "Hot Sauce": { + "x": [19], + "y": [19], + "type": "decoration" + }, + "Hoisin Sauce": { + "x": [20], + "y": [10], + "type": "decoration" + }, + "Churro Kit": { + "x": [20], + "y": [11, 12], + "type": "decoration" + }, + "Honey Sauce": { + "x": [20], + "y": [13], + "type": "decoration" + }, + "Yeast": { + "x": [20], + "y": [14], + "type": "decoration" + }, + "Sugar Cones": { + "x": [20], + "y": [15], + "type": "decoration" + }, + "Sugar Stars": { + "x": [20], + "y": [16], + "type": "decoration" + }, + "Gummies": { + "x": [20], + "y": [17], + "type": "decoration" + }, + "Fruit Snacks": { + "x": [20], + "y": [18], + "type": "decoration" + }, + "Marinated Mushrooms": { + "x": [20], + "y": [19], + "type": "decoration" + }, + "Canned Soup": { + "x": [23], + "y": [10, 11], + "type": "decoration" + }, + "White Bread": { + "x": [23], + "y": [12], + "type": "decoration" + }, + "Canned Shrimp.": { + "x": [23], + "y": [13], + "type": "decoration" + }, + "Shrimp Snack": { + "x": [23], + "y": [14], + "type": "decoration" + }, + "Budget Wine": { + "x": [23], + "y": [15], + "type": "decoration" + }, + "X-Treme Chipz": { + "x": [23], + "y": [16], + "type": "decoration" + }, + "Pure Gluten.": { + "x": [23], + "y": [17], + "type": "decoration" + }, + "Joja Meal": { + "x": [23], + "y": [18], + "type": "decoration" + }, + "Canned Pasta": { + "x": [23], + "y": [19], + "type": "decoration" + }, + "Ranch Chips": { + "x": [24], + "y": [10, 11], + "type": "decoration" + }, + "Smokehouse Chips": { + "x": [24], + "y": [12], + "type": "decoration" + }, + "Cinnamon Sugar Chips": { + "x": [24], + "y": [13], + "type": "decoration" + }, + "Sugar Cane Chips": { + "x": [24], + "y": [14], + "type": "decoration" + }, + "Inferno Chips": { + "x": [24], + "y": [15, 16], + "type": "decoration" + }, + "Carbo Cones": { + "x": [24], + "y": [17, 18], + "type": "decoration" + }, + "Corn Flour": { + "x": [24], + "y": [19], + "type": "decoration" + }, + "Low Fat Beans": { + "x": [28], + "y": [11, 12], + "type": "decoration" + }, + "White Fungus Soda": { + "x": [28], + "y": [13], + "type": "decoration" + }, + "Powdered Breakfast": { + "x": [28], + "y": [14, 15], + "type": "decoration" + }, + "Powdered Wine": { + "x": [28], + "y": [16], + "type": "decoration" + }, + "Canned Scrambled Eggs": { + "x": [28], + "y": [17], + "type": "decoration" + }, + "Canned Fish": { + "x": [28], + "y": [18], + "type": "decoration" + }, + "Canned Meals": { + "x": [28], + "y": [19], + "type": "decoration" } }, "joshhouse": { @@ -1614,13 +2149,13 @@ "y": [35], "type": "door" }, - "Empty Crate": { + "Empty Box": { "x": [45], "y": [40], "type": "other" }, "Dumpster": { - "x": [28], + "x": [28, 29], "y": [36], "type": "other" }, @@ -1629,6 +2164,16 @@ "y": [38], "type": "decoration" }, + "Summit Boulder": { + "x": [24, 25], + "y": [35], + "type": "decoration" + }, + "Bench": { + "x": [38, 39, 40], + "y": [40], + "type": "furniture" + }, "Water": { "x": [14, 15, 16], "y": [54, 55, 56], From 52c8b6e3e3be46d2a11239cb3145d7bc8e5075c9 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Tue, 23 Aug 2022 08:57:34 +0530 Subject: [PATCH 194/232] Reverted back to using escape key --- stardew-access/Patches/MenuPatches.cs | 4 ++-- stardew-access/Patches/TitleMenuPatches.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/stardew-access/Patches/MenuPatches.cs b/stardew-access/Patches/MenuPatches.cs index 40c98bf..993d580 100644 --- a/stardew-access/Patches/MenuPatches.cs +++ b/stardew-access/Patches/MenuPatches.cs @@ -472,7 +472,7 @@ namespace stardew_access.Patches { string toSpeak = ""; int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position - bool isEnterPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Enter); // For escaping/unselecting from the animal name text box + bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box if (firstTimeInNamingMenu) { @@ -485,7 +485,7 @@ namespace stardew_access.Patches ___textBox.Update(); toSpeak = ___textBox.Text; - if (isEnterPressed) + if (isEscPressed) { ___textBox.Selected = false; } diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index 39f3387..3873cf3 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -247,7 +247,7 @@ namespace stardew_access.Patches { try { - bool isEnterPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Enter); // For escaping/unselecting from the animal name text box + bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box string toSpeak = " "; string currentPetName = getCurrentPetName(); @@ -255,7 +255,7 @@ namespace stardew_access.Patches { toSpeak = ___nameBox.Text; - if (isEnterPressed) + if (isEscPressed) { ___nameBox.Selected = false; } @@ -264,7 +264,7 @@ namespace stardew_access.Patches { toSpeak = ___farmnameBox.Text; - if (isEnterPressed) + if (isEscPressed) { ___farmnameBox.Selected = false; } @@ -273,7 +273,7 @@ namespace stardew_access.Patches { toSpeak = ___favThingBox.Text; - if (isEnterPressed) + if (isEscPressed) { ___favThingBox.Selected = false; } From 589ec8eff851852567beb2c5e69dbe52ee469500 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Tue, 23 Aug 2022 08:58:50 +0530 Subject: [PATCH 195/232] Bug fix --- stardew-access/Patches/BuildingNAnimalMenuPatches.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs index 2b6d929..910e45b 100644 --- a/stardew-access/Patches/BuildingNAnimalMenuPatches.cs +++ b/stardew-access/Patches/BuildingNAnimalMenuPatches.cs @@ -29,7 +29,7 @@ namespace stardew_access.Patches { int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); // For narrating animal details - bool isEnterPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Enter); // For escaping/unselecting from the animal name text box + bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box string toSpeak = " ", details = " "; isOnFarm = ___movingAnimal; @@ -40,7 +40,7 @@ namespace stardew_access.Patches { toSpeak = ___textBox.Text; - if (isEnterPressed) + if (isEscPressed) { ___textBox.Selected = false; } From 2cebc7ce99e155a38fdf1856e1d127c31074bd09 Mon Sep 17 00:00:00 2001 From: Bartholomue Date: Fri, 26 Aug 2022 07:59:41 -0500 Subject: [PATCH 196/232] More Tile Definitions Started adding [number] to tile definitions with the same name to coincide with the unique bracketed identifiers. Adventure Guild: Defined various decoration and furniture, though I did leave out all the barrels as I felt they weren't important enough to enumerate. Archaeology House: Defined the 21 lost books in Strings/Notes under the "Other" category, as their state requires previous interaction to determine. Defined other various other decoration and furniture. Town: Defined the 5X4 fountain in the water category. Expanded the mountain entrance definition to cover the entire 5X1 tile width. Defined various benches as furniture. Defined two bridges in the bridge category. --- stardew-access/assets/static-tiles.json | 317 +++++++++++++++++++++++- 1 file changed, 316 insertions(+), 1 deletion(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 62011c8..b448c00 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -19,6 +19,41 @@ "x": [11], "y": [12], "type": "npc" + }, + "Stool[1]": { + "x": [2], + "y": [13], + "type": "furniture" + }, + "Stool[2]": { + "x": [11], + "y": [16], + "type": "furniture" + }, + "Stool[3]": { + "x": [9], + "y": [17], + "type": "furniture" + }, + "Stool[4]": { + "x": [11], + "y": [18], + "type": "furniture" + }, + "Fireplace": { + "x": [9, 10], + "y": [11], + "type": "decoration" + }, + "Table": { + "x": [10, 11], + "y": [17], + "type": "decoration" + }, + "House Plant": { + "x": [12], + "y": [18], + "type": "decoration" } }, "animalshop": { @@ -134,10 +169,230 @@ "y": [9], "type": "other" }, + "Rearrange Collection": { + "x": [2], + "y": [9], + "type": "interactable" + }, "Counter": { "x": [3], "y": [9], "type": "interactable" + }, + "Stool": { + "x": [1], + "y": [10], + "type": "furniture" + }, + "Chair[1]": { + "x": [13], + "y": [14], + "type": "furniture" + }, + "Chair[2]": { + "x": [9], + "y": [16], + "type": "furniture" + }, + "Chair[3]": { + "x": [13], + "y": [18], + "type": "furniture" + }, + "Chair[4]": { + "x": [17], + "y": [18], + "type": "furniture" + }, + "Table": { + "x": [17, 18], + "y": [10, 11], + "type": "decoration" + }, + "Desk[1]": { + "x": [9, 10], + "y": [15], + "type": "decoration" + }, + "Desk[2]": { + "x": [13, 14], + "y": [15], + "type": "decoration" + }, + "Desk[3]": { + "x": [17, 18], + "y": [15], + "type": "decoration" + }, + "Desk[4]": { + "x": [9, 10], + "y": [17], + "type": "decoration" + }, + "Desk[5]": { + "x": [13, 14], + "y": [17], + "type": "decoration" + }, + "Desk[6]": { + "x": [17, 18], + "y": [17], + "type": "decoration" + }, + "House Plant[1]": { + "x": [27], + "y": [4], + "type": "decoration" + }, + "House Plant[2]": { + "x": [26], + "y": [18], + "type": "decoration" + }, + "Palm Tree": { + "x": [38], + "y": [4], + "type": "decoration" + }, + "Fireplace": { + "x": [43, 44], + "y": [4], + "type": "decoration" + }, + "History Book": { + "x": [15, 16, 17, 18, 19], + "y": [8], + "type": "decoration" + }, + "Step Stool[1]": { + "x": [10], + "y": [9], + "type": "decoration" + }, + "Step Stool[2]": { + "x": [18], + "y": [5], + "type": "decoration" + }, + "Teddybear": { + "x": [20], + "y": [9], + "type": "decoration" + }, + "Atlas": { + "x": [21], + "y": [7, 8], + "type": "decoration" + }, + "Children's Book": { + "x": [21], + "y": [9, 10], + "type": "decoration" + }, + "Book 1": { + "x": [9], + "y": [13], + "type": "other" + }, + "Book 2": { + "x": [9], + "y": [12], + "type": "other" + }, + "Book 3": { + "x": [9], + "y": [11], + "type": "other" + }, + "Book 4": { + "x": [9], + "y": [10], + "type": "other" + }, + "Book 5": { + "x": [9], + "y": [4], + "type": "other" + }, + "Book 6": { + "x": [10], + "y": [4], + "type": "other" + }, + "Book 7": { + "x": [11], + "y": [4], + "type": "other" + }, + "Book 8": { + "x": [12], + "y": [4], + "type": "other" + }, + "Book 9": { + "x": [13], + "y": [4], + "type": "other" + }, + "Book 10": { + "x": [15], + "y": [4], + "type": "other" + }, + "Book 11": { + "x": [16], + "y": [4], + "type": "other" + }, + "Book 12": { + "x": [17], + "y": [4], + "type": "other" + }, + "Book 13": { + "x": [19], + "y": [4], + "type": "other" + }, + "Book 14": { + "x": [20], + "y": [4], + "type": "other" + }, + "Book 15": { + "x": [21], + "y": [4], + "type": "other" + }, + "Book 16": { + "x": [11], + "y": [8], + "type": "other" + }, + "Book 17": { + "x": [12], + "y": [8], + "type": "other" + }, + "Book 18": { + "x": [13], + "y": [8], + "type": "other" + }, + "Book 19": { + "x": [9], + "y": [9], + "type": "other" + }, + "Book 20": { + "x": [9], + "y": [8], + "type": "other" + }, + "Book 21": { + "x": [9], + "y": [7], + "type": "other" } }, "backwoods": { @@ -2658,9 +2913,69 @@ "type": "door" }, "Mountain Entrance": { - "x": [81], + "x": [79, 80, 81, 82, 83], "y": [0], "type": "door" + }, + "Joja Bridge": { + "x": [92, 93, 94, 95, 96], + "y": [11], + "type": "bridge" + }, + "Beach Bridge": { + "x": [75, 76, 77, 78, 79, 80, 81, 82], + "y": [4, 5], + "type": "bridge" + }, + "Bench[1]": { + "x": [25, 26, 27], + "y": [21], + "type": "furniture" + }, + "Bench[2]": { + "x": [20], + "y": [26, 27], + "type": "furniture" + }, + "Bench[3]": { + "x": [33], + "y": [26, 27], + "type": "furniture" + }, + "Bench[4]": { + "x": [16], + "y": [57, 58], + "type": "furniture" + }, + "Bench[5]": { + "x": [17, 18], + "y": [63], + "type": "furniture" + }, + "Bench[6]": { + "x": [42, 43, 44, 45], + "y": [77], + "type": "furniture" + }, + "Bench[7]": { + "x": [41], + "y": [78, 79], + "type": "furniture" + }, + "Bench[8]": { + "x": [46], + "y": [78, 79], + "type": "furniture" + }, + "Smelter": { + "x": [100, 101], + "y": [79, 80], + "type": "decoration" + }, + "Fountain": { + "x": [24, 25, 26, 27, 28], + "y": [25, 26, 27, 28], + "type": "water" } }, "trailer": { From 0edd6e54e03bf4906ba52c6704fa7532197c5e44 Mon Sep 17 00:00:00 2001 From: Bartholomue Date: Wed, 7 Sep 2022 03:23:41 -0500 Subject: [PATCH 197/232] Redact static-tiles.json Re-added the redundancy entries for big barn/coop and deluxe barn/coop to cover Stardew inconsistent behavior. Removed the static tile definitions for the lost books in preparation for them being made dynamic. Recommend adding the lost books to the "Other" category so that they'll be easily navigable without cluttering up the other categories which contain important objects. --- stardew-access/assets/static-tiles.json | 365 +++++++++++++++++------- 1 file changed, 259 insertions(+), 106 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index b448c00..2d25f12 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -169,7 +169,7 @@ "y": [9], "type": "other" }, - "Rearrange Collection": { + "Collection": { "x": [2], "y": [9], "type": "interactable" @@ -288,111 +288,6 @@ "x": [21], "y": [9, 10], "type": "decoration" - }, - "Book 1": { - "x": [9], - "y": [13], - "type": "other" - }, - "Book 2": { - "x": [9], - "y": [12], - "type": "other" - }, - "Book 3": { - "x": [9], - "y": [11], - "type": "other" - }, - "Book 4": { - "x": [9], - "y": [10], - "type": "other" - }, - "Book 5": { - "x": [9], - "y": [4], - "type": "other" - }, - "Book 6": { - "x": [10], - "y": [4], - "type": "other" - }, - "Book 7": { - "x": [11], - "y": [4], - "type": "other" - }, - "Book 8": { - "x": [12], - "y": [4], - "type": "other" - }, - "Book 9": { - "x": [13], - "y": [4], - "type": "other" - }, - "Book 10": { - "x": [15], - "y": [4], - "type": "other" - }, - "Book 11": { - "x": [16], - "y": [4], - "type": "other" - }, - "Book 12": { - "x": [17], - "y": [4], - "type": "other" - }, - "Book 13": { - "x": [19], - "y": [4], - "type": "other" - }, - "Book 14": { - "x": [20], - "y": [4], - "type": "other" - }, - "Book 15": { - "x": [21], - "y": [4], - "type": "other" - }, - "Book 16": { - "x": [11], - "y": [8], - "type": "other" - }, - "Book 17": { - "x": [12], - "y": [8], - "type": "other" - }, - "Book 18": { - "x": [13], - "y": [8], - "type": "other" - }, - "Book 19": { - "x": [9], - "y": [9], - "type": "other" - }, - "Book 20": { - "x": [9], - "y": [8], - "type": "other" - }, - "Book 21": { - "x": [9], - "y": [7], - "type": "other" } }, "backwoods": { @@ -501,6 +396,58 @@ "type": "door" } }, + "big barn": { + "Hay Hopper": { + "x": [6], + "y": [3], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [8], + "y": [3], + "type": "other" + }, + "Feeding Bench 2": { + "x": [9], + "y": [3], + "type": "other" + }, + "Feeding Bench 3": { + "x": [10], + "y": [3], + "type": "other" + }, + "Feeding Bench 4": { + "x": [11], + "y": [3], + "type": "other" + }, + "Feeding Bench 5": { + "x": [12], + "y": [3], + "type": "other" + }, + "Feeding Bench 6": { + "x": [13], + "y": [3], + "type": "other" + }, + "Feeding Bench 7": { + "x": [14], + "y": [3], + "type": "other" + }, + "Feeding Bench 8": { + "x": [15], + "y": [3], + "type": "other" + }, + "Exit": { + "x": [11], + "y": [14], + "type": "door" + } + }, "barn3": { "Hay Hopper": { "x": [6], @@ -573,6 +520,78 @@ "type": "door" } }, + "deluxe barn": { + "Hay Hopper": { + "x": [6], + "y": [3], + "type": "interactable" + }, + "Feeding Bench 1": { + "x": [8], + "y": [3], + "type": "other" + }, + "Feeding Bench 2": { + "x": [9], + "y": [3], + "type": "other" + }, + "Feeding Bench 3": { + "x": [10], + "y": [3], + "type": "other" + }, + "Feeding Bench 4": { + "x": [11], + "y": [3], + "type": "other" + }, + "Feeding Bench 5": { + "x": [12], + "y": [3], + "type": "other" + }, + "Feeding Bench 6": { + "x": [13], + "y": [3], + "type": "other" + }, + "Feeding Bench 7": { + "x": [14], + "y": [3], + "type": "other" + }, + "Feeding Bench 8": { + "x": [15], + "y": [3], + "type": "other" + }, + "Feeding Bench 9": { + "x": [16], + "y": [3], + "type": "other" + }, + "Feeding Bench 10": { + "x": [17], + "y": [3], + "type": "other" + }, + "Feeding Bench 11": { + "x": [18], + "y": [3], + "type": "other" + }, + "Feeding Bench 12": { + "x": [19], + "y": [3], + "type": "other" + }, + "Exit": { + "x": [11], + "y": [14], + "type": "door" + } + }, "bathhouse_entry": { "Exit": { "x": [5], @@ -935,6 +954,63 @@ "type": "door" } }, + "big coop": { + "Hay Hopper": { + "x": [3], + "y": [3], + "type": "interactable" + }, + "Incubator": { + "x": [2], + "y": [3], + "type": "machine" + }, + "Feeding Bench 1": { + "x": [6], + "y": [3], + "type": "other" + }, + "Feeding Bench 2": { + "x": [7], + "y": [3], + "type": "other" + }, + "Feeding Bench 3": { + "x": [8], + "y": [3], + "type": "other" + }, + "Feeding Bench 4": { + "x": [9], + "y": [3], + "type": "other" + }, + "Feeding Bench 5": { + "x": [10], + "y": [3], + "type": "other" + }, + "Feeding Bench 6": { + "x": [11], + "y": [3], + "type": "other" + }, + "Feeding Bench 7": { + "x": [12], + "y": [3], + "type": "other" + }, + "Feeding Bench 8": { + "x": [13], + "y": [3], + "type": "other" + }, + "Exit": { + "x": [2], + "y": [9], + "type": "door" + } + }, "coop3": { "Hay Hopper": { "x": [3], @@ -1012,6 +1088,83 @@ "type": "door" } }, + "deluxe coop": { + "Hay Hopper": { + "x": [3], + "y": [3], + "type": "interactable" + }, + "Incubator": { + "x": [2], + "y": [3], + "type": "machine" + }, + "Feeding Bench 1": { + "x": [6], + "y": [3], + "type": "other" + }, + "Feeding Bench 2": { + "x": [7], + "y": [3], + "type": "other" + }, + "Feeding Bench 3": { + "x": [8], + "y": [3], + "type": "other" + }, + "Feeding Bench 4": { + "x": [9], + "y": [3], + "type": "other" + }, + "Feeding Bench 5": { + "x": [10], + "y": [3], + "type": "other" + }, + "Feeding Bench 6": { + "x": [11], + "y": [3], + "type": "other" + }, + "Feeding Bench 7": { + "x": [12], + "y": [3], + "type": "other" + }, + "Feeding Bench 8": { + "x": [13], + "y": [3], + "type": "other" + }, + "Feeding Bench 9": { + "x": [14], + "y": [3], + "type": "other" + }, + "Feeding Bench 10": { + "x": [15], + "y": [3], + "type": "other" + }, + "Feeding Bench 11": { + "x": [16], + "y": [3], + "type": "other" + }, + "Feeding Bench 12": { + "x": [17], + "y": [3], + "type": "other" + }, + "Exit": { + "x": [2], + "y": [9], + "type": "door" + } + }, "desert": { "Bus": { "x": [18], From 01ec8209af45cd1b10aff4a64f54f0719030e366 Mon Sep 17 00:00:00 2001 From: Bartholomue Date: Wed, 7 Sep 2022 06:54:21 -0500 Subject: [PATCH 198/232] Added Big Shed Redundancy Duplicated "shed2" to "big shed" for stardew redundancy in name changes upon logging back in to the game. --- stardew-access/assets/static-tiles.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 2d25f12..5d1a2be 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -2901,6 +2901,13 @@ "type": "door" } }, + "big shed": { + "Exit": { + "x": [9], + "y": [16], + "type": "door" + } + }, "skullcave": { "Exit": { "x": [7], From e1614917a2c78b67fd4f81c74ffb56362d09e40a Mon Sep 17 00:00:00 2001 From: Bartholomue Date: Fri, 9 Sep 2022 09:24:00 -0500 Subject: [PATCH 199/232] Alphabetize Feeding Benches Renamed the feeding benches to have an alphabet suffix, rather than number. The name sort is by string rather than integer value. Renaming to "Feeding Bench "J" (for example) makes sure that the tenth bench actually comes after the ninth bench, rather than after the first. --- stardew-access/assets/static-tiles.json | 176 ++++++++++++------------ 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 5d1a2be..30fc1d1 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -318,22 +318,22 @@ "y": [3], "type": "interactable" }, - "Feeding Bench 1": { + "Feeding Bench A": { "x": [8], "y": [3], "type": "other" }, - "Feeding Bench 2": { + "Feeding Bench B": { "x": [9], "y": [3], "type": "other" }, - "Feeding Bench 3": { + "Feeding Bench C": { "x": [10], "y": [3], "type": "other" }, - "Feeding Bench 4": { + "Feeding Bench D": { "x": [11], "y": [3], "type": "other" @@ -350,42 +350,42 @@ "y": [3], "type": "interactable" }, - "Feeding Bench 1": { + "Feeding Bench A": { "x": [8], "y": [3], "type": "other" }, - "Feeding Bench 2": { + "Feeding Bench B": { "x": [9], "y": [3], "type": "other" }, - "Feeding Bench 3": { + "Feeding Bench C": { "x": [10], "y": [3], "type": "other" }, - "Feeding Bench 4": { + "Feeding Bench D": { "x": [11], "y": [3], "type": "other" }, - "Feeding Bench 5": { + "Feeding Bench E": { "x": [12], "y": [3], "type": "other" }, - "Feeding Bench 6": { + "Feeding Bench F": { "x": [13], "y": [3], "type": "other" }, - "Feeding Bench 7": { + "Feeding Bench G": { "x": [14], "y": [3], "type": "other" }, - "Feeding Bench 8": { + "Feeding Bench H": { "x": [15], "y": [3], "type": "other" @@ -402,42 +402,42 @@ "y": [3], "type": "interactable" }, - "Feeding Bench 1": { + "Feeding Bench A": { "x": [8], "y": [3], "type": "other" }, - "Feeding Bench 2": { + "Feeding Bench B": { "x": [9], "y": [3], "type": "other" }, - "Feeding Bench 3": { + "Feeding Bench C": { "x": [10], "y": [3], "type": "other" }, - "Feeding Bench 4": { + "Feeding Bench D": { "x": [11], "y": [3], "type": "other" }, - "Feeding Bench 5": { + "Feeding Bench E": { "x": [12], "y": [3], "type": "other" }, - "Feeding Bench 6": { + "Feeding Bench F": { "x": [13], "y": [3], "type": "other" }, - "Feeding Bench 7": { + "Feeding Bench G": { "x": [14], "y": [3], "type": "other" }, - "Feeding Bench 8": { + "Feeding Bench H": { "x": [15], "y": [3], "type": "other" @@ -454,62 +454,62 @@ "y": [3], "type": "interactable" }, - "Feeding Bench 1": { + "Feeding Bench A": { "x": [8], "y": [3], "type": "other" }, - "Feeding Bench 2": { + "Feeding Bench B": { "x": [9], "y": [3], "type": "other" }, - "Feeding Bench 3": { + "Feeding Bench C": { "x": [10], "y": [3], "type": "other" }, - "Feeding Bench 4": { + "Feeding Bench D": { "x": [11], "y": [3], "type": "other" }, - "Feeding Bench 5": { + "Feeding Bench E": { "x": [12], "y": [3], "type": "other" }, - "Feeding Bench 6": { + "Feeding Bench F": { "x": [13], "y": [3], "type": "other" }, - "Feeding Bench 7": { + "Feeding Bench G": { "x": [14], "y": [3], "type": "other" }, - "Feeding Bench 8": { + "Feeding Bench H": { "x": [15], "y": [3], "type": "other" }, - "Feeding Bench 9": { + "Feeding Bench I": { "x": [16], "y": [3], "type": "other" }, - "Feeding Bench 10": { + "Feeding Bench J": { "x": [17], "y": [3], "type": "other" }, - "Feeding Bench 11": { + "Feeding Bench K": { "x": [18], "y": [3], "type": "other" }, - "Feeding Bench 12": { + "Feeding Bench L": { "x": [19], "y": [3], "type": "other" @@ -526,62 +526,62 @@ "y": [3], "type": "interactable" }, - "Feeding Bench 1": { + "Feeding Bench A": { "x": [8], "y": [3], "type": "other" }, - "Feeding Bench 2": { + "Feeding Bench B": { "x": [9], "y": [3], "type": "other" }, - "Feeding Bench 3": { + "Feeding Bench C": { "x": [10], "y": [3], "type": "other" }, - "Feeding Bench 4": { + "Feeding Bench D": { "x": [11], "y": [3], "type": "other" }, - "Feeding Bench 5": { + "Feeding Bench E": { "x": [12], "y": [3], "type": "other" }, - "Feeding Bench 6": { + "Feeding Bench F": { "x": [13], "y": [3], "type": "other" }, - "Feeding Bench 7": { + "Feeding Bench G": { "x": [14], "y": [3], "type": "other" }, - "Feeding Bench 8": { + "Feeding Bench H": { "x": [15], "y": [3], "type": "other" }, - "Feeding Bench 9": { + "Feeding Bench I": { "x": [16], "y": [3], "type": "other" }, - "Feeding Bench 10": { + "Feeding Bench J": { "x": [17], "y": [3], "type": "other" }, - "Feeding Bench 11": { + "Feeding Bench K": { "x": [18], "y": [3], "type": "other" }, - "Feeding Bench 12": { + "Feeding Bench L": { "x": [19], "y": [3], "type": "other" @@ -871,22 +871,22 @@ "y": [3], "type": "interactable" }, - "Feeding Bench 1": { + "Feeding Bench A": { "x": [6], "y": [3], "type": "other" }, - "Feeding Bench 2": { + "Feeding Bench B": { "x": [7], "y": [3], "type": "other" }, - "Feeding Bench 3": { + "Feeding Bench C": { "x": [8], "y": [3], "type": "other" }, - "Feeding Bench 4": { + "Feeding Bench D": { "x": [9], "y": [3], "type": "other" @@ -908,42 +908,42 @@ "y": [3], "type": "machine" }, - "Feeding Bench 1": { + "Feeding Bench A": { "x": [6], "y": [3], "type": "other" }, - "Feeding Bench 2": { + "Feeding Bench B": { "x": [7], "y": [3], "type": "other" }, - "Feeding Bench 3": { + "Feeding Bench C": { "x": [8], "y": [3], "type": "other" }, - "Feeding Bench 4": { + "Feeding Bench D": { "x": [9], "y": [3], "type": "other" }, - "Feeding Bench 5": { + "Feeding Bench E": { "x": [10], "y": [3], "type": "other" }, - "Feeding Bench 6": { + "Feeding Bench F": { "x": [11], "y": [3], "type": "other" }, - "Feeding Bench 7": { + "Feeding Bench G": { "x": [12], "y": [3], "type": "other" }, - "Feeding Bench 8": { + "Feeding Bench H": { "x": [13], "y": [3], "type": "other" @@ -965,42 +965,42 @@ "y": [3], "type": "machine" }, - "Feeding Bench 1": { + "Feeding Bench A": { "x": [6], "y": [3], "type": "other" }, - "Feeding Bench 2": { + "Feeding Bench B": { "x": [7], "y": [3], "type": "other" }, - "Feeding Bench 3": { + "Feeding Bench C": { "x": [8], "y": [3], "type": "other" }, - "Feeding Bench 4": { + "Feeding Bench D": { "x": [9], "y": [3], "type": "other" }, - "Feeding Bench 5": { + "Feeding Bench E": { "x": [10], "y": [3], "type": "other" }, - "Feeding Bench 6": { + "Feeding Bench F": { "x": [11], "y": [3], "type": "other" }, - "Feeding Bench 7": { + "Feeding Bench G": { "x": [12], "y": [3], "type": "other" }, - "Feeding Bench 8": { + "Feeding Bench H": { "x": [13], "y": [3], "type": "other" @@ -1022,62 +1022,62 @@ "y": [3], "type": "machine" }, - "Feeding Bench 1": { + "Feeding Bench A": { "x": [6], "y": [3], "type": "other" }, - "Feeding Bench 2": { + "Feeding Bench B": { "x": [7], "y": [3], "type": "other" }, - "Feeding Bench 3": { + "Feeding Bench C": { "x": [8], "y": [3], "type": "other" }, - "Feeding Bench 4": { + "Feeding Bench D": { "x": [9], "y": [3], "type": "other" }, - "Feeding Bench 5": { + "Feeding Bench E": { "x": [10], "y": [3], "type": "other" }, - "Feeding Bench 6": { + "Feeding Bench F": { "x": [11], "y": [3], "type": "other" }, - "Feeding Bench 7": { + "Feeding Bench G": { "x": [12], "y": [3], "type": "other" }, - "Feeding Bench 8": { + "Feeding Bench H": { "x": [13], "y": [3], "type": "other" }, - "Feeding Bench 9": { + "Feeding Bench I": { "x": [14], "y": [3], "type": "other" }, - "Feeding Bench 10": { + "Feeding Bench J": { "x": [15], "y": [3], "type": "other" }, - "Feeding Bench 11": { + "Feeding Bench K": { "x": [16], "y": [3], "type": "other" }, - "Feeding Bench 12": { + "Feeding Bench L": { "x": [17], "y": [3], "type": "other" @@ -1099,62 +1099,62 @@ "y": [3], "type": "machine" }, - "Feeding Bench 1": { + "Feeding Bench A": { "x": [6], "y": [3], "type": "other" }, - "Feeding Bench 2": { + "Feeding Bench B": { "x": [7], "y": [3], "type": "other" }, - "Feeding Bench 3": { + "Feeding Bench C": { "x": [8], "y": [3], "type": "other" }, - "Feeding Bench 4": { + "Feeding Bench D": { "x": [9], "y": [3], "type": "other" }, - "Feeding Bench 5": { + "Feeding Bench E": { "x": [10], "y": [3], "type": "other" }, - "Feeding Bench 6": { + "Feeding Bench F": { "x": [11], "y": [3], "type": "other" }, - "Feeding Bench 7": { + "Feeding Bench G": { "x": [12], "y": [3], "type": "other" }, - "Feeding Bench 8": { + "Feeding Bench H": { "x": [13], "y": [3], "type": "other" }, - "Feeding Bench 9": { + "Feeding Bench I": { "x": [14], "y": [3], "type": "other" }, - "Feeding Bench 10": { + "Feeding Bench J": { "x": [15], "y": [3], "type": "other" }, - "Feeding Bench 11": { + "Feeding Bench K": { "x": [16], "y": [3], "type": "other" }, - "Feeding Bench 12": { + "Feeding Bench L": { "x": [17], "y": [3], "type": "other" From 542d5390a2d26f0002cef16e8a8ad53e31030b58 Mon Sep 17 00:00:00 2001 From: Bartholomue Date: Sun, 18 Sep 2022 13:17:34 -0500 Subject: [PATCH 200/232] Pool And Saloon Definitions Tile definitions for bathhouse Pool and Saloon, including pool entry steps, decoration, and furniture. --- stardew-access/assets/static-tiles.json | 119 +++++++++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 30fc1d1..ca5c786 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -614,13 +614,48 @@ "bathhouse_pool": { "Women's Locker Room Door": { "x": [6], - "y": [1], + "y": [0], "type": "door" }, "Men's Locker Room Door": { "x": [21], - "y": [1], + "y": [0], "type": "door" + }, + "Bench[1]": { + "x": [11], + "y": [9], + "type": "furniture" + }, + "Bench[2]": { + "x": [13], + "y": [9], + "type": "furniture" + }, + "Bench[3]": { + "x": [16], + "y": [9], + "type": "furniture" + }, + "Northwest Steps": { + "x": [6], + "y": [8], + "type": "other" + }, + "Northeast Steps": { + "x": [21], + "y": [8], + "type": "other" + }, + "Southwest Steps": { + "x": [7], + "y": [26], + "type": "other" + }, + "Southeast Steps": { + "x": [20], + "y": [26], + "type": "other" } }, "bathhouse_womenslocker": { @@ -2557,6 +2592,11 @@ "y": [35], "type": "door" }, + "Summit Entrance": { + "x": [32, 33, 34], + "y": [0], + "type": "door" + }, "Empty Box": { "x": [45], "y": [40], @@ -2648,6 +2688,81 @@ "x": [28], "y": [7], "type": "other" + }, + "Barstool[1]": { + "x": [7], + "y": [19], + "type": "furniture" + }, + "Barstool[2]": { + "x": [13], + "y": [20], + "type": "furniture" + }, + "Barstool[3]": { + "x": [15], + "y": [20], + "type": "furniture" + }, + "Barstool[4]": { + "x": [19], + "y": [20], + "type": "furniture" + }, + "Barstool[5]": { + "x": [20], + "y": [18], + "type": "furniture" + }, + "Stool[1]": { + "x": [3], + "y": [19], + "type": "furniture" + }, + "Stool[2]": { + "x": [4], + "y": [21], + "type": "furniture" + }, + "Stool[3]": { + "x": [9], + "y": [21], + "type": "furniture" + }, + "Stool[4]": { + "x": [2], + "y": [22], + "type": "furniture" + }, + "Stool[5]": { + "x": [12], + "y": [22], + "type": "furniture" + }, + "Stool[6]": { + "x": [16], + "y": [23], + "type": "furniture" + }, + "Table[1]": { + "x": [3, 4], + "y": [20], + "type": "decoration" + }, + "Table[2]": { + "x": [10, 11], + "y": [22], + "type": "decoration" + }, + "Table[3]": { + "x": [1, 2], + "y": [23], + "type": "decoration" + }, + "Table[4]": { + "x": [17, 18], + "y": [23], + "type": "decoration" } }, "samhouse": { From ad22982b0943a9e0dca2ce3f07908fcb87e27c85 Mon Sep 17 00:00:00 2001 From: Bartholomue Date: Mon, 26 Sep 2022 13:11:52 -0500 Subject: [PATCH 201/232] Recategorize Pierre's Bin It was erroneously in the interactable category. It's now been moved to other in order to match the other special orders drop box definitions. --- stardew-access/assets/static-tiles.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index ca5c786..47ec818 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -2957,7 +2957,7 @@ "Pierre's Bin": { "x": [19], "y": [28], - "type": "interactable" + "type": "other" }, "Shrine of Yoba": { "x": [37], From a0336d630fcf42b836f534de41a1a6b2076d03c0 Mon Sep 17 00:00:00 2001 From: Bartholomue Date: Fri, 30 Sep 2022 07:31:09 -0500 Subject: [PATCH 202/232] Expanded Some Tile Ranges Expanded some tile definitions to cover the full breadth of the objects, in cases where they only listed as one tile but actually spanned a few tiles (such as the exit in the secret woods). --- stardew-access/assets/static-tiles.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 47ec818..cb2ded0 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -1296,7 +1296,7 @@ "type": "interactable" }, "Lumber Pile": { - "x": [60], + "x": [60, 61], "y": [14], "type": "decoration" } @@ -2957,7 +2957,7 @@ "Pierre's Bin": { "x": [19], "y": [28], - "type": "other" + "type": "interactable" }, "Shrine of Yoba": { "x": [37], @@ -3426,7 +3426,7 @@ "woods": { "Forest Entrance": { "x": [59], - "y": [17], + "y": [15, 16, 17], "type": "door" }, "Old Master Cannoli": { From 452bbdf86c25063e2ddb56a326e8fb1dc263a6f3 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sun, 9 Oct 2022 13:38:26 +0530 Subject: [PATCH 203/232] Fixed duplicates in static-tiles.json --- stardew-access/assets/static-tiles.json | 60 ++++++++++++------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index cb2ded0..d33a93a 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -1801,7 +1801,7 @@ "y": [18, 19], "type": "decoration" }, - "Hoisin Sauce": { + "Hoisin Sauce A": { "x": [4], "y": [10], "type": "decoration" @@ -1811,72 +1811,72 @@ "y": [11, 12], "type": "decoration" }, - "Honey Sauce": { + "Honey Sauce A": { "x": [4], "y": [13], "type": "decoration" }, - "Yeast": { + "Yeast A": { "x": [4], "y": [14], "type": "decoration" }, - "Sugar Cones": { + "Sugar Cones A": { "x": [4], "y": [15], "type": "decoration" }, - "Sugar Stars": { + "Sugar Stars A": { "x": [4], "y": [16], "type": "decoration" }, - "Gummies": { + "Gummies A": { "x": [4], "y": [17], "type": "decoration" }, - "Fruit Snacks": { + "Fruit Snacks A": { "x": [4], "y": [18], "type": "decoration" }, - "Marinated Mushrooms": { + "Marinated Mushrooms A": { "x": [4], "y": [19], "type": "decoration" }, - "Low Fat Beans": { + "Low Fat Beans A": { "x": [7], "y": [11, 12], "type": "decoration" }, - "White Fungus Soda": { + "White Fungus Soda A": { "x": [7], "y": [13], "type": "decoration" }, - "Powdered Breakfast": { + "Powdered Breakfast A": { "x": [7], "y": [14, 15], "type": "decoration" }, - "Powdered Wine": { + "Powdered Wine A": { "x": [7], "y": [16], "type": "decoration" }, - "Canned Scrambled Eggs": { + "Canned Scrambled Eggs A": { "x": [7], "y": [17], "type": "decoration" }, - "Canned Fish": { + "Canned Fish A": { "x": [7], "y": [18], "type": "decoration" }, - "Canned Meals": { + "Canned Meals A": { "x": [7], "y": [19, 20], "type": "decoration" @@ -2141,7 +2141,7 @@ "y": [19], "type": "decoration" }, - "Hoisin Sauce": { + "Hoisin Sauce B": { "x": [20], "y": [10], "type": "decoration" @@ -2151,37 +2151,37 @@ "y": [11, 12], "type": "decoration" }, - "Honey Sauce": { + "Honey Sauce B": { "x": [20], "y": [13], "type": "decoration" }, - "Yeast": { + "Yeast B": { "x": [20], "y": [14], "type": "decoration" }, - "Sugar Cones": { + "Sugar Cones B": { "x": [20], "y": [15], "type": "decoration" }, - "Sugar Stars": { + "Sugar Stars B": { "x": [20], "y": [16], "type": "decoration" }, - "Gummies": { + "Gummies B": { "x": [20], "y": [17], "type": "decoration" }, - "Fruit Snacks": { + "Fruit Snacks B": { "x": [20], "y": [18], "type": "decoration" }, - "Marinated Mushrooms": { + "Marinated Mushrooms B": { "x": [20], "y": [19], "type": "decoration" @@ -2266,37 +2266,37 @@ "y": [19], "type": "decoration" }, - "Low Fat Beans": { + "Low Fat Beans B": { "x": [28], "y": [11, 12], "type": "decoration" }, - "White Fungus Soda": { + "White Fungus Soda B": { "x": [28], "y": [13], "type": "decoration" }, - "Powdered Breakfast": { + "Powdered Breakfast B": { "x": [28], "y": [14, 15], "type": "decoration" }, - "Powdered Wine": { + "Powdered Wine B": { "x": [28], "y": [16], "type": "decoration" }, - "Canned Scrambled Eggs": { + "Canned Scrambled Eggs B": { "x": [28], "y": [17], "type": "decoration" }, - "Canned Fish": { + "Canned Fish B": { "x": [28], "y": [18], "type": "decoration" }, - "Canned Meals": { + "Canned Meals B": { "x": [28], "y": [19], "type": "decoration" From b600eda78e3dd5bd374f7a60fc153733826792e9 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sun, 9 Oct 2022 13:50:35 +0530 Subject: [PATCH 204/232] Added command to toggle tts --- stardew-access/CustomCommands.cs | 8 ++++++++ stardew-access/ModConfig.cs | 1 + stardew-access/ScreenReader/ScreenReaderLinux.cs | 13 ++++++++----- stardew-access/ScreenReader/ScreenReaderWindows.cs | 3 +++ 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index 7f231ad..df18be3 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -521,6 +521,14 @@ namespace stardew_access MainClass.DebugLog("Warnings is " + (MainClass.Config.Warning ? "on" : "off")); }); + + helper.ConsoleCommands.Add("tts", "Toggles the screen reader/tts", (string commmand, string[] args) => + { + MainClass.Config.TTS = !MainClass.Config.TTS; + helper.WriteConfig(MainClass.Config); + + MainClass.DebugLog("TTS is " + (MainClass.Config.TTS ? "on" : "off")); + }); #endregion } } diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index 76f057d..f4ea1de 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -78,6 +78,7 @@ namespace stardew_access public Boolean VerboseCoordinates { get; set; } = true; public Boolean SnapMouse { get; set; } = true; // Toggles the snap mouse feature public Boolean Warning { get; set; } = true; // Toggles the warnings feature + public Boolean TTS { get; set; } = true; // Toggles the screen reader/tts. // TODO add command to toggle warning feature #endregion diff --git a/stardew-access/ScreenReader/ScreenReaderLinux.cs b/stardew-access/ScreenReader/ScreenReaderLinux.cs index d058cd6..0cbe1f3 100644 --- a/stardew-access/ScreenReader/ScreenReaderLinux.cs +++ b/stardew-access/ScreenReader/ScreenReaderLinux.cs @@ -62,11 +62,14 @@ namespace stardew_access.ScreenReader if (text == null) return; - if (initialized) - { - GoString str = new GoString(text, text.Length); - Speak(str, interrupt); - } + if (!initialized) + return; + + if (!MainClass.Config.TTS) + return; + + GoString str = new GoString(text, text.Length); + Speak(str, interrupt); } public void SayWithChecker(string text, bool interrupt) diff --git a/stardew-access/ScreenReader/ScreenReaderWindows.cs b/stardew-access/ScreenReader/ScreenReaderWindows.cs index deb8a73..a52d546 100644 --- a/stardew-access/ScreenReader/ScreenReaderWindows.cs +++ b/stardew-access/ScreenReader/ScreenReaderWindows.cs @@ -62,6 +62,9 @@ namespace stardew_access.ScreenReader if (screenReader == null) return; + if (!MainClass.Config.TTS) + return; + screenReader.Speak(text, interrupt); } From 5205df84002667b768df61d533fcddb1fba7fa8c Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Mon, 10 Oct 2022 15:56:35 +0530 Subject: [PATCH 205/232] Replaced ^ with \n when narrating texts --- stardew-access/ScreenReader/ScreenReaderLinux.cs | 2 ++ stardew-access/ScreenReader/ScreenReaderWindows.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/stardew-access/ScreenReader/ScreenReaderLinux.cs b/stardew-access/ScreenReader/ScreenReaderLinux.cs index 0cbe1f3..7e58c69 100644 --- a/stardew-access/ScreenReader/ScreenReaderLinux.cs +++ b/stardew-access/ScreenReader/ScreenReaderLinux.cs @@ -68,6 +68,8 @@ namespace stardew_access.ScreenReader if (!MainClass.Config.TTS) return; + if (text.Contains('^')) text = text.Replace('^', '\n'); + GoString str = new GoString(text, text.Length); Speak(str, interrupt); } diff --git a/stardew-access/ScreenReader/ScreenReaderWindows.cs b/stardew-access/ScreenReader/ScreenReaderWindows.cs index a52d546..f2da4b5 100644 --- a/stardew-access/ScreenReader/ScreenReaderWindows.cs +++ b/stardew-access/ScreenReader/ScreenReaderWindows.cs @@ -65,6 +65,8 @@ namespace stardew_access.ScreenReader if (!MainClass.Config.TTS) return; + if (text.Contains('^')) text = text.Replace('^', '\n'); + screenReader.Speak(text, interrupt); } From 8c080e11f099cd65bbfcfa20e599c602be5b4b21 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Mon, 10 Oct 2022 16:14:14 +0530 Subject: [PATCH 206/232] Change priority order in getObjectAtTile() --- stardew-access/Features/TileInfo.cs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index ebcdd04..95539c4 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -695,19 +695,7 @@ namespace stardew_access.Features // Get object names based on index (string? name, CATEGORY category) correctNameAndCategory = getCorrectNameAndCategoryFromIndex(index, obj.Name); - if (correctNameAndCategory.name != null) - toReturn = correctNameAndCategory; - else if (obj.name.ToLower().Equals("stone")) - toReturn.category = CATEGORY.Debris; - else if (obj.name.ToLower().Equals("twig")) - toReturn.category = CATEGORY.Debris; - else if (obj.name.ToLower().Contains("quartz")) - toReturn.category = CATEGORY.MineItems; - else if (obj.name.ToLower().Contains("earth crystal")) - toReturn.category = CATEGORY.MineItems; - else if (obj.name.ToLower().Contains("frozen tear")) - toReturn.category = CATEGORY.MineItems; - else if (obj is Chest) + if (obj is Chest) { Chest chest = (Chest)obj; toReturn = (chest.DisplayName, CATEGORY.Chests); @@ -719,7 +707,7 @@ namespace stardew_access.Features else if (obj is Sign sign) { if (sign.displayItem.Value != null) - toReturn.name = $"{obj.DisplayName}, {sign.displayItem.Value.DisplayName}"; + toReturn.name = $"{sign.DisplayName}, {sign.displayItem.Value.DisplayName}"; } else if (obj is Furniture furniture) { @@ -758,6 +746,18 @@ namespace stardew_access.Features } } } + else if (correctNameAndCategory.name != null) + toReturn = correctNameAndCategory; + else if (obj.name.ToLower().Equals("stone")) + toReturn.category = CATEGORY.Debris; + else if (obj.name.ToLower().Equals("twig")) + toReturn.category = CATEGORY.Debris; + else if (obj.name.ToLower().Contains("quartz")) + toReturn.category = CATEGORY.MineItems; + else if (obj.name.ToLower().Contains("earth crystal")) + toReturn.category = CATEGORY.MineItems; + else if (obj.name.ToLower().Contains("frozen tear")) + toReturn.category = CATEGORY.MineItems; if (toReturn.category == CATEGORY.Machines) // Fix for `Harvestable table` and `Busy nodes` { From 34cb47bdaa5d0e215873a03f5959f6ace0d3624a Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 22 Oct 2022 21:53:10 +0530 Subject: [PATCH 207/232] Issue#42 feeding benches in coops and barns are now dynamic --- stardew-access/Features/TileInfo.cs | 68 ++++ stardew-access/assets/static-tiles.json | 440 ------------------------ 2 files changed, 68 insertions(+), 440 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index ebcdd04..1e15d45 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -478,6 +478,72 @@ namespace stardew_access.Features return (CATEGORY.Interactables, "Island Trader"); } } + else if (Game1.currentLocation.name.Value.ToLower().Equals("coop")) + { + if (x >= 6 && x <= 9 && y == 3) + { + (string? name, CATEGORY category) bench = getObjectAtTile(x, y, true); + if (bench.name != null && bench.name.ToLower().Contains("hay")) + return (CATEGORY.Others, "Feeding Bench"); + else + return (CATEGORY.Others, "Empty Feeding Bench"); + } + } + else if (Game1.currentLocation.name.Value.ToLower().Equals("big coop") || Game1.currentLocation.name.Value.ToLower().Equals("coop2")) + { + if (x >= 6 && x <= 13 && y == 3) + { + (string? name, CATEGORY category) bench = getObjectAtTile(x, y, true); + if (bench.name != null && bench.name.ToLower().Contains("hay")) + return (CATEGORY.Others, "Feeding Bench"); + else + return (CATEGORY.Others, "Empty Feeding Bench"); + } + } + else if (Game1.currentLocation.name.Value.ToLower().Equals("deluxe coop") || Game1.currentLocation.name.Value.ToLower().Equals("coop3")) + { + if (x >= 6 && x <= 17 && y == 3) + { + (string? name, CATEGORY category) bench = getObjectAtTile(x, y, true); + if (bench.name != null && bench.name.ToLower().Contains("hay")) + return (CATEGORY.Others, "Feeding Bench"); + else + return (CATEGORY.Others, "Empty Feeding Bench"); + } + } + else if (Game1.currentLocation.name.Value.ToLower().Equals("barn")) + { + if (x >= 8 && x <= 11 && y == 3) + { + (string? name, CATEGORY category) bench = getObjectAtTile(x, y, true); + if (bench.name != null && bench.name.ToLower().Contains("hay")) + return (CATEGORY.Others, "Feeding Bench"); + else + return (CATEGORY.Others, "Empty Feeding Bench"); + } + } + else if (Game1.currentLocation.name.Value.ToLower().Equals("big barn") || Game1.currentLocation.name.Value.ToLower().Equals("barn2")) + { + if (x >= 8 && x <= 15 && y == 3) + { + (string? name, CATEGORY category) bench = getObjectAtTile(x, y, true); + if (bench.name != null && bench.name.ToLower().Contains("hay")) + return (CATEGORY.Others, "Feeding Bench"); + else + return (CATEGORY.Others, "Empty Feeding Bench"); + } + } + else if (Game1.currentLocation.name.Value.ToLower().Equals("deluxe barn") || Game1.currentLocation.name.Value.ToLower().Equals("barn3")) + { + if (x >= 8 && x <= 19 && y == 3) + { + (string? name, CATEGORY category) bench = getObjectAtTile(x, y, true); + if (bench.name != null && bench.name.ToLower().Contains("hay")) + return (CATEGORY.Others, "Feeding Bench"); + else + return (CATEGORY.Others, "Empty Feeding Bench"); + } + } return (null, null); } @@ -689,6 +755,8 @@ namespace stardew_access.Features (string? name, CATEGORY category) toReturn = (null, CATEGORY.Others); StardewValley.Object obj = Game1.currentLocation.getObjectAtTile(x, y); + if (obj == null) return toReturn; + int index = obj.ParentSheetIndex; toReturn.name = obj.DisplayName; diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index d33a93a..cbb3968 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -318,26 +318,6 @@ "y": [3], "type": "interactable" }, - "Feeding Bench A": { - "x": [8], - "y": [3], - "type": "other" - }, - "Feeding Bench B": { - "x": [9], - "y": [3], - "type": "other" - }, - "Feeding Bench C": { - "x": [10], - "y": [3], - "type": "other" - }, - "Feeding Bench D": { - "x": [11], - "y": [3], - "type": "other" - }, "Exit": { "x": [11], "y": [14], @@ -350,46 +330,6 @@ "y": [3], "type": "interactable" }, - "Feeding Bench A": { - "x": [8], - "y": [3], - "type": "other" - }, - "Feeding Bench B": { - "x": [9], - "y": [3], - "type": "other" - }, - "Feeding Bench C": { - "x": [10], - "y": [3], - "type": "other" - }, - "Feeding Bench D": { - "x": [11], - "y": [3], - "type": "other" - }, - "Feeding Bench E": { - "x": [12], - "y": [3], - "type": "other" - }, - "Feeding Bench F": { - "x": [13], - "y": [3], - "type": "other" - }, - "Feeding Bench G": { - "x": [14], - "y": [3], - "type": "other" - }, - "Feeding Bench H": { - "x": [15], - "y": [3], - "type": "other" - }, "Exit": { "x": [11], "y": [14], @@ -402,46 +342,6 @@ "y": [3], "type": "interactable" }, - "Feeding Bench A": { - "x": [8], - "y": [3], - "type": "other" - }, - "Feeding Bench B": { - "x": [9], - "y": [3], - "type": "other" - }, - "Feeding Bench C": { - "x": [10], - "y": [3], - "type": "other" - }, - "Feeding Bench D": { - "x": [11], - "y": [3], - "type": "other" - }, - "Feeding Bench E": { - "x": [12], - "y": [3], - "type": "other" - }, - "Feeding Bench F": { - "x": [13], - "y": [3], - "type": "other" - }, - "Feeding Bench G": { - "x": [14], - "y": [3], - "type": "other" - }, - "Feeding Bench H": { - "x": [15], - "y": [3], - "type": "other" - }, "Exit": { "x": [11], "y": [14], @@ -454,66 +354,6 @@ "y": [3], "type": "interactable" }, - "Feeding Bench A": { - "x": [8], - "y": [3], - "type": "other" - }, - "Feeding Bench B": { - "x": [9], - "y": [3], - "type": "other" - }, - "Feeding Bench C": { - "x": [10], - "y": [3], - "type": "other" - }, - "Feeding Bench D": { - "x": [11], - "y": [3], - "type": "other" - }, - "Feeding Bench E": { - "x": [12], - "y": [3], - "type": "other" - }, - "Feeding Bench F": { - "x": [13], - "y": [3], - "type": "other" - }, - "Feeding Bench G": { - "x": [14], - "y": [3], - "type": "other" - }, - "Feeding Bench H": { - "x": [15], - "y": [3], - "type": "other" - }, - "Feeding Bench I": { - "x": [16], - "y": [3], - "type": "other" - }, - "Feeding Bench J": { - "x": [17], - "y": [3], - "type": "other" - }, - "Feeding Bench K": { - "x": [18], - "y": [3], - "type": "other" - }, - "Feeding Bench L": { - "x": [19], - "y": [3], - "type": "other" - }, "Exit": { "x": [11], "y": [14], @@ -526,66 +366,6 @@ "y": [3], "type": "interactable" }, - "Feeding Bench A": { - "x": [8], - "y": [3], - "type": "other" - }, - "Feeding Bench B": { - "x": [9], - "y": [3], - "type": "other" - }, - "Feeding Bench C": { - "x": [10], - "y": [3], - "type": "other" - }, - "Feeding Bench D": { - "x": [11], - "y": [3], - "type": "other" - }, - "Feeding Bench E": { - "x": [12], - "y": [3], - "type": "other" - }, - "Feeding Bench F": { - "x": [13], - "y": [3], - "type": "other" - }, - "Feeding Bench G": { - "x": [14], - "y": [3], - "type": "other" - }, - "Feeding Bench H": { - "x": [15], - "y": [3], - "type": "other" - }, - "Feeding Bench I": { - "x": [16], - "y": [3], - "type": "other" - }, - "Feeding Bench J": { - "x": [17], - "y": [3], - "type": "other" - }, - "Feeding Bench K": { - "x": [18], - "y": [3], - "type": "other" - }, - "Feeding Bench L": { - "x": [19], - "y": [3], - "type": "other" - }, "Exit": { "x": [11], "y": [14], @@ -906,26 +686,6 @@ "y": [3], "type": "interactable" }, - "Feeding Bench A": { - "x": [6], - "y": [3], - "type": "other" - }, - "Feeding Bench B": { - "x": [7], - "y": [3], - "type": "other" - }, - "Feeding Bench C": { - "x": [8], - "y": [3], - "type": "other" - }, - "Feeding Bench D": { - "x": [9], - "y": [3], - "type": "other" - }, "Exit": { "x": [2], "y": [9], @@ -943,46 +703,6 @@ "y": [3], "type": "machine" }, - "Feeding Bench A": { - "x": [6], - "y": [3], - "type": "other" - }, - "Feeding Bench B": { - "x": [7], - "y": [3], - "type": "other" - }, - "Feeding Bench C": { - "x": [8], - "y": [3], - "type": "other" - }, - "Feeding Bench D": { - "x": [9], - "y": [3], - "type": "other" - }, - "Feeding Bench E": { - "x": [10], - "y": [3], - "type": "other" - }, - "Feeding Bench F": { - "x": [11], - "y": [3], - "type": "other" - }, - "Feeding Bench G": { - "x": [12], - "y": [3], - "type": "other" - }, - "Feeding Bench H": { - "x": [13], - "y": [3], - "type": "other" - }, "Exit": { "x": [2], "y": [9], @@ -1000,46 +720,6 @@ "y": [3], "type": "machine" }, - "Feeding Bench A": { - "x": [6], - "y": [3], - "type": "other" - }, - "Feeding Bench B": { - "x": [7], - "y": [3], - "type": "other" - }, - "Feeding Bench C": { - "x": [8], - "y": [3], - "type": "other" - }, - "Feeding Bench D": { - "x": [9], - "y": [3], - "type": "other" - }, - "Feeding Bench E": { - "x": [10], - "y": [3], - "type": "other" - }, - "Feeding Bench F": { - "x": [11], - "y": [3], - "type": "other" - }, - "Feeding Bench G": { - "x": [12], - "y": [3], - "type": "other" - }, - "Feeding Bench H": { - "x": [13], - "y": [3], - "type": "other" - }, "Exit": { "x": [2], "y": [9], @@ -1057,66 +737,6 @@ "y": [3], "type": "machine" }, - "Feeding Bench A": { - "x": [6], - "y": [3], - "type": "other" - }, - "Feeding Bench B": { - "x": [7], - "y": [3], - "type": "other" - }, - "Feeding Bench C": { - "x": [8], - "y": [3], - "type": "other" - }, - "Feeding Bench D": { - "x": [9], - "y": [3], - "type": "other" - }, - "Feeding Bench E": { - "x": [10], - "y": [3], - "type": "other" - }, - "Feeding Bench F": { - "x": [11], - "y": [3], - "type": "other" - }, - "Feeding Bench G": { - "x": [12], - "y": [3], - "type": "other" - }, - "Feeding Bench H": { - "x": [13], - "y": [3], - "type": "other" - }, - "Feeding Bench I": { - "x": [14], - "y": [3], - "type": "other" - }, - "Feeding Bench J": { - "x": [15], - "y": [3], - "type": "other" - }, - "Feeding Bench K": { - "x": [16], - "y": [3], - "type": "other" - }, - "Feeding Bench L": { - "x": [17], - "y": [3], - "type": "other" - }, "Exit": { "x": [2], "y": [9], @@ -1134,66 +754,6 @@ "y": [3], "type": "machine" }, - "Feeding Bench A": { - "x": [6], - "y": [3], - "type": "other" - }, - "Feeding Bench B": { - "x": [7], - "y": [3], - "type": "other" - }, - "Feeding Bench C": { - "x": [8], - "y": [3], - "type": "other" - }, - "Feeding Bench D": { - "x": [9], - "y": [3], - "type": "other" - }, - "Feeding Bench E": { - "x": [10], - "y": [3], - "type": "other" - }, - "Feeding Bench F": { - "x": [11], - "y": [3], - "type": "other" - }, - "Feeding Bench G": { - "x": [12], - "y": [3], - "type": "other" - }, - "Feeding Bench H": { - "x": [13], - "y": [3], - "type": "other" - }, - "Feeding Bench I": { - "x": [14], - "y": [3], - "type": "other" - }, - "Feeding Bench J": { - "x": [15], - "y": [3], - "type": "other" - }, - "Feeding Bench K": { - "x": [16], - "y": [3], - "type": "other" - }, - "Feeding Bench L": { - "x": [17], - "y": [3], - "type": "other" - }, "Exit": { "x": [2], "y": [9], From a9160b4ff643a13f36fb86d3c31d38f6c2453274 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 22 Oct 2022 22:19:38 +0530 Subject: [PATCH 208/232] Issue#54 the names in static tiles now ignore anything between square brackets --- stardew-access/Features/StaticTiles.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/stardew-access/Features/StaticTiles.cs b/stardew-access/Features/StaticTiles.cs index 5befb38..c0bb806 100644 --- a/stardew-access/Features/StaticTiles.cs +++ b/stardew-access/Features/StaticTiles.cs @@ -45,7 +45,7 @@ namespace stardew_access.Features foreach (var location in data) { - if (!Game1.currentLocation.Name.ToLower().Equals(location.Key.ToLower())) + if (!Game1.currentLocation.name.Value.ToLower().Equals(location.Key.ToLower())) continue; if (location.Value != null) @@ -83,7 +83,21 @@ namespace stardew_access.Features } if (isXPresent && isYPresent) - return (tile.Key, CATEGORY.FromString(tileType.ToString().ToLower())); + { + string key = tile.Key; + if (key.Contains('[') && key.Contains(']')) + { + int i1 = key.IndexOf('['); + int i2 = key.LastIndexOf(']'); + + if (i1 < i2) + { + key = key.Remove(i1, ++i2 - i1); + } + } + + return (key.Trim(), CATEGORY.FromString(tileType.ToString().ToLower())); + } } } From d131d26ac834d50b7cf9a38fc1d81c77497b6486 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 22 Oct 2022 22:23:14 +0530 Subject: [PATCH 209/232] Issue#54 updated names in static-tiles.json according to the issue --- stardew-access/assets/static-tiles.json | 60 ++++++++++++------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index cbb3968..cbc2549 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -1361,7 +1361,7 @@ "y": [18, 19], "type": "decoration" }, - "Hoisin Sauce A": { + "Hoisin Sauce[A]": { "x": [4], "y": [10], "type": "decoration" @@ -1371,72 +1371,72 @@ "y": [11, 12], "type": "decoration" }, - "Honey Sauce A": { + "Honey Sauce[A]": { "x": [4], "y": [13], "type": "decoration" }, - "Yeast A": { + "Yeast[A]": { "x": [4], "y": [14], "type": "decoration" }, - "Sugar Cones A": { + "Sugar Cones[A]": { "x": [4], "y": [15], "type": "decoration" }, - "Sugar Stars A": { + "Sugar Stars[A]": { "x": [4], "y": [16], "type": "decoration" }, - "Gummies A": { + "Gummies[A]": { "x": [4], "y": [17], "type": "decoration" }, - "Fruit Snacks A": { + "Fruit Snacks[A]": { "x": [4], "y": [18], "type": "decoration" }, - "Marinated Mushrooms A": { + "Marinated Mushrooms[A]": { "x": [4], "y": [19], "type": "decoration" }, - "Low Fat Beans A": { + "Low Fat Beans[A]": { "x": [7], "y": [11, 12], "type": "decoration" }, - "White Fungus Soda A": { + "White Fungus Soda[A]": { "x": [7], "y": [13], "type": "decoration" }, - "Powdered Breakfast A": { + "Powdered Breakfast[A]": { "x": [7], "y": [14, 15], "type": "decoration" }, - "Powdered Wine A": { + "Powdered Wine[A]": { "x": [7], "y": [16], "type": "decoration" }, - "Canned Scrambled Eggs A": { + "Canned Scrambled Eggs[A]": { "x": [7], "y": [17], "type": "decoration" }, - "Canned Fish A": { + "Canned Fish[A]": { "x": [7], "y": [18], "type": "decoration" }, - "Canned Meals A": { + "Canned Meals[A]": { "x": [7], "y": [19, 20], "type": "decoration" @@ -1701,7 +1701,7 @@ "y": [19], "type": "decoration" }, - "Hoisin Sauce B": { + "Hoisin Sauce[B]": { "x": [20], "y": [10], "type": "decoration" @@ -1711,37 +1711,37 @@ "y": [11, 12], "type": "decoration" }, - "Honey Sauce B": { + "Honey Sauce[B]": { "x": [20], "y": [13], "type": "decoration" }, - "Yeast B": { + "Yeast[B]": { "x": [20], "y": [14], "type": "decoration" }, - "Sugar Cones B": { + "Sugar Cones[B]": { "x": [20], "y": [15], "type": "decoration" }, - "Sugar Stars B": { + "Sugar Stars[B]": { "x": [20], "y": [16], "type": "decoration" }, - "Gummies B": { + "Gummies[B]": { "x": [20], "y": [17], "type": "decoration" }, - "Fruit Snacks B": { + "Fruit Snacks[B]": { "x": [20], "y": [18], "type": "decoration" }, - "Marinated Mushrooms B": { + "Marinated Mushrooms[B]": { "x": [20], "y": [19], "type": "decoration" @@ -1826,37 +1826,37 @@ "y": [19], "type": "decoration" }, - "Low Fat Beans B": { + "Low Fat Beans[B]": { "x": [28], "y": [11, 12], "type": "decoration" }, - "White Fungus Soda B": { + "White Fungus Soda[B]": { "x": [28], "y": [13], "type": "decoration" }, - "Powdered Breakfast B": { + "Powdered Breakfast[B]": { "x": [28], "y": [14, 15], "type": "decoration" }, - "Powdered Wine B": { + "Powdered Wine[B]": { "x": [28], "y": [16], "type": "decoration" }, - "Canned Scrambled Eggs B": { + "Canned Scrambled Eggs[B]": { "x": [28], "y": [17], "type": "decoration" }, - "Canned Fish B": { + "Canned Fish[B]": { "x": [28], "y": [18], "type": "decoration" }, - "Canned Meals B": { + "Canned Meals[B]": { "x": [28], "y": [19], "type": "decoration" From 9d1c6f95dd3a44efaa8d34dca6228d55c8f9dd2d Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sun, 23 Oct 2022 11:14:58 +0530 Subject: [PATCH 210/232] Issue#52 Patched speech bubbles --- stardew-access/Features/StaticTiles.cs | 2 +- stardew-access/Features/TileInfo.cs | 12 ++++++------ stardew-access/HarmonyPatches.cs | 5 +++++ stardew-access/Patches/DialoguePatches.cs | 16 +++++++++++++++- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/stardew-access/Features/StaticTiles.cs b/stardew-access/Features/StaticTiles.cs index c0bb806..4ce596d 100644 --- a/stardew-access/Features/StaticTiles.cs +++ b/stardew-access/Features/StaticTiles.cs @@ -45,7 +45,7 @@ namespace stardew_access.Features foreach (var location in data) { - if (!Game1.currentLocation.name.Value.ToLower().Equals(location.Key.ToLower())) + if (!Game1.currentLocation.Name.ToLower().Equals(location.Key.ToLower())) continue; if (location.Value != null) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 1e15d45..5f20015 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -478,7 +478,7 @@ namespace stardew_access.Features return (CATEGORY.Interactables, "Island Trader"); } } - else if (Game1.currentLocation.name.Value.ToLower().Equals("coop")) + else if (Game1.currentLocation.Name.ToLower().Equals("coop")) { if (x >= 6 && x <= 9 && y == 3) { @@ -489,7 +489,7 @@ namespace stardew_access.Features return (CATEGORY.Others, "Empty Feeding Bench"); } } - else if (Game1.currentLocation.name.Value.ToLower().Equals("big coop") || Game1.currentLocation.name.Value.ToLower().Equals("coop2")) + else if (Game1.currentLocation.Name.ToLower().Equals("big coop") || Game1.currentLocation.Name.ToLower().Equals("coop2")) { if (x >= 6 && x <= 13 && y == 3) { @@ -500,7 +500,7 @@ namespace stardew_access.Features return (CATEGORY.Others, "Empty Feeding Bench"); } } - else if (Game1.currentLocation.name.Value.ToLower().Equals("deluxe coop") || Game1.currentLocation.name.Value.ToLower().Equals("coop3")) + else if (Game1.currentLocation.Name.ToLower().Equals("deluxe coop") || Game1.currentLocation.Name.ToLower().Equals("coop3")) { if (x >= 6 && x <= 17 && y == 3) { @@ -511,7 +511,7 @@ namespace stardew_access.Features return (CATEGORY.Others, "Empty Feeding Bench"); } } - else if (Game1.currentLocation.name.Value.ToLower().Equals("barn")) + else if (Game1.currentLocation.Name.ToLower().Equals("barn")) { if (x >= 8 && x <= 11 && y == 3) { @@ -522,7 +522,7 @@ namespace stardew_access.Features return (CATEGORY.Others, "Empty Feeding Bench"); } } - else if (Game1.currentLocation.name.Value.ToLower().Equals("big barn") || Game1.currentLocation.name.Value.ToLower().Equals("barn2")) + else if (Game1.currentLocation.Name.ToLower().Equals("big barn") || Game1.currentLocation.Name.ToLower().Equals("barn2")) { if (x >= 8 && x <= 15 && y == 3) { @@ -533,7 +533,7 @@ namespace stardew_access.Features return (CATEGORY.Others, "Empty Feeding Bench"); } } - else if (Game1.currentLocation.name.Value.ToLower().Equals("deluxe barn") || Game1.currentLocation.name.Value.ToLower().Equals("barn3")) + else if (Game1.currentLocation.Name.ToLower().Equals("deluxe barn") || Game1.currentLocation.Name.ToLower().Equals("barn3")) { if (x >= 8 && x <= 19 && y == 3) { diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index ec74193..fbd1d92 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -28,6 +28,11 @@ namespace stardew_access 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)) ); + + harmony.Patch( + original: AccessTools.Method(typeof(NPC), nameof(NPC.drawAboveAlwaysFrontLayer)), + postfix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.drawAboveAlwaysFrontLayerPatch)) + ); #endregion #region Title Menu Patches diff --git a/stardew-access/Patches/DialoguePatches.cs b/stardew-access/Patches/DialoguePatches.cs index b59bbef..e131952 100644 --- a/stardew-access/Patches/DialoguePatches.cs +++ b/stardew-access/Patches/DialoguePatches.cs @@ -340,7 +340,6 @@ namespace stardew_access.Patches } catch (Exception e) { - MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); } } @@ -409,5 +408,20 @@ namespace stardew_access.Patches #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}"); + } + } } } From 950ab21d61ca1cc51edbcc8aef174ea6cabec036 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sun, 23 Oct 2022 12:00:30 +0530 Subject: [PATCH 211/232] Issue#56 Other currency narration --- stardew-access/Patches/GameMenuPatches.cs | 34 ++++++++++++++++++----- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/stardew-access/Patches/GameMenuPatches.cs b/stardew-access/Patches/GameMenuPatches.cs index f322daf..e1c2d46 100644 --- a/stardew-access/Patches/GameMenuPatches.cs +++ b/stardew-access/Patches/GameMenuPatches.cs @@ -917,7 +917,6 @@ namespace stardew_access.Patches MainClass.ScreenReader.Say(toSpeak, true); Game1.playSound("drop_item"); } - return; } if (__instance.organizeButton != null && __instance.organizeButton.containsPoint(x, y)) @@ -930,7 +929,6 @@ namespace stardew_access.Patches hoveredItemQueryKey = ""; MainClass.ScreenReader.Say(toSpeak, true); } - return; } if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y)) @@ -943,7 +941,6 @@ namespace stardew_access.Patches hoveredItemQueryKey = ""; MainClass.ScreenReader.Say(toSpeak, true); } - return; } if (__instance.organizeButton != null && __instance.organizeButton.containsPoint(x, y)) @@ -956,7 +953,6 @@ namespace stardew_access.Patches hoveredItemQueryKey = ""; MainClass.ScreenReader.Say(toSpeak, true); } - return; } if (__instance.junimoNoteIcon != null && __instance.junimoNoteIcon.containsPoint(x, y)) @@ -970,7 +966,6 @@ namespace stardew_access.Patches hoveredItemQueryKey = ""; MainClass.ScreenReader.Say(toSpeak, true); } - return; } #endregion @@ -1066,7 +1061,6 @@ namespace stardew_access.Patches hoveredItemQueryKey = ""; MainClass.ScreenReader.Say(toSpeak, true); } - return; } } #endregion @@ -1076,9 +1070,35 @@ namespace stardew_access.Patches { gameMenuQueryKey = ""; inventoryPageQueryKey = ""; - return; } #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) { From 7a4ca8983625fc0ebb13f54953d8f5bef89c1481 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sun, 23 Oct 2022 12:12:27 +0530 Subject: [PATCH 212/232] Issue#63 Prepend the player's name in tile viewer --- stardew-access/Features/TileViewer.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stardew-access/Features/TileViewer.cs b/stardew-access/Features/TileViewer.cs index 030610e..822d057 100644 --- a/stardew-access/Features/TileViewer.cs +++ b/stardew-access/Features/TileViewer.cs @@ -174,6 +174,13 @@ namespace stardew_access.Features Vector2 position = this.GetTileCursorPosition(); Vector2 tile = this.GetViewingTile(); String? name = TileInfo.getNameAtTile(tile); + + // Prepend the player's name if the viewing tile is occupied by the player itself + if (CurrentPlayer.PositionX == tile.X && CurrentPlayer.PositionY == tile.Y) + { + name = $"{Game1.player.displayName}, {name}"; + } + if (name == null) { // Report if a tile is empty or blocked if there is nothing on it From f2ba3e8793e594c187cc476733b78e51455e82d1 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Mon, 24 Oct 2022 12:12:53 +0530 Subject: [PATCH 213/232] Implemented Tolk library for screen reader in windows --- .gitignore | 2 + stardew-access/CustomCommands.cs | 120 +++++++++--------- stardew-access/ModEntry.cs | 9 ++ .../ScreenReader/ScreenReaderLinux.cs | 17 ++- .../ScreenReader/ScreenReaderWindows.cs | 58 ++++----- stardew-access/stardew-access.csproj | 2 +- 6 files changed, 114 insertions(+), 94 deletions(-) diff --git a/.gitignore b/.gitignore index 2c855eb..45acc76 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +*.dll + .vscode/* .git-old/ bin/ diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index df18be3..a3f748a 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -21,7 +21,7 @@ namespace stardew_access MainClass.Config.ReadTile = !MainClass.Config.ReadTile; helper.WriteConfig(MainClass.Config); - MainClass.DebugLog("Read Tile is " + (MainClass.Config.ReadTile ? "on" : "off")); + MainClass.InfoLog("Read Tile is " + (MainClass.Config.ReadTile ? "on" : "off")); }); helper.ConsoleCommands.Add("flooring", "Toggle flooring in read tile.", (string commmand, string[] args) => @@ -29,7 +29,7 @@ namespace stardew_access MainClass.Config.ReadFlooring = !MainClass.Config.ReadFlooring; helper.WriteConfig(MainClass.Config); - MainClass.DebugLog("Flooring is " + (MainClass.Config.ReadFlooring ? "on" : "off")); + MainClass.InfoLog("Flooring is " + (MainClass.Config.ReadFlooring ? "on" : "off")); }); helper.ConsoleCommands.Add("watered", "Toggle speaking watered or unwatered for crops.", (string commmand, string[] args) => @@ -37,7 +37,7 @@ namespace stardew_access MainClass.Config.WateredToggle = !MainClass.Config.WateredToggle; helper.WriteConfig(MainClass.Config); - MainClass.DebugLog("Watered toggle is " + (MainClass.Config.WateredToggle ? "on" : "off")); + MainClass.InfoLog("Watered toggle is " + (MainClass.Config.WateredToggle ? "on" : "off")); }); #endregion @@ -47,14 +47,14 @@ namespace stardew_access MainClass.Config.Radar = !MainClass.Config.Radar; helper.WriteConfig(MainClass.Config); - MainClass.DebugLog("Radar " + (MainClass.Config.Radar ? "on" : "off")); + MainClass.InfoLog("Radar " + (MainClass.Config.Radar ? "on" : "off")); }); helper.ConsoleCommands.Add("rdebug", "Toggle debugging in radar feature.", (string commmand, string[] args) => { MainClass.radarDebug = !MainClass.radarDebug; - MainClass.DebugLog("Radar debugging " + (MainClass.radarDebug ? "on" : "off")); + MainClass.InfoLog("Radar debugging " + (MainClass.radarDebug ? "on" : "off")); }); helper.ConsoleCommands.Add("rstereo", "Toggle stereo sound in radar feature.", (string commmand, string[] args) => @@ -62,14 +62,14 @@ namespace stardew_access MainClass.Config.RadarStereoSound = !MainClass.Config.RadarStereoSound; helper.WriteConfig(MainClass.Config); - MainClass.DebugLog("Stereo sound is " + (MainClass.Config.RadarStereoSound ? "on" : "off")); + MainClass.InfoLog("Stereo sound is " + (MainClass.Config.RadarStereoSound ? "on" : "off")); }); helper.ConsoleCommands.Add("rfocus", "Toggle focus mode in radar feature.", (string commmand, string[] args) => { bool focus = MainClass.RadarFeature.ToggleFocus(); - MainClass.DebugLog("Focus mode is " + (focus ? "on" : "off")); + MainClass.InfoLog("Focus mode is " + (focus ? "on" : "off")); }); helper.ConsoleCommands.Add("rdelay", "Set the delay of radar feature in milliseconds.", (string commmand, string[] args) => @@ -88,19 +88,19 @@ namespace stardew_access { MainClass.RadarFeature.delay = delay; if (delay >= 1000) - MainClass.DebugLog($"Delay set to {MainClass.RadarFeature.delay} milliseconds."); + MainClass.InfoLog($"Delay set to {MainClass.RadarFeature.delay} milliseconds."); else - MainClass.DebugLog($"Delay should be atleast 1 second or 1000 millisecond long."); + MainClass.InfoLog($"Delay should be atleast 1 second or 1000 millisecond long."); } else { - MainClass.DebugLog("Invalid delay amount, it can only be in numeric form."); + MainClass.InfoLog("Invalid delay amount, it can only be in numeric form."); } } else { - MainClass.DebugLog("Enter the delay amount (in milliseconds)!"); + MainClass.InfoLog("Enter the delay amount (in milliseconds)!"); } }); @@ -121,19 +121,19 @@ namespace stardew_access { MainClass.RadarFeature.range = range; if (range >= 2 && range <= 10) - MainClass.DebugLog($"Range set to {MainClass.RadarFeature.range}."); + MainClass.InfoLog($"Range set to {MainClass.RadarFeature.range}."); else - MainClass.DebugLog($"Range should be atleast 2 and maximum 10."); + MainClass.InfoLog($"Range should be atleast 2 and maximum 10."); } else { - MainClass.DebugLog("Invalid range amount, it can only be in numeric form."); + MainClass.InfoLog("Invalid range amount, it can only be in numeric form."); } } else { - MainClass.DebugLog("Enter the range amount!"); + MainClass.InfoLog("Enter the range amount!"); } }); @@ -152,16 +152,16 @@ namespace stardew_access if (!MainClass.RadarFeature.exclusions.Contains(keyToAdd)) { MainClass.RadarFeature.exclusions.Add(keyToAdd); - MainClass.DebugLog($"Added {keyToAdd} key to exclusions list."); + MainClass.InfoLog($"Added {keyToAdd} key to exclusions list."); } else { - MainClass.DebugLog($"{keyToAdd} key already present in the list."); + MainClass.InfoLog($"{keyToAdd} key already present in the list."); } } else { - MainClass.DebugLog("Unable to add the key to exclusions list."); + MainClass.InfoLog("Unable to add the key to exclusions list."); } }); @@ -177,16 +177,16 @@ namespace stardew_access if (MainClass.RadarFeature.exclusions.Contains(keyToAdd)) { MainClass.RadarFeature.exclusions.Remove(keyToAdd); - MainClass.DebugLog($"Removed {keyToAdd} key from exclusions list."); + MainClass.InfoLog($"Removed {keyToAdd} key from exclusions list."); } else { - MainClass.DebugLog($"Cannot find {keyToAdd} key in exclusions list."); + MainClass.InfoLog($"Cannot find {keyToAdd} key in exclusions list."); } } else { - MainClass.DebugLog("Unable to remove the key from exclusions list."); + MainClass.InfoLog("Unable to remove the key from exclusions list."); } }); @@ -199,23 +199,23 @@ namespace stardew_access { toPrint = $"{toPrint}\t{i + 1}: {MainClass.RadarFeature.exclusions[i]}"; } - MainClass.DebugLog(toPrint); + MainClass.InfoLog(toPrint); } else { - MainClass.DebugLog("No exclusions found."); + MainClass.InfoLog("No exclusions found."); } }); helper.ConsoleCommands.Add("reclear", "Clear the focus exclusions in the radar featrure.", (string commmand, string[] args) => { MainClass.RadarFeature.exclusions.Clear(); - MainClass.DebugLog($"Cleared the focus list in the exclusions feature."); + MainClass.InfoLog($"Cleared the focus list in the exclusions feature."); }); helper.ConsoleCommands.Add("recount", "Number of exclusions in the radar feature.", (string commmand, string[] args) => { - MainClass.DebugLog($"There are {MainClass.RadarFeature.exclusions.Count} exclusiond in the radar feature."); + MainClass.InfoLog($"There are {MainClass.RadarFeature.exclusions.Count} exclusiond in the radar feature."); }); #endregion @@ -232,16 +232,16 @@ namespace stardew_access if (!MainClass.RadarFeature.focus.Contains(keyToAdd)) { MainClass.RadarFeature.focus.Add(keyToAdd); - MainClass.DebugLog($"Added {keyToAdd} key to focus list."); + MainClass.InfoLog($"Added {keyToAdd} key to focus list."); } else { - MainClass.DebugLog($"{keyToAdd} key already present in the list."); + MainClass.InfoLog($"{keyToAdd} key already present in the list."); } } else { - MainClass.DebugLog("Unable to add the key to focus list."); + MainClass.InfoLog("Unable to add the key to focus list."); } }); @@ -257,16 +257,16 @@ namespace stardew_access if (MainClass.RadarFeature.focus.Contains(keyToAdd)) { MainClass.RadarFeature.focus.Remove(keyToAdd); - MainClass.DebugLog($"Removed {keyToAdd} key from focus list."); + MainClass.InfoLog($"Removed {keyToAdd} key from focus list."); } else { - MainClass.DebugLog($"Cannot find {keyToAdd} key in focus list."); + MainClass.InfoLog($"Cannot find {keyToAdd} key in focus list."); } } else { - MainClass.DebugLog("Unable to remove the key from focus list."); + MainClass.InfoLog("Unable to remove the key from focus list."); } }); @@ -279,23 +279,23 @@ namespace stardew_access { toPrint = $"{toPrint}\t{i + 1}): {MainClass.RadarFeature.focus[i]}"; } - MainClass.DebugLog(toPrint); + MainClass.InfoLog(toPrint); } else { - MainClass.DebugLog("No objects found in the focus list."); + MainClass.InfoLog("No objects found in the focus list."); } }); helper.ConsoleCommands.Add("rfclear", "Clear the focus list in the radar featrure.", (string commmand, string[] args) => { MainClass.RadarFeature.focus.Clear(); - MainClass.DebugLog($"Cleared the focus list in the radar feature."); + MainClass.InfoLog($"Cleared the focus list in the radar feature."); }); helper.ConsoleCommands.Add("rfcount", "Number of list in the radar feature.", (string commmand, string[] args) => { - MainClass.DebugLog($"There are {MainClass.RadarFeature.focus.Count} objects in the focus list in the radar feature."); + MainClass.InfoLog($"There are {MainClass.RadarFeature.focus.Count} objects in the focus list in the radar feature."); }); #endregion @@ -306,14 +306,14 @@ namespace stardew_access { if (Game1.currentLocation is not Farm) { - MainClass.DebugLog("Can only use this command in the farm"); + MainClass.InfoLog("Can only use this command in the farm"); return; } string? indexInString = args.ElementAtOrDefault(0); if (indexInString == null) { - MainClass.DebugLog("Enter the index too!"); + MainClass.InfoLog("Enter the index too!"); return; } @@ -322,12 +322,12 @@ namespace stardew_access if (!isParsable || !(index >= 0 && index <= 9)) { - MainClass.DebugLog("Index can only be a number and from 0 to 9 only"); + MainClass.InfoLog("Index can only be a number and from 0 to 9 only"); return; } BuildingNAnimalMenuPatches.marked[index] = new Vector2((int)Game1.player.getTileX(), (int)Game1.player.getTileY()); - MainClass.DebugLog($"Location {(int)Game1.player.getTileX()}x {(int)Game1.player.getTileY()}y added at {index} index."); + 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) => @@ -342,16 +342,16 @@ namespace stardew_access } if (toPrint == "") - MainClass.DebugLog("No positions marked!"); + MainClass.InfoLog("No positions marked!"); else - MainClass.DebugLog($"Marked positions:{toPrint}\nOpen command menu and use pageup and pagedown to check the list"); + MainClass.InfoLog($"Marked positions:{toPrint}\nOpen command menu and use pageup and pagedown to check the list"); }); helper.ConsoleCommands.Add("buildlist", "List all buildings for selection for upgrading/demolishing/painting", (string commmand, string[] args) => { if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu && Game1.activeClickableMenu is not AnimalQueryMenu) || !BuildingNAnimalMenuPatches.isOnFarm) { - MainClass.DebugLog($"Cannot list buildings."); + MainClass.InfoLog($"Cannot list buildings."); return; } @@ -372,11 +372,11 @@ namespace stardew_access if (toPrint == "") { - MainClass.DebugLog("No appropriate buildings to list"); + MainClass.InfoLog("No appropriate buildings to list"); } else { - MainClass.DebugLog($"Available buildings:{toPrint}\nOpen command menu and use pageup and pagedown to check the list"); + MainClass.InfoLog($"Available buildings:{toPrint}\nOpen command menu and use pageup and pagedown to check the list"); } }); @@ -384,14 +384,14 @@ namespace stardew_access { if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu && Game1.activeClickableMenu is not AnimalQueryMenu) || !BuildingNAnimalMenuPatches.isOnFarm) { - MainClass.DebugLog($"Cannot select building."); + MainClass.InfoLog($"Cannot select building."); return; } string? indexInString = args.ElementAtOrDefault(0); if (indexInString == null) { - MainClass.DebugLog("Enter the index of the building too! Use buildlist"); + MainClass.InfoLog("Enter the index of the building too! Use buildlist"); return; } @@ -400,7 +400,7 @@ namespace stardew_access if (!isParsable) { - MainClass.DebugLog("Index can only be a number."); + MainClass.InfoLog("Index can only be a number."); return; } @@ -414,13 +414,13 @@ namespace stardew_access { if (BuildingNAnimalMenuPatches.availableBuildings[index] == null) { - MainClass.DebugLog($"No building found with index {index}. Use buildlist."); + MainClass.InfoLog($"No building found with index {index}. Use buildlist."); return; } if (positionIndexInString == null) { - MainClass.DebugLog("Enter the index of marked place too! Use marklist."); + MainClass.InfoLog("Enter the index of marked place too! Use marklist."); return; } @@ -428,7 +428,7 @@ namespace stardew_access if (!isParsable) { - MainClass.DebugLog("Index can only be a number."); + MainClass.InfoLog("Index can only be a number."); return; } } @@ -437,7 +437,7 @@ namespace stardew_access { if (BuildingNAnimalMenuPatches.marked[index] == Vector2.Zero) { - MainClass.DebugLog($"No marked position found at {index} index."); + MainClass.InfoLog($"No marked position found at {index} index."); return; } } @@ -445,7 +445,7 @@ namespace stardew_access { if (BuildingNAnimalMenuPatches.availableBuildings[index] == null) { - MainClass.DebugLog($"No building found with index {index}. Use buildlist."); + MainClass.InfoLog($"No building found with index {index}. Use buildlist."); return; } } @@ -471,7 +471,7 @@ namespace stardew_access if (response != null) { - MainClass.DebugLog(response); + MainClass.InfoLog(response); } }); #endregion @@ -481,21 +481,21 @@ namespace stardew_access { MainClass.ScreenReader.InitializeScreenReader(); - MainClass.DebugLog("Screen Reader refreshed!"); + MainClass.InfoLog("Screen Reader refreshed!"); }); helper.ConsoleCommands.Add("refmc", "Refresh mod config", (string commmand, string[] args) => { MainClass.Config = helper.ReadConfig(); - MainClass.DebugLog("Mod Config refreshed!"); + MainClass.InfoLog("Mod Config refreshed!"); }); helper.ConsoleCommands.Add("refst", "Refresh static tiles", (string commmand, string[] args) => { MainClass.STiles = new Features.StaticTiles(); - MainClass.DebugLog("Static tiles refreshed!"); + MainClass.InfoLog("Static tiles refreshed!"); }); helper.ConsoleCommands.Add("hnspercent", "Toggle between speaking in percentage or full health and stamina.", (string commmand, string[] args) => @@ -503,7 +503,7 @@ namespace stardew_access MainClass.Config.HealthNStaminaInPercentage = !MainClass.Config.HealthNStaminaInPercentage; helper.WriteConfig(MainClass.Config); - MainClass.DebugLog("Speaking in percentage is " + (MainClass.Config.HealthNStaminaInPercentage ? "on" : "off")); + MainClass.InfoLog("Speaking in percentage is " + (MainClass.Config.HealthNStaminaInPercentage ? "on" : "off")); }); helper.ConsoleCommands.Add("snapmouse", "Toggle snap mouse feature.", (string commmand, string[] args) => @@ -511,7 +511,7 @@ namespace stardew_access MainClass.Config.SnapMouse = !MainClass.Config.SnapMouse; helper.WriteConfig(MainClass.Config); - MainClass.DebugLog("Snap Mouse is " + (MainClass.Config.SnapMouse ? "on" : "off")); + MainClass.InfoLog("Snap Mouse is " + (MainClass.Config.SnapMouse ? "on" : "off")); }); helper.ConsoleCommands.Add("warning", "Toggle warnings feature.", (string commmand, string[] args) => @@ -519,7 +519,7 @@ namespace stardew_access MainClass.Config.Warning = !MainClass.Config.Warning; helper.WriteConfig(MainClass.Config); - MainClass.DebugLog("Warnings is " + (MainClass.Config.Warning ? "on" : "off")); + MainClass.InfoLog("Warnings is " + (MainClass.Config.Warning ? "on" : "off")); }); helper.ConsoleCommands.Add("tts", "Toggles the screen reader/tts", (string commmand, string[] args) => @@ -527,7 +527,7 @@ namespace stardew_access MainClass.Config.TTS = !MainClass.Config.TTS; helper.WriteConfig(MainClass.Config); - MainClass.DebugLog("TTS is " + (MainClass.Config.TTS ? "on" : "off")); + MainClass.InfoLog("TTS is " + (MainClass.Config.TTS ? "on" : "off")); }); #endregion } diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 03cdc36..05858f1 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -114,6 +114,7 @@ namespace stardew_access Game1.options.setGamepadMode("force_on"); ScreenReader = new ScreenReaderController().Initialize(); + ScreenReader.Say("Initializing Stardew Access", true); CustomSoundEffects.Initialize(); @@ -346,6 +347,14 @@ namespace stardew_access monitor.Log(message, LogLevel.Error); } + public static void InfoLog(string message) + { + if (monitor == null) + return; + + monitor.Log(message, LogLevel.Info); + } + public static void DebugLog(string message) { if (monitor == null) diff --git a/stardew-access/ScreenReader/ScreenReaderLinux.cs b/stardew-access/ScreenReader/ScreenReaderLinux.cs index 7e58c69..03f5afd 100644 --- a/stardew-access/ScreenReader/ScreenReaderLinux.cs +++ b/stardew-access/ScreenReader/ScreenReaderLinux.cs @@ -41,10 +41,16 @@ namespace stardew_access.ScreenReader public void InitializeScreenReader() { + MainClass.InfoLog("Initializing speech dispatcher..."); int res = Initialize(); if (res == 1) { initialized = true; + MainClass.InfoLog("Successfully initialized."); + } + else + { + MainClass.ErrorLog("Unable to initialize."); } } @@ -71,7 +77,16 @@ namespace stardew_access.ScreenReader if (text.Contains('^')) text = text.Replace('^', '\n'); GoString str = new GoString(text, text.Length); - Speak(str, interrupt); + int re = Speak(str, interrupt); + + if (re == 1) + { + MainClass.DebugLog($"Speaking(interrupt: {interrupt}) = {text}"); + } + else + { + MainClass.ErrorLog($"Failed to output text: {text}"); + } } public void SayWithChecker(string text, bool interrupt) diff --git a/stardew-access/ScreenReader/ScreenReaderWindows.cs b/stardew-access/ScreenReader/ScreenReaderWindows.cs index f2da4b5..e4075b5 100644 --- a/stardew-access/ScreenReader/ScreenReaderWindows.cs +++ b/stardew-access/ScreenReader/ScreenReaderWindows.cs @@ -1,10 +1,10 @@ -using AccessibleOutput; +using DavyKager; namespace stardew_access.ScreenReader { public class ScreenReaderWindows : IScreenReader { - public IAccessibleOutput? screenReader = null; + private bool isLoaded = false; public string prevText = "", prevTextTile = " ", prevChatText = "", prevMenuText = ""; public string PrevTextTile @@ -15,43 +15,30 @@ namespace stardew_access.ScreenReader public void InitializeScreenReader() { + MainClass.InfoLog("Initializing Tolk..."); + Tolk.Load(); - NvdaOutput? nvdaOutput = null; - JawsOutput? jawsOutput = null; - SapiOutput? sapiOutput = null; - - // Initialize NVDA - try + MainClass.InfoLog("Querying for the active screen reader driver..."); + string name = Tolk.DetectScreenReader(); + if (name != null) { - nvdaOutput = new NvdaOutput(); + MainClass.InfoLog($"The active screen reader driver is: {name}"); + isLoaded = true; } - catch (Exception) { } - - // Initialize JAWS - try + else { - jawsOutput = new JawsOutput(); + MainClass.ErrorLog("None of the supported screen readers is running"); + isLoaded = false; } - catch (Exception) { } - - // Initialize SAPI - try - { - sapiOutput = new SapiOutput(); - } - catch (Exception) { } - - if (nvdaOutput != null && nvdaOutput.IsAvailable()) - screenReader = nvdaOutput; - else if (jawsOutput != null && jawsOutput.IsAvailable()) - screenReader = jawsOutput; - else if (sapiOutput != null && sapiOutput.IsAvailable()) - screenReader = sapiOutput; } public void CloseScreenReader() { - + if (isLoaded) + { + Tolk.Unload(); + isLoaded = false; + } } public void Say(string text, bool interrupt) @@ -59,7 +46,7 @@ namespace stardew_access.ScreenReader if (text == null) return; - if (screenReader == null) + if (!isLoaded) return; if (!MainClass.Config.TTS) @@ -67,7 +54,14 @@ namespace stardew_access.ScreenReader if (text.Contains('^')) text = text.Replace('^', '\n'); - screenReader.Speak(text, interrupt); + if (Tolk.Output("Hello, World!")) + { + MainClass.DebugLog($"Speaking(interrupt: {interrupt}) = {text}"); + } + else + { + MainClass.ErrorLog($"Failed to output text: {text}"); + } } public void SayWithChecker(string text, bool interrupt) diff --git a/stardew-access/stardew-access.csproj b/stardew-access/stardew-access.csproj index b3ab566..719f9a1 100644 --- a/stardew-access/stardew-access.csproj +++ b/stardew-access/stardew-access.csproj @@ -12,9 +12,9 @@ - + From 1ea07533bbc3e297a07304c7d69dbfc46aac627a Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Thu, 27 Oct 2022 13:12:41 +0530 Subject: [PATCH 214/232] Fixed minor code warnings --- stardew-access/Features/TileInfo.cs | 2 +- stardew-access/ModEntry.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 1ac2250..afb8b1f 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -770,7 +770,7 @@ namespace stardew_access.Features } else if (obj is IndoorPot indoorPot) { - toReturn.name = $"{obj.DisplayName}, {getHoeDirtDetail(indoorPot.hoeDirt, true)}"; + toReturn.name = $"{obj.DisplayName}, {getHoeDirtDetail(indoorPot.hoeDirt.Value, true)}"; } else if (obj is Sign sign) { diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index 05858f1..a6683e5 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -13,6 +13,7 @@ namespace stardew_access public class MainClass : Mod { #region Global Vars & Properties +#pragma warning disable CS8603 private static ModConfig? config; private Harmony? harmony; private static IMonitor? monitor; @@ -96,6 +97,7 @@ namespace stardew_access return warnings; } } +#pragma warning restore CS8603 #endregion /********* From 9d9b5f5c760f50295eb924257ddb1b3bcdc1035f Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Thu, 27 Oct 2022 14:55:38 +0530 Subject: [PATCH 215/232] Added books and museum pieces in LibraryMuseum --- stardew-access/Features/TileInfo.cs | 52 +++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index afb8b1f..0194b8f 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -544,6 +544,58 @@ namespace stardew_access.Features return (CATEGORY.Others, "Empty Feeding Bench"); } } + else if (Game1.currentLocation is LibraryMuseum libraryMuseum) + { + foreach (KeyValuePair pair in libraryMuseum.museumPieces.Pairs) + { + if (pair.Key.X == x && pair.Key.Y == y) + { + string displayName = Game1.objectInformation[pair.Value].Split('/')[0]; + return (CATEGORY.Interactables, $"{displayName} showcase"); + } + } + + int booksFound = Game1.netWorldState.Value.LostBooksFound.Value; + for (int x1 = 0; x1 < libraryMuseum.map.Layers[0].LayerWidth; x1++) + { + for (int y1 = 0; y1 < libraryMuseum.map.Layers[0].LayerHeight; y1++) + { + if (x != x1 && y != y1) continue; + + if (libraryMuseum.doesTileHaveProperty(x1, y1, "Action", "Buildings") != null && libraryMuseum.doesTileHaveProperty(x1, y1, "Action", "Buildings").Contains("Notes")) + { + int key = Convert.ToInt32(libraryMuseum.doesTileHaveProperty(x1, y1, "Action", "Buildings").Split(' ')[1]); + xTile.Tiles.Tile tile = libraryMuseum.map.GetLayer("Buildings").PickTile(new xTile.Dimensions.Location(x * 64, y * 64), Game1.viewport.Size); + string? action = null; + try + { + tile.Properties.TryGetValue("Action", out xTile.ObjectModel.PropertyValue? value); + if (value != null) action = value.ToString(); + } + catch (System.Exception) + { + MainClass.ErrorLog($"Cannot get action value at x:{x} y:{y} in LibraryMuseum"); + } + + if (action != null) + { + string[] actionParams = action.Split(' '); + int which = Convert.ToInt32(actionParams[1]); + + if (booksFound >= which) + { + string message = Game1.content.LoadString("Strings\\Notes:" + which); + return (CATEGORY.Interactables, $"{message.Split('\n')[0]} Book"); + } + else + { + return (CATEGORY.Others, $"Lost Book"); + } + } + } + } + } + } return (null, null); } From dccbf8b79faf6295207eaa649b36d9adeedadb5b Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Thu, 27 Oct 2022 15:14:37 +0530 Subject: [PATCH 216/232] Issue#45 Update buildlist on start of day - also removed calling restrictions from buildlist command, it can now be called from any location --- stardew-access/CustomCommands.cs | 58 ++++++++++++++++---------------- stardew-access/ModEntry.cs | 12 +++++++ 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/stardew-access/CustomCommands.cs b/stardew-access/CustomCommands.cs index a3f748a..557d7bf 100644 --- a/stardew-access/CustomCommands.cs +++ b/stardew-access/CustomCommands.cs @@ -11,6 +11,7 @@ namespace stardew_access { internal static void Initialize() { + //TODO organise this, create separate method for all commands IModHelper? helper = MainClass.ModHelper; if (helper == null) return; @@ -349,35 +350,7 @@ namespace stardew_access helper.ConsoleCommands.Add("buildlist", "List all buildings for selection for upgrading/demolishing/painting", (string commmand, string[] args) => { - if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu && Game1.activeClickableMenu is not AnimalQueryMenu) || !BuildingNAnimalMenuPatches.isOnFarm) - { - MainClass.InfoLog($"Cannot list buildings."); - return; - } - - string toPrint = ""; - Farm farm = (Farm)Game1.getLocationFromName("Farm"); - Netcode.NetCollection buildings = farm.buildings; - int buildingIndex = 0; - - for (int i = 0; i < buildings.Count; i++) - { - string? name = buildings[i].nameOfIndoorsWithoutUnique; - name = (name == "null") ? buildings[i].buildingType.Value : name; - - BuildingNAnimalMenuPatches.availableBuildings[buildingIndex] = buildings[i]; - toPrint = $"{toPrint}\nIndex {buildingIndex}: {name}: At {buildings[i].tileX}x and {buildings[i].tileY}y"; - ++buildingIndex; - } - - if (toPrint == "") - { - MainClass.InfoLog("No appropriate buildings to list"); - } - else - { - MainClass.InfoLog($"Available buildings:{toPrint}\nOpen command menu and use pageup and pagedown to check the list"); - } + onBuildListCalled(); }); helper.ConsoleCommands.Add("buildsel", "Select the building index which you want to upgrade/demolish/paint", (string commmand, string[] args) => @@ -531,6 +504,33 @@ namespace stardew_access }); #endregion } + + internal static void onBuildListCalled() + { + string toPrint = ""; + Farm farm = (Farm)Game1.getLocationFromName("Farm"); + Netcode.NetCollection buildings = farm.buildings; + int buildingIndex = 0; + + for (int i = 0; i < buildings.Count; i++) + { + string? name = buildings[i].nameOfIndoorsWithoutUnique; + name = (name == "null") ? buildings[i].buildingType.Value : name; + + BuildingNAnimalMenuPatches.availableBuildings[buildingIndex] = buildings[i]; + toPrint = $"{toPrint}\nIndex {buildingIndex}: {name}: At {buildings[i].tileX}x and {buildings[i].tileY}y"; + ++buildingIndex; + } + + if (toPrint == "") + { + MainClass.InfoLog("No appropriate buildings to list"); + } + else + { + MainClass.InfoLog($"Available buildings:{toPrint}\nOpen command menu and use pageup and pagedown to check the list"); + } + } } } diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index a6683e5..a7009f9 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -14,6 +14,7 @@ namespace stardew_access { #region Global Vars & Properties #pragma warning disable CS8603 + private static int prevDate = -99; private static ModConfig? config; private Harmony? harmony; private static IMonitor? monitor; @@ -27,6 +28,7 @@ namespace stardew_access internal static ModConfig Config { get => config; set => config = value; } public static IModHelper? ModHelper { get => modHelper; } + public static StaticTiles STiles { get @@ -190,6 +192,16 @@ namespace stardew_access Other.narrateHudMessages(); Task.Delay(300).ContinueWith(_ => { isNarratingHudMessage = false; }); } + + if (Game1.player != null) + { + if (Game1.timeOfDay >= 600 && prevDate != CurrentPlayer.Date) + { + prevDate = CurrentPlayer.Date; + DebugLog("Refreshing buildlist..."); + CustomCommands.onBuildListCalled(); + } + } } private void OnButtonPressed(object? sender, ButtonPressedEventArgs? e) From 1187ba30e7103449b5c672fc594ac39499c2229a Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Thu, 27 Oct 2022 19:25:24 +0530 Subject: [PATCH 217/232] Mod Specific Tiles We can add tiles that only get detected when the specified mod is loaded Syntax: || --- stardew-access/Features/StaticTiles.cs | 42 +++++++++++++++++++++++-- stardew-access/assets/static-tiles.json | 2 +- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/stardew-access/Features/StaticTiles.cs b/stardew-access/Features/StaticTiles.cs index 4ce596d..b522145 100644 --- a/stardew-access/Features/StaticTiles.cs +++ b/stardew-access/Features/StaticTiles.cs @@ -26,7 +26,16 @@ namespace stardew_access.Features foreach (var location in data) { - if (locationName.ToLower().Equals(location.Key.ToLower())) + if (location.Key.Contains("||") && MainClass.ModHelper != null) + { + string uniqueModID = location.Key.Substring(location.Key.LastIndexOf("||") + 2); + string locationNameInJson = location.Key.Remove(location.Key.LastIndexOf("||")); + bool isLoaded = MainClass.ModHelper.ModRegistry.IsLoaded(uniqueModID); + + if (!isLoaded) continue; // Skip if the specified mod is not loaded + if (locationName.ToLower().Equals(locationNameInJson.ToLower())) return true; + } + else if (locationName.ToLower().Equals(location.Key.ToLower())) return true; } @@ -45,8 +54,35 @@ namespace stardew_access.Features foreach (var location in data) { - if (!Game1.currentLocation.Name.ToLower().Equals(location.Key.ToLower())) - continue; + if (location.Key.Contains("||") && MainClass.ModHelper != null) + { + // Mod Specific Tiles + // We can add tiles that only get detected when the specified mod is loaded. + // Syntax: || + // Example: THe following tile will only be detected if Stardew Valley Expanded mod is installed + // { + // . + // . + // . + // "Town||FlashShifter.StardewValleyExpandedCP":{ + // "":{ + // "x": [], + // "y": [], + // "type": "" + // } + // }, + // . + // . + // . + // } + string uniqueModID = location.Key.Substring(location.Key.LastIndexOf("||") + 2); + string locationName = location.Key.Remove(location.Key.LastIndexOf("||")); + bool isLoaded = MainClass.ModHelper.ModRegistry.IsLoaded(uniqueModID); + + if (!isLoaded) continue; // Skip if the specified mod is not loaded + if (!Game1.currentLocation.Name.ToLower().Equals(locationName.ToLower())) continue; + } + else if (!Game1.currentLocation.Name.ToLower().Equals(location.Key.ToLower())) continue; if (location.Value != null) foreach (var tile in ((JObject)location.Value)) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index cbc2549..99789aa 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -2995,4 +2995,4 @@ "type": "interactable" } } -} +} \ No newline at end of file From b4560bc9e183c2896962be31b0088ae391fbe217 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Thu, 27 Oct 2022 20:05:12 +0530 Subject: [PATCH 218/232] Added custom-tiles.json support - users can add their own tiles in this file just asin static-tiles.json - the file needs to be placed just where the static-tiles.json is --- .gitignore | 1 + stardew-access/Features/StaticTiles.cs | 210 ++++++++++++++----------- 2 files changed, 123 insertions(+), 88 deletions(-) diff --git a/.gitignore b/.gitignore index 45acc76..cf5e8fc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore *.dll +stardew-access/assets/custom-tiles.json .vscode/* .git-old/ diff --git a/stardew-access/Features/StaticTiles.cs b/stardew-access/Features/StaticTiles.cs index b522145..bcedc1b 100644 --- a/stardew-access/Features/StaticTiles.cs +++ b/stardew-access/Features/StaticTiles.cs @@ -5,38 +5,68 @@ namespace stardew_access.Features { public class StaticTiles { - private JObject? data = null; + private JObject? staticTilesData = null; + private JObject? customTilesData = null; public StaticTiles() { if (MainClass.ModHelper == null) return; - using (StreamReader file = new StreamReader(Path.Combine(MainClass.ModHelper.DirectoryPath, "assets", "static-tiles.json"))) + try { - string json = file.ReadToEnd(); - data = JObject.Parse(json); + using (StreamReader file = new StreamReader(Path.Combine(MainClass.ModHelper.DirectoryPath, "assets", "static-tiles.json"))) + { + string json = file.ReadToEnd(); + staticTilesData = JObject.Parse(json); + } + + MainClass.InfoLog($"Loaded static-tile.json"); + } + catch (System.Exception) + { + MainClass.ErrorLog($"static-tiles.json file not found or an error occured while initializing static-tiles.json\nThe path of the file should be:\n\t{Path.Combine(MainClass.ModHelper.DirectoryPath, "assets", "static-tiles.json")}"); + } + + try + { + using (StreamReader file = new StreamReader(Path.Combine(MainClass.ModHelper.DirectoryPath, "assets", "custom-tiles.json"))) + { + string json = file.ReadToEnd(); + customTilesData = JObject.Parse(json); + } + + MainClass.InfoLog($"Loaded custom-tile.json"); + } + catch (System.Exception) + { + MainClass.ErrorLog($"custom-tiles.json file not found or an error occured while initializing custom-tiles.json\nThe path of the file should be:\n\t{Path.Combine(MainClass.ModHelper.DirectoryPath, "assets", "custom-tiles.json")}"); } } public bool isAvailable(string locationName) { - if (data == null) - return false; + List allData = new List(); - foreach (var location in data) + if (staticTilesData != null) allData.Add(staticTilesData); + if (customTilesData != null) allData.Add(customTilesData); + + foreach (JObject data in allData) { - if (location.Key.Contains("||") && MainClass.ModHelper != null) + foreach (KeyValuePair location in data) { - string uniqueModID = location.Key.Substring(location.Key.LastIndexOf("||") + 2); - string locationNameInJson = location.Key.Remove(location.Key.LastIndexOf("||")); - bool isLoaded = MainClass.ModHelper.ModRegistry.IsLoaded(uniqueModID); + if (location.Key.Contains("||") && MainClass.ModHelper != null) + { + string uniqueModID = location.Key.Substring(location.Key.LastIndexOf("||") + 2); + string locationNameInJson = location.Key.Remove(location.Key.LastIndexOf("||")); + bool isLoaded = MainClass.ModHelper.ModRegistry.IsLoaded(uniqueModID); - if (!isLoaded) continue; // Skip if the specified mod is not loaded - if (locationName.ToLower().Equals(locationNameInJson.ToLower())) return true; + if (!isLoaded) continue; // Skip if the specified mod is not loaded + if (locationName.ToLower().Equals(locationNameInJson.ToLower())) return true; + } + else if (locationName.ToLower().Equals(location.Key.ToLower())) + return true; } - else if (locationName.ToLower().Equals(location.Key.ToLower())) - return true; } return false; @@ -49,94 +79,98 @@ namespace stardew_access.Features public (string? name, CATEGORY category) getStaticTileInfoAtWithCategory(int x, int y) { - if (data == null) - return (null, CATEGORY.Others); + List allData = new List(); - foreach (var location in data) + if (staticTilesData != null) allData.Add(staticTilesData); + if (customTilesData != null) allData.Add(customTilesData); + + foreach (JObject data in allData) { - if (location.Key.Contains("||") && MainClass.ModHelper != null) + foreach (KeyValuePair location in data) { - // Mod Specific Tiles - // We can add tiles that only get detected when the specified mod is loaded. - // Syntax: || - // Example: THe following tile will only be detected if Stardew Valley Expanded mod is installed - // { - // . - // . - // . - // "Town||FlashShifter.StardewValleyExpandedCP":{ - // "":{ - // "x": [], - // "y": [], - // "type": "" - // } - // }, - // . - // . - // . - // } - string uniqueModID = location.Key.Substring(location.Key.LastIndexOf("||") + 2); - string locationName = location.Key.Remove(location.Key.LastIndexOf("||")); - bool isLoaded = MainClass.ModHelper.ModRegistry.IsLoaded(uniqueModID); - - if (!isLoaded) continue; // Skip if the specified mod is not loaded - if (!Game1.currentLocation.Name.ToLower().Equals(locationName.ToLower())) continue; - } - else if (!Game1.currentLocation.Name.ToLower().Equals(location.Key.ToLower())) continue; - - if (location.Value != null) - foreach (var tile in ((JObject)location.Value)) + if (location.Key.Contains("||") && MainClass.ModHelper != null) { - if (tile.Value == null) - continue; + // Mod Specific Tiles + // We can add tiles that only get detected when the specified mod is loaded. + // Syntax: || + // Example: THe following tile will only be detected if Stardew Valley Expanded mod is installed + // { + // . + // . + // . + // "Town||FlashShifter.StardewValleyExpandedCP":{ + // "":{ + // "x": [], + // "y": [], + // "type": "" + // } + // }, + // . + // . + // . + // } + string uniqueModID = location.Key.Substring(location.Key.LastIndexOf("||") + 2); + string locationName = location.Key.Remove(location.Key.LastIndexOf("||")); + bool isLoaded = MainClass.ModHelper.ModRegistry.IsLoaded(uniqueModID); - JToken? tileXArray = tile.Value["x"]; - JToken? tileYArray = tile.Value["y"]; - JToken? tileType = tile.Value["type"]; + if (!isLoaded) continue; // Skip if the specified mod is not loaded + if (!Game1.currentLocation.Name.ToLower().Equals(locationName.ToLower())) continue; + } + else if (!Game1.currentLocation.Name.ToLower().Equals(location.Key.ToLower())) continue; - if (tileXArray == null || tileYArray == null || tileType == null) - continue; - - bool isXPresent = false; - bool isYPresent = false; - - foreach (var item in tileXArray) + if (location.Value != null) + foreach (var tile in ((JObject)location.Value)) { - if (short.Parse(item.ToString()) == x) - { - isXPresent = true; - break; - } - } + if (tile.Value == null) + continue; - foreach (var item in tileYArray) - { - if (short.Parse(item.ToString()) == y) - { - isYPresent = true; - break; - } - } + JToken? tileXArray = tile.Value["x"]; + JToken? tileYArray = tile.Value["y"]; + JToken? tileType = tile.Value["type"]; - if (isXPresent && isYPresent) - { - string key = tile.Key; - if (key.Contains('[') && key.Contains(']')) - { - int i1 = key.IndexOf('['); - int i2 = key.LastIndexOf(']'); + if (tileXArray == null || tileYArray == null || tileType == null) + continue; - if (i1 < i2) + bool isXPresent = false; + bool isYPresent = false; + + foreach (var item in tileXArray) + { + if (short.Parse(item.ToString()) == x) { - key = key.Remove(i1, ++i2 - i1); + isXPresent = true; + break; } } - return (key.Trim(), CATEGORY.FromString(tileType.ToString().ToLower())); - } - } - } + foreach (var item in tileYArray) + { + if (short.Parse(item.ToString()) == y) + { + isYPresent = true; + break; + } + } + if (isXPresent && isYPresent) + { + string key = tile.Key; + if (key.Contains('[') && key.Contains(']')) + { + int i1 = key.IndexOf('['); + int i2 = key.LastIndexOf(']'); + + if (i1 < i2) + { + key = key.Remove(i1, ++i2 - i1); + } + } + + return (key.Trim(), CATEGORY.FromString(tileType.ToString().ToLower())); + } + } + } + } return (null, CATEGORY.Others); } } From e1a19ae0c8839aa2f346c51fe3b3e7cdd5f05990 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 29 Oct 2022 13:20:11 +0530 Subject: [PATCH 219/232] Bug fix --- stardew-access/ScreenReader/ScreenReaderWindows.cs | 2 +- stardew-access/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stardew-access/ScreenReader/ScreenReaderWindows.cs b/stardew-access/ScreenReader/ScreenReaderWindows.cs index e4075b5..33035ce 100644 --- a/stardew-access/ScreenReader/ScreenReaderWindows.cs +++ b/stardew-access/ScreenReader/ScreenReaderWindows.cs @@ -54,7 +54,7 @@ namespace stardew_access.ScreenReader if (text.Contains('^')) text = text.Replace('^', '\n'); - if (Tolk.Output("Hello, World!")) + if (Tolk.Output(text, interrupt)) { MainClass.DebugLog($"Speaking(interrupt: {interrupt}) = {text}"); } diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index 988b529..d638b17 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.3.2", + "Version": "1.3.3", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From ac2a384d84c750c5ab999c650f606f249a44cef0 Mon Sep 17 00:00:00 2001 From: Bartholomue Date: Tue, 25 Oct 2022 06:31:35 -0500 Subject: [PATCH 220/232] Dwarf Shop Etc Defined the location of the Dwarf shop on Volcano Dungeon 5. Defined two signs in town as decorations. Removed the top and bottom tiles of the Joja Mart shop counter so that Accessible Tiles auto pathing won't navigate to the south / north end of the counter, as those tiles will only let you interact with them from the right. --- stardew-access/assets/static-tiles.json | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 99789aa..9378d14 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -1323,7 +1323,7 @@ }, "Shop Counter": { "x": [10], - "y": [23, 24, 25, 26], + "y": [24, 25], "type": "interactable" }, "Salt": { @@ -2655,7 +2655,7 @@ "Sewer": { "x": [34, 35], "y": [95, 96], - "type": "interactable" + "type": "door" }, "Ice Cream Stand": { "x": [88], @@ -2667,6 +2667,16 @@ "y": [79], "type": "interactable" }, + "Clinic Sign": { + "x": [37], + "y": [56], + "type": "decoration" + }, + "General Store Sign": { + "x": [45], + "y": [56], + "type": "decoration" + }, "Faded Gravestone": { "x": [42], "y": [85], @@ -2898,6 +2908,13 @@ "type": "door" } }, + "volcanodungeon5": { + "Dwarf Shop": { + "x": [36], + "y": [30], + "type": "interactable" + } + }, "witchhut": { "Exit": { "x": [7], From c153462706653a26bb831dbf012f3826f8daccd7 Mon Sep 17 00:00:00 2001 From: Bartholomue Date: Wed, 26 Oct 2022 20:22:17 -0500 Subject: [PATCH 221/232] Desert Camel Defined the location of the camel in the desert in the NPC category. --- stardew-access/assets/static-tiles.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 9378d14..9a4f593 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -766,6 +766,11 @@ "y": [27], "type": "interactable" }, + "Camel": { + "x": [38], + "y": [24], + "type": "npc" + }, "Desert Trader": { "x": [42], "y": [23], From 8aac4499fc16fbbc6208825082511fc8962f9489 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 29 Oct 2022 14:41:41 +0530 Subject: [PATCH 222/232] Added dropped item detection --- stardew-access/Features/TileInfo.cs | 22 ++++++++++++++++++++++ stardew-access/Features/Utils.cs | 3 +++ 2 files changed, 25 insertions(+) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 0194b8f..b1833a0 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -1,4 +1,5 @@ using Microsoft.Xna.Framework; +using Netcode; using StardewValley; using StardewValley.Buildings; using StardewValley.Locations; @@ -148,6 +149,27 @@ namespace stardew_access.Features category = CATEGORY.JunimoBundle; } + #region Track dropped items + NetCollection droppedItems = Game1.currentLocation.debris; + if (droppedItems.Count() > 0) + { + foreach (var item in droppedItems) + { + int xPos = ((int)item.Chunks[0].position.Value.X / Game1.tileSize) + 1; + int yPos = ((int)item.Chunks[0].position.Value.Y / Game1.tileSize) + 1; + if (xPos != x || yPos != y) continue; + + string name = item.item.DisplayName; + int count = item.item.Stack; + + if (toReturn == "") + return ($"Dropped Item: {count} {name}", CATEGORY.DroppedItems); + else + toReturn = $"{toReturn}, Dropped Item: {count} {name}"; + } + } + #endregion + if (toReturn == "") return (null, category); diff --git a/stardew-access/Features/Utils.cs b/stardew-access/Features/Utils.cs index f8d6945..51a222a 100644 --- a/stardew-access/Features/Utils.cs +++ b/stardew-access/Features/Utils.cs @@ -59,6 +59,8 @@ namespace stardew_access.Features return CATEGORY.Machines; else if (name == "bridge") return CATEGORY.Bridges; + else if (name == "dropped item") + return CATEGORY.DroppedItems; else if (name == "other") return CATEGORY.Others; @@ -85,6 +87,7 @@ namespace stardew_access.Features public static CATEGORY Decor = new CATEGORY("decoration"); public static CATEGORY Machines = new CATEGORY("machine"); public static CATEGORY Bridges = new CATEGORY("bridge"); + public static CATEGORY DroppedItems = new CATEGORY("dropped item"); public static CATEGORY Others = new CATEGORY("other"); } From f5db6eb655e1338f13091ab6f14880fb151b98e5 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 29 Oct 2022 14:48:13 +0530 Subject: [PATCH 223/232] Renamed chest category to container --- stardew-access/Features/Radar.cs | 4 ++-- stardew-access/Features/TileInfo.cs | 4 ++-- stardew-access/Features/Utils.cs | 6 +++--- stardew-access/assets/static-tiles.json | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/stardew-access/Features/Radar.cs b/stardew-access/Features/Radar.cs index 2c3c050..3b4fded 100644 --- a/stardew-access/Features/Radar.cs +++ b/stardew-access/Features/Radar.cs @@ -57,7 +57,7 @@ namespace stardew_access.Features * exclusions.Add("building"); * exclusions.Add("resource clump"); * exclusions.Add("mine item"); - * exclusions.Add("chest"); + * exclusions.Add("container"); * exclusions.Add("bundle"); * exclusions.Add("door"); * exclusions.Add("machine"); @@ -362,7 +362,7 @@ namespace stardew_access.Features soundName = $"obj{soundName}"; else if (category == CATEGORY.MineItems) // Mine items soundName = $"obj{soundName}"; - else if (category == CATEGORY.Chests) // Chests + else if (category == CATEGORY.Containers) // Chests soundName = $"obj{soundName}"; else if (category == CATEGORY.Debris) // Grass and debris soundName = $"obj{soundName}"; diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index b1833a0..8856652 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -467,7 +467,7 @@ namespace stardew_access.Features else if (Game1.currentLocation is CommunityCenter communityCenter) { if (communityCenter.missedRewardsChestVisible.Value && x == 22 && y == 10) - return (CATEGORY.Chests, "Missed Rewards Chest"); + return (CATEGORY.Containers, "Missed Rewards Chest"); } else if (Game1.currentLocation is BoatTunnel) { @@ -840,7 +840,7 @@ namespace stardew_access.Features if (obj is Chest) { Chest chest = (Chest)obj; - toReturn = (chest.DisplayName, CATEGORY.Chests); + toReturn = (chest.DisplayName, CATEGORY.Containers); } else if (obj is IndoorPot indoorPot) { diff --git a/stardew-access/Features/Utils.cs b/stardew-access/Features/Utils.cs index 51a222a..a1b1297 100644 --- a/stardew-access/Features/Utils.cs +++ b/stardew-access/Features/Utils.cs @@ -43,8 +43,8 @@ namespace stardew_access.Features return CATEGORY.MineItems; else if (name == "resource clump") return CATEGORY.ResourceClumps; - else if (name == "chest") - return CATEGORY.Chests; + else if (name == "container") + return CATEGORY.Containers; else if (name == "bundle") return CATEGORY.JunimoBundle; else if (name == "door") @@ -79,7 +79,7 @@ namespace stardew_access.Features public static CATEGORY Buildings = new CATEGORY("building"); public static CATEGORY MineItems = new CATEGORY("mine item"); public static CATEGORY ResourceClumps = new CATEGORY("resource clump"); - public static CATEGORY Chests = new CATEGORY("chest"); + public static CATEGORY Containers = new CATEGORY("container"); public static CATEGORY JunimoBundle = new CATEGORY("bundle"); public static CATEGORY Doors = new CATEGORY("door"); // Also includes ladders and elevators public static CATEGORY WaterTiles = new CATEGORY("water"); diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 9a4f593..17b06dc 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -589,7 +589,7 @@ "Rare Chest": { "x": [25], "y": [28], - "type": "chest" + "type": "container" }, "Forge": { "x": [23], From 0f0824a4226709749dcac74d4e8423c9a9f391a6 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 29 Oct 2022 16:52:37 +0530 Subject: [PATCH 224/232] Prioritised custom-tiles.json over static-tiles - Bug fix in LibraryMuseum --- stardew-access/Features/StaticTiles.cs | 4 ++-- stardew-access/Features/TileInfo.cs | 22 ++++++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/stardew-access/Features/StaticTiles.cs b/stardew-access/Features/StaticTiles.cs index bcedc1b..7cc1a32 100644 --- a/stardew-access/Features/StaticTiles.cs +++ b/stardew-access/Features/StaticTiles.cs @@ -48,8 +48,8 @@ namespace stardew_access.Features { List allData = new List(); - if (staticTilesData != null) allData.Add(staticTilesData); if (customTilesData != null) allData.Add(customTilesData); + if (staticTilesData != null) allData.Add(staticTilesData); foreach (JObject data in allData) { @@ -81,8 +81,8 @@ namespace stardew_access.Features { List allData = new List(); - if (staticTilesData != null) allData.Add(staticTilesData); if (customTilesData != null) allData.Add(customTilesData); + if (staticTilesData != null) allData.Add(staticTilesData); foreach (JObject data in allData) { diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 8856652..4de8066 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -582,7 +582,7 @@ namespace stardew_access.Features { for (int y1 = 0; y1 < libraryMuseum.map.Layers[0].LayerHeight; y1++) { - if (x != x1 && y != y1) continue; + if (x != x1 || y != y1) continue; if (libraryMuseum.doesTileHaveProperty(x1, y1, "Action", "Buildings") != null && libraryMuseum.doesTileHaveProperty(x1, y1, "Action", "Buildings").Contains("Notes")) { @@ -594,25 +594,31 @@ namespace stardew_access.Features tile.Properties.TryGetValue("Action", out xTile.ObjectModel.PropertyValue? value); if (value != null) action = value.ToString(); } - catch (System.Exception) + catch (System.Exception e) { MainClass.ErrorLog($"Cannot get action value at x:{x} y:{y} in LibraryMuseum"); + MainClass.ErrorLog(e.Message); } if (action != null) { string[] actionParams = action.Split(' '); - int which = Convert.ToInt32(actionParams[1]); - if (booksFound >= which) + try { - string message = Game1.content.LoadString("Strings\\Notes:" + which); - return (CATEGORY.Interactables, $"{message.Split('\n')[0]} Book"); + int which = Convert.ToInt32(actionParams[1]); + if (booksFound >= which) + { + string message = Game1.content.LoadString("Strings\\Notes:" + which); + return (CATEGORY.Interactables, $"{message.Split('\n')[0]} Book"); + } } - else + catch (System.Exception e) { - return (CATEGORY.Others, $"Lost Book"); + MainClass.ErrorLog(e.Message); } + + return (CATEGORY.Others, $"Lost Book"); } } } From dd0d052f64f203e2211acd50123561790d44b492 Mon Sep 17 00:00:00 2001 From: Bartholomue Date: Sat, 29 Oct 2022 07:09:47 -0500 Subject: [PATCH 225/232] Static Tiles Container Migration Changed categorization of multiple objects to fit the new container category for ease of access to important items. --- stardew-access/assets/static-tiles.json | 44 ++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 17b06dc..c936f05 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -167,7 +167,7 @@ "Gunther's Box": { "x": [6], "y": [9], - "type": "other" + "type": "container" }, "Collection": { "x": [2], @@ -464,7 +464,7 @@ "Willy's Barrel": { "x": [37], "y": [33], - "type": "other" + "type": "container" } }, "beachnightmarket": { @@ -1936,7 +1936,7 @@ "Evelyn's Stove": { "x": [3], "y": [16], - "type": "other" + "type": "container" } }, "manorhouse": { @@ -2165,12 +2165,12 @@ "Empty Box": { "x": [45], "y": [40], - "type": "other" + "type": "container" }, "Dumpster": { "x": [28, 29], "y": [36], - "type": "other" + "type": "container" }, "Boulder": { "x": [11], @@ -2227,7 +2227,7 @@ "Gus's Fridge": { "x": [18], "y": [16], - "type": "other" + "type": "container" }, "Gus's Room Door": { "x": [20], @@ -2252,7 +2252,7 @@ "Lockbox": { "x": [28], "y": [7], - "type": "other" + "type": "container" }, "Barstool[1]": { "x": [7], @@ -2349,7 +2349,7 @@ "Toy Chest": { "x": [12], "y": [21], - "type": "other" + "type": "container" }, "Sam's Room Door": { "x": [12], @@ -2413,7 +2413,7 @@ "Robin's Wood Pile": { "x": [11], "y": [19], - "type": "other" + "type": "container" }, "Sebastian's Room Entrance": { "x": [12], @@ -2705,47 +2705,47 @@ "brown box": { "x": [100], "y": [66], - "type": "other" + "type": "container" }, "Joja Mart Trash": { "x": [110], "y": [56], - "type": "interactable" + "type": "container" }, "1 River Road Trash": { "x": [52], "y": [63], - "type": "interactable" + "type": "container" }, "Stardew Saloon Trash": { "x": [47], "y": [70], - "type": "interactable" + "type": "container" }, "Blacksmith Trash": { "x": [97], "y": [80], - "type": "interactable" + "type": "container" }, "1 Willow Lane Trash": { "x": [13], "y": [86], - "type": "interactable" + "type": "container" }, "Manor Trash": { "x": [56], "y": [86], - "type": "interactable" + "type": "container" }, "2 Willow Lane Trash": { "x": [19], "y": [89], - "type": "interactable" + "type": "container" }, "Museum Trash": { "x": [108], "y": [91], - "type": "interactable" + "type": "container" }, "Bus Stop Entrance": { "x": [0], @@ -2857,7 +2857,7 @@ "Pam's Kitchen": { "x": [10], "y": [6], - "type": "other" + "type": "container" } }, "trailer_big": { @@ -2869,7 +2869,7 @@ "Pam's Kitchen": { "x": [10], "y": [6], - "type": "other" + "type": "container" } }, "tunnel": { @@ -2878,10 +2878,10 @@ "y": [7, 8, 9, 10, 11], "type": "door" }, - "Safe Panel": { + "Safe": { "x": [17], "y": [6], - "type": "other" + "type": "container" } }, "undergroundmine77377": { From c4ce0a5280c0c0fa50307538bbea15b9ca7b6264 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Mon, 7 Nov 2022 19:51:35 +0530 Subject: [PATCH 226/232] Added config option to toggle detecting dropped items \n Fixed bug --- stardew-access/Features/TileInfo.cs | 29 +++++++++++++++++------------ stardew-access/ModConfig.cs | 2 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 4de8066..2232087 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -150,22 +150,27 @@ namespace stardew_access.Features } #region Track dropped items - NetCollection droppedItems = Game1.currentLocation.debris; - if (droppedItems.Count() > 0) + if (MainClass.Config.TrackDroppedItems) { - foreach (var item in droppedItems) + NetCollection droppedItems = Game1.currentLocation.debris; + if (droppedItems.Count() > 0) { - int xPos = ((int)item.Chunks[0].position.Value.X / Game1.tileSize) + 1; - int yPos = ((int)item.Chunks[0].position.Value.Y / Game1.tileSize) + 1; - if (xPos != x || yPos != y) continue; + foreach (var item in droppedItems) + { + int xPos = ((int)item.Chunks[0].position.Value.X / Game1.tileSize) + 1; + int yPos = ((int)item.Chunks[0].position.Value.Y / Game1.tileSize) + 1; + if (xPos != x || yPos != y) continue; - string name = item.item.DisplayName; - int count = item.item.Stack; + if (item.item == null) continue; - if (toReturn == "") - return ($"Dropped Item: {count} {name}", CATEGORY.DroppedItems); - else - toReturn = $"{toReturn}, Dropped Item: {count} {name}"; + string name = item.item.DisplayName; + int count = item.item.Stack; + + if (toReturn == "") + return ($"Dropped Item: {count} {name}", CATEGORY.DroppedItems); + else + toReturn = $"{toReturn}, Dropped Item: {count} {name}"; + } } } #endregion diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index f4ea1de..bce66e6 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -79,7 +79,7 @@ namespace stardew_access public Boolean SnapMouse { get; set; } = true; // Toggles the snap mouse feature public Boolean Warning { get; set; } = true; // Toggles the warnings feature public Boolean TTS { get; set; } = true; // Toggles the screen reader/tts. - // TODO add command to toggle warning feature + public Boolean TrackDroppedItems {get; set;} = true; // Toggles detecting the dropped items. #endregion // TODO Add the exclusion and focus list too From ec7148e6135bedfa1cae47403228da3e21100a87 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Mon, 7 Nov 2022 20:07:25 +0530 Subject: [PATCH 227/232] Added warp point detection --- stardew-access/Features/TileInfo.cs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 2232087..d8ed500 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -40,6 +40,7 @@ namespace stardew_access.Features bool isColliding = isCollidingAtTile(x, y); var terrainFeature = Game1.currentLocation.terrainFeatures.FieldDict; string? door = getDoorAtTile(x, y); + string? warp = getWarpPointAtTile(x, y); (CATEGORY? category, string? name) dynamicTile = getDynamicTilesInfo(x, y, lessInfo); string? junimoBundle = getJunimoBundleAt(x, y); string? resourceClump = getResourceClumpAtTile(x, y, lessInfo); @@ -113,6 +114,11 @@ namespace stardew_access.Features toReturn = bush; category = CATEGORY.Bush; } + else if (warp != null) + { + toReturn = warp; + category = CATEGORY.Doors; + } else if (door != null) { toReturn = door; @@ -1164,6 +1170,27 @@ namespace stardew_access.Features return false; } + public static string? getWarpPointAtTile(int x, int y) + { + try + { + if (Game1.currentLocation == null) return null; + + foreach (Warp warpPoint in Game1.currentLocation.warps) + { + if (warpPoint.X != x || warpPoint.Y != y) continue; + + return $"{warpPoint.TargetName} Entrance"; + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Error while detecting warp points.\n{e.Message}"); + } + + return null; + } + public static string? getDoorAtTile(int x, int y) { Point tilePoint = new Point(x, y); From fe03dfb46eaf3be0914814040aff0c84df925b70 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Mon, 7 Nov 2022 20:27:11 +0530 Subject: [PATCH 228/232] Removed exit and entrance entries --- stardew-access/Features/TileInfo.cs | 8 +- stardew-access/Patches/TitleMenuPatches.cs | 2 +- stardew-access/assets/static-tiles.json | 632 --------------------- 3 files changed, 3 insertions(+), 639 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index d8ed500..55c7029 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -432,9 +432,7 @@ namespace stardew_access.Features } else if (Game1.currentLocation is FarmHouse farmHouse) { - if (farmHouse.getEntryLocation().X == x && farmHouse.getEntryLocation().Y == y) - return (CATEGORY.Doors, "Exit"); - else if (farmHouse.upgradeLevel >= 1) + if (farmHouse.upgradeLevel >= 1) if (farmHouse.getKitchenStandingSpot().X == x && (farmHouse.getKitchenStandingSpot().Y - 1) == y) // Standing spot is where the player will stand return (CATEGORY.Interactables, "Kitchen"); else if (farmHouse.fridgePosition.X == x && farmHouse.fridgePosition.Y == y) @@ -442,9 +440,7 @@ namespace stardew_access.Features } else if (Game1.currentLocation is IslandFarmHouse islandFarmHouse) { - if (x == 14 && y == 17) - return (CATEGORY.Doors, "Exit"); - else if ((islandFarmHouse.fridgePosition.X - 1) == x && islandFarmHouse.fridgePosition.Y == y) + if ((islandFarmHouse.fridgePosition.X - 1) == x && islandFarmHouse.fridgePosition.Y == y) return (CATEGORY.Interactables, "Kitchen"); else if (islandFarmHouse.fridgePosition.X == x && islandFarmHouse.fridgePosition.Y == y) return (CATEGORY.Interactables, "Fridge"); diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index 3873cf3..ae17c55 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -228,7 +228,7 @@ namespace stardew_access.Patches !__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}"; + 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 diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index c936f05..c90a313 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -1,10 +1,5 @@ { "adventureguild": { - "Exit": { - "x": [6], - "y": [19], - "type": "door" - }, "Goals Board": { "x": [8], "y": [10], @@ -57,11 +52,6 @@ } }, "animalshop": { - "Exit": { - "x": [13], - "y": [19], - "type": "door" - }, "Shop Counter": { "x": [12, 13], "y": [15], @@ -159,11 +149,6 @@ } }, "archaeologyhouse": { - "Exit": { - "x": [3], - "y": [14], - "type": "door" - }, "Gunther's Box": { "x": [6], "y": [9], @@ -290,38 +275,11 @@ "type": "decoration" } }, - "backwoods": { - "Mountain Entrance": { - "x": [49], - "y": [14], - "type": "door" - }, - "Farm Entrance": { - "x": [14], - "y": [39], - "type": "door" - }, - "Bus stop Entrance": { - "x": [49], - "y": [28, 29, 30, 31, 32], - "type": "door" - }, - "Tunnel Entrance": { - "x": [23], - "y": [29, 30, 31], - "type": "door" - } - }, "barn": { "Hay Hopper": { "x": [6], "y": [3], "type": "interactable" - }, - "Exit": { - "x": [11], - "y": [14], - "type": "door" } }, "barn2": { @@ -329,11 +287,6 @@ "x": [6], "y": [3], "type": "interactable" - }, - "Exit": { - "x": [11], - "y": [14], - "type": "door" } }, "big barn": { @@ -341,11 +294,6 @@ "x": [6], "y": [3], "type": "interactable" - }, - "Exit": { - "x": [11], - "y": [14], - "type": "door" } }, "barn3": { @@ -353,11 +301,6 @@ "x": [6], "y": [3], "type": "interactable" - }, - "Exit": { - "x": [11], - "y": [14], - "type": "door" } }, "deluxe barn": { @@ -365,18 +308,6 @@ "x": [6], "y": [3], "type": "interactable" - }, - "Exit": { - "x": [11], - "y": [14], - "type": "door" - } - }, - "bathhouse_entry": { - "Exit": { - "x": [5], - "y": [9], - "type": "door" } }, "bathhouse_menslocker": { @@ -384,11 +315,6 @@ "x": [3], "y": [27], "type": "door" - }, - "Pool Entrance": { - "x": [15], - "y": [27], - "type": "door" } }, "bathhouse_pool": { @@ -443,19 +369,9 @@ "x": [13], "y": [27], "type": "door" - }, - "Pool Entrance": { - "x": [2], - "y": [27], - "type": "door" } }, "beach": { - "Town Entrance": { - "x": [38], - "y": [0], - "type": "door" - }, "Beach Warp Statue": { "x": [20], "y": [4], @@ -510,11 +426,6 @@ } }, "blacksmith": { - "Exit": { - "x": [5], - "y": [19], - "type": "door" - }, "Shop Counter": { "x": [3], "y": [14], @@ -551,13 +462,6 @@ "type": "decoration" } }, - "boattunnel": { - "Exit": { - "x": [6], - "y": [11], - "type": "door" - } - }, "busstop": { "Ticket Machine": { "x": [7], @@ -568,21 +472,6 @@ "x": [4, 5], "y": [3], "type": "interactable" - }, - "Farm Entrance": { - "x": [0], - "y": [23], - "type": "door" - }, - "Town Entrance": { - "x": [34], - "y": [23], - "type": "door" - }, - "Backwoods Entrance": { - "x": [0], - "y": [6, 7, 8, 9], - "type": "door" } }, "caldera": { @@ -595,30 +484,6 @@ "x": [23], "y": [21], "type": "interactable" - }, - "Volcano Dungeon 0 Entrance": { - "x": [11], - "y": [36], - "type": "door" - }, - "Volcano Dungeon 9 Entrance": { - "x": [21], - "y": [39], - "type": "door" - } - }, - "captainroom": { - "Exit": { - "x": [0], - "y": [5], - "type": "door" - } - }, - "cellar": { - "Exit": { - "x": [3], - "y": [2], - "type": "door" } }, "club": { @@ -666,18 +531,6 @@ "x": [3], "y": [4], "type": "interactable" - }, - "Exit": { - "x": [8], - "y": [12], - "type": "door" - } - }, - "communitycenter": { - "Exit": { - "x": [32], - "y": [23], - "type": "door" } }, "coop": { @@ -685,11 +538,6 @@ "x": [3], "y": [3], "type": "interactable" - }, - "Exit": { - "x": [2], - "y": [9], - "type": "door" } }, "coop2": { @@ -702,11 +550,6 @@ "x": [2], "y": [3], "type": "machine" - }, - "Exit": { - "x": [2], - "y": [9], - "type": "door" } }, "big coop": { @@ -719,11 +562,6 @@ "x": [2], "y": [3], "type": "machine" - }, - "Exit": { - "x": [2], - "y": [9], - "type": "door" } }, "coop3": { @@ -736,11 +574,6 @@ "x": [2], "y": [3], "type": "machine" - }, - "Exit": { - "x": [2], - "y": [9], - "type": "door" } }, "deluxe coop": { @@ -753,11 +586,6 @@ "x": [2], "y": [3], "type": "machine" - }, - "Exit": { - "x": [2], - "y": [9], - "type": "door" } }, "desert": { @@ -786,11 +614,6 @@ "y": [11], "type": "interactable" }, - "Skull Cavern Entrance": { - "x": [8], - "y": [6], - "type": "door" - }, "Desert Warp Statue": { "x": [35], "y": [43], @@ -808,11 +631,6 @@ } }, "elliotthouse": { - "Exit": { - "x": [3], - "y": [9], - "type": "door" - }, "Piano Key 1": { "x": [7], "y": [4], @@ -835,26 +653,6 @@ } }, "farm": { - "Bus Stop Entrance": { - "x": [79], - "y": [15, 16, 17, 18], - "type": "door" - }, - "Backwoods Entrance": { - "x": [40, 41], - "y": [0], - "type": "door" - }, - "Cindersap Forest Entrance": { - "x": [40, 41], - "y": [64], - "type": "door" - }, - "Farm Cave Entrance": { - "x": [34], - "y": [7], - "type": "door" - }, "Grandpa's Shrine": { "x": [8], "y": [7], @@ -866,36 +664,14 @@ "type": "decoration" } }, - "farmcave": { - "Exit": { - "x": [8], - "y": [11], - "type": "door" - } - }, "fishshop": { "Shop Counter": { "x": [4, 5, 6], "y": [5], "type": "interactable" - }, - "Exit": { - "x": [5], - "y": [9], - "type": "door" } }, "forest": { - "Farm Entrance": { - "x": [68], - "y": [0], - "type": "door" - }, - "Town Entrance": { - "x": [119], - "y": [25], - "type": "door" - }, "Bridge 1": { "x": [77, 82], "y": [49], @@ -928,11 +704,6 @@ } }, "greenhouse": { - "Exit": { - "x": [10], - "y": [23], - "type": "door" - }, "Water Trough": { "x": [9, 10], "y": [7], @@ -940,11 +711,6 @@ } }, "haleyhouse": { - "Exit": { - "x": [2], - "y": [24], - "type": "door" - }, "Sewing Machine": { "x": [12, 13], "y": [23], @@ -1007,11 +773,6 @@ } }, "harveyroom": { - "Exit": { - "x": [6], - "y": [12], - "type": "door" - }, "Fridge": { "x": [21], "y": [6], @@ -1039,21 +800,6 @@ } }, "hospital": { - "Exit": { - "x": [10], - "y": [19], - "type": "door" - }, - "Harvey's Room Entrance": { - "x": [10], - "y": [2], - "type": "door" - }, - "Harvey's Room Entrance Door": { - "x": [10], - "y": [5], - "type": "door" - }, "Main Area Door": { "x": [10], "y": [13], @@ -1075,29 +821,9 @@ "x": [28], "y": [27], "type": "interactable" - }, - "Island Hut Entrance": { - "x": [22], - "y": [10], - "type": "door" - }, - "Island South Entrance": { - "x": [0], - "y": [46], - "type": "door" - }, - "Island Shrine Entrance": { - "x": [32], - "y": [30], - "type": "door" } }, "islandfarmcave": { - "Exit": { - "x": [4], - "y": [10], - "type": "door" - }, "Gourmand Frog": { "x": [5], "y": [4], @@ -1105,11 +831,6 @@ } }, "islandfieldoffice": { - "Exit": { - "x": [4], - "y": [10], - "type": "door" - }, "Counter": { "x": [8], "y": [7], @@ -1121,49 +842,12 @@ "type": "interactable" } }, - "islandhut": { - "Exit": { - "x": [7], - "y": [13], - "type": "door" - } - }, "islandnorth": { - "Island South Entrance": { - "x": [35, 36, 37], - "y": [89], - "type": "door" - }, - "Island South Entrance 2": { - "x": [43, 44], - "y": [89], - "type": "door" - }, - "Island Field Office Entrance": { - "x": [46], - "y": [46], - "type": "door" - }, - "Island North Cave Entrance": { - "x": [21, 22], - "y": [47], - "type": "door" - }, "Dig Site Parrot Express": { "x": [5], "y": [48], "type": "interactable" }, - "Volcano Dungeon Entrance": { - "x": [39, 40, 41, 42], - "y": [21], - "type": "door" - }, - "Volcano Dungeon Entrance 2": { - "x": [12], - "y": [31], - "type": "door" - }, "Volcano Parrot Express": { "x": [60], "y": [16], @@ -1171,11 +855,6 @@ } }, "islandshrine": { - "Exit": { - "x": [13], - "y": [28], - "type": "door" - }, "Shrine": { "x": [24], "y": [22], @@ -1203,31 +882,11 @@ } }, "islandsouth": { - "Island East Entrance": { - "x": [34], - "y": [12], - "type": "door" - }, "Ginger Island Warp Statue": { "x": [11], "y": [11], "type": "decoration" }, - "Island West Entrance": { - "x": [0], - "y": [11], - "type": "door" - }, - "Island North Entrance": { - "x": [17, 18, 19], - "y": [0], - "type": "door" - }, - "Island North Entrance 2": { - "x": [27, 28], - "y": [0], - "type": "door" - }, "Docks Parrot Express": { "x": [6], "y": [31], @@ -1239,20 +898,6 @@ "type": "interactable" } }, - "islandsoutheast": { - "Island South East Cave Entrance": { - "x": [30], - "y": [18], - "type": "door" - } - }, - "islandsoutheastcave": { - "Exit": { - "x": [1], - "y": [8], - "type": "door" - } - }, "islandwest": { "Farm Parrot Express": { "x": [74], @@ -1303,24 +948,9 @@ "x": [77], "y": [39], "type": "door" - }, - "Island Farm Cave Entrance": { - "x": [96], - "y": [33], - "type": "door" - }, - "Island South Entrance": { - "x": [105], - "y": [40], - "type": "door" } }, "jojamart": { - "Exit": { - "x": [13], - "y": [29], - "type": "door" - }, "Morris's Kiosk": { "x": [21], "y": [25], @@ -1868,11 +1498,6 @@ } }, "joshhouse": { - "Exit": { - "x": [9], - "y": [24], - "type": "door" - }, "TV": { "x": [15, 16], "y": [20], @@ -1940,11 +1565,6 @@ } }, "manorhouse": { - "Exit": { - "x": [4], - "y": [11], - "type": "door" - }, "Town Ledger Book": { "x": [2], "y": [5], @@ -1977,11 +1597,6 @@ } }, "mermaidhouse": { - "Exit": { - "x": [4], - "y": [10], - "type": "door" - }, "Clam Shell 1": { "x": [2], "y": [6], @@ -2009,11 +1624,6 @@ } }, "mine": { - "Mountain Exit": { - "x": [18], - "y": [13], - "type": "door" - }, "Minecart": { "x": [11, 12], "y": [10], @@ -2023,19 +1633,9 @@ "x": [67], "y": [9], "type": "door" - }, - "Quarry Exit": { - "x": [18], - "y": [13], - "type": "door" } }, "mountain": { - "Mine Entrance": { - "x": [54], - "y": [5], - "type": "door" - }, "Mine Bridge": { "x": [47], "y": [7], @@ -2051,11 +1651,6 @@ "y": [11], "type": "interactable" }, - "Quarry Mine Entrance": { - "x": [103], - "y": [17], - "type": "door" - }, "Bridge 1": { "x": [57], "y": [30], @@ -2071,26 +1666,6 @@ "y": [20], "type": "decoration" }, - "Linus Tent Entrance": { - "x": [29], - "y": [7], - "type": "door" - }, - "Backwoods Entrance": { - "x": [0], - "y": [13], - "type": "door" - }, - "Town Entrance": { - "x": [15], - "y": [40], - "type": "door" - }, - "Railroad Entrance": { - "x": [9], - "y": [0], - "type": "door" - }, "Science House Secondary Door": { "x": [8], "y": [20], @@ -2098,11 +1673,6 @@ } }, "movietheater": { - "Exit": { - "x": [13], - "y": [15], - "type": "door" - }, "Concessions Counter": { "x": [7], "y": [6], @@ -2125,11 +1695,6 @@ } }, "qinutroom": { - "Exit": { - "x": [7], - "y": [7], - "type": "door" - }, "Perfection Tracker": { "x": [13], "y": [4], @@ -2147,21 +1712,6 @@ } }, "railroad": { - "Mountain Entrance": { - "x": [29], - "y": [61], - "type": "door" - }, - "Witch's Cave Entrance": { - "x": [54], - "y": [35], - "type": "door" - }, - "Summit Entrance": { - "x": [32, 33, 34], - "y": [0], - "type": "door" - }, "Empty Box": { "x": [45], "y": [40], @@ -2194,11 +1744,6 @@ } }, "saloon": { - "Exit": { - "x": [14], - "y": [24], - "type": "door" - }, "Shop Counter": { "x": [14], "y": [19], @@ -2331,11 +1876,6 @@ } }, "samhouse": { - "Exit": { - "x": [4], - "y": [23], - "type": "door" - }, "Radio": { "x": [6], "y": [12], @@ -2378,16 +1918,6 @@ } }, "sandyhouse": { - "Exit": { - "x": [4], - "y": [9], - "type": "door" - }, - "Club Entrance": { - "x": [17], - "y": [1], - "type": "door" - }, "Shop Counter": { "x": [2], "y": [6], @@ -2395,16 +1925,6 @@ } }, "sciencehouse": { - "Exit": { - "x": [6], - "y": [24], - "type": "door" - }, - "Secondary Exit": { - "x": [3], - "y": [8], - "type": "door" - }, "Shop Counter": { "x": [8], "y": [19], @@ -2415,11 +1935,6 @@ "y": [19], "type": "container" }, - "Sebastian's Room Entrance": { - "x": [12], - "y": [21], - "type": "door" - }, "Beaker Set": { "x": [17], "y": [17], @@ -2435,16 +1950,6 @@ "y": [20], "type": "decoration" }, - "Robin and Demetrius's Room Entrance": { - "x": [13], - "y": [10], - "type": "door" - }, - "Maru's Room Entrance": { - "x": [7], - "y": [10], - "type": "door" - }, "Bookshelf": { "x": [16, 17], "y": [4], @@ -2477,11 +1982,6 @@ } }, "sebastianroom": { - "Exit": { - "x": [1], - "y": [1], - "type": "door" - }, "Room Door": { "x": [1], "y": [3], @@ -2504,11 +2004,6 @@ } }, "seedshop": { - "Exit": { - "x": [6], - "y": [29], - "type": "door" - }, "Shop Counter": { "x": [4, 5], "y": [18], @@ -2567,39 +2062,6 @@ "type": "door" } }, - "shed": { - "Exit": { - "x": [6], - "y": [13], - "type": "door" - } - }, - "shed2": { - "Exit": { - "x": [9], - "y": [16], - "type": "door" - } - }, - "big shed": { - "Exit": { - "x": [9], - "y": [16], - "type": "door" - } - }, - "skullcave": { - "Exit": { - "x": [7], - "y": [9], - "type": "door" - }, - "Skull Cavern Entrance": { - "x": [3], - "y": [3], - "type": "door" - } - }, "slime hutch": { "Water Trough 1": { "x": [16], @@ -2620,32 +2082,15 @@ "x": [16], "y": [9], "type": "other" - }, - "Exit": { - "x": [8], - "y": [12], - "type": "door" } }, "submarine": { - "Exit": { - "x": [14], - "y": [15], - "type": "door" - }, "Captain": { "x": [2], "y": [9], "type": "npc" } }, - "tent": { - "Exit": { - "x": [2], - "y": [5], - "type": "door" - } - }, "town": { "Calender Board": { "x": [41], @@ -2747,26 +2192,6 @@ "y": [91], "type": "container" }, - "Bus Stop Entrance": { - "x": [0], - "y": [54], - "type": "door" - }, - "Cindersap Forest Entrance": { - "x": [0], - "y": [90], - "type": "door" - }, - "Beach Entrance": { - "x": [54], - "y": [109], - "type": "door" - }, - "Mountain Entrance": { - "x": [79, 80, 81, 82, 83], - "y": [0], - "type": "door" - }, "Joja Bridge": { "x": [92, 93, 94, 95, 96], "y": [11], @@ -2829,11 +2254,6 @@ } }, "trailer": { - "Exit": { - "x": [12], - "y": [9], - "type": "door" - }, "Penny's Room Door": { "x": [6], "y": [7], @@ -2861,11 +2281,6 @@ } }, "trailer_big": { - "Exit": { - "x": [13], - "y": [25], - "type": "door" - }, "Pam's Kitchen": { "x": [10], "y": [6], @@ -2873,11 +2288,6 @@ } }, "tunnel": { - "Exit": { - "x": [39], - "y": [7, 8, 9, 10, 11], - "type": "door" - }, "Safe": { "x": [17], "y": [6], @@ -2891,28 +2301,6 @@ "type": "interactable" } }, - "volcanodungeon0": { - "Island North Entrance 1": { - "x": [31], - "y": [54], - "type": "door" - }, - "Island North Entrance 2": { - "x": [6], - "y": [50], - "type": "door" - }, - "Caldera Entrance": { - "x": [44], - "y": [50], - "type": "door" - }, - "Volcano Dungeon 1 Entrance": { - "x": [37], - "y": [5], - "type": "door" - } - }, "volcanodungeon5": { "Dwarf Shop": { "x": [36], @@ -2921,11 +2309,6 @@ } }, "witchhut": { - "Exit": { - "x": [7], - "y": [15], - "type": "door" - }, "Wizard's Basement Rune": { "x": [11], "y": [11], @@ -2964,19 +2347,9 @@ "x": [4], "y": [5], "type": "door" - }, - "Railroad Entrance": { - "x": [4], - "y": [9], - "type": "door" } }, "wizardhouse": { - "Exit": { - "x": [8], - "y": [24], - "type": "door" - }, "Basement Door": { "x": [4], "y": [4], @@ -3006,11 +2379,6 @@ } }, "woods": { - "Forest Entrance": { - "x": [59], - "y": [15, 16, 17], - "type": "door" - }, "Old Master Cannoli": { "x": [8, 9], "y": [7], From 771ebb07e4d55273003caa22ed4c1e277fadebf1 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Mon, 7 Nov 2022 20:51:00 +0530 Subject: [PATCH 229/232] Added exception for warp points when checking if a tile is on map --- stardew-access/Features/Radar.cs | 2 +- stardew-access/Features/TileInfo.cs | 15 +++++++++++++++ stardew-access/Features/TileViewer.cs | 3 +++ stardew-access/manifest.json | 2 +- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/stardew-access/Features/Radar.cs b/stardew-access/Features/Radar.cs index 3b4fded..558da81 100644 --- a/stardew-access/Features/Radar.cs +++ b/stardew-access/Features/Radar.cs @@ -166,7 +166,7 @@ namespace stardew_access.Features { Vector2 dir = new Vector2(item.X + dirX[i], item.Y + dirY[i]); - if (!searched.Contains(dir) && Game1.currentLocation.isTileOnMap(dir)) + if (!searched.Contains(dir) && (TileInfo.isWarpPointAtTile((int)dir.X, (int)dir.Y) || Game1.currentLocation.isTileOnMap(dir))) { toSearch.Enqueue(dir); searched.Add(dir); diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 55c7029..51c78d6 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -279,6 +279,9 @@ namespace stardew_access.Features { Rectangle rect = new Rectangle(x * 64 + 1, y * 64 + 1, 62, 62); + // Check whether the position is a warp point, if so then return false, sometimes warp points are 1 tile off the map for example in coops and barns + if (isWarpPointAtTile(x, y)) return false; + if (Game1.currentLocation.isCollidingPosition(rect, Game1.viewport, true, 0, glider: false, Game1.player, pathfinding: true)) { return true; @@ -290,6 +293,18 @@ namespace stardew_access.Features return false; } + public static Boolean isWarpPointAtTile(int x, int y) + { + if (Game1.currentLocation == null) return false; + + foreach (Warp warpPoint in Game1.currentLocation.warps) + { + if (warpPoint.X == x && warpPoint.Y == y) return true; + } + + return false; + } + public static string? getFarmAnimalAt(GameLocation? location, int x, int y) { if (location == null) diff --git a/stardew-access/Features/TileViewer.cs b/stardew-access/Features/TileViewer.cs index 822d057..1c6ceaf 100644 --- a/stardew-access/Features/TileViewer.cs +++ b/stardew-access/Features/TileViewer.cs @@ -278,6 +278,9 @@ namespace stardew_access.Features private static bool isPositionOnMap(Vector2 position) { + // Check whether the position is a warp point, if so then return true, sometimes warp points are 1 tile off the map for example in coops and barns + if (TileInfo.isWarpPointAtTile((int)(position.X / Game1.tileSize), (int)(position.Y / Game1.tileSize))) return true; + //position does not take viewport into account since the entire map needs to be checked. Map map = Game1.currentLocation.map; if (position.X < 0 || position.X > map.Layers[0].DisplayWidth) return false; diff --git a/stardew-access/manifest.json b/stardew-access/manifest.json index d638b17..494cb2e 100644 --- a/stardew-access/manifest.json +++ b/stardew-access/manifest.json @@ -1,7 +1,7 @@ { "Name": "Stardew Access", "Author": "Mohammad Shoaib", - "Version": "1.3.3", + "Version": "1.3.4", "Description": "An accessibility mod with screen reader support!", "UniqueID": "shoaib.stardewaccess", "EntryDll": "stardew-access.dll", From d66fd0afb0a4d35eac5a73270fec2edf29276a53 Mon Sep 17 00:00:00 2001 From: Bartholomue Date: Tue, 8 Nov 2022 11:21:19 -0600 Subject: [PATCH 230/232] IslandWestCave1 Definitions Added definitions to IslandWestCave1 for the lion statue and note crystals so players can resolve that puzzle.Reverted the removal of the skull cavern door in 1.3.4, since that's an interaction tile used to enter the actual dungeon. --- stardew-access/assets/static-tiles.json | 39 +++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index c90a313..8e37f1e 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -950,6 +950,38 @@ "type": "door" } }, + "islandwestcave1": { + "Lion Statue": { + "x": [6], + "y": [2], + "type": "other" + }, + "Crystal 1": { + "x": [3], + "y": [4], + "type": "interactable" + }, + "Crystal 2": { + "x": [4], + "y": [6], + "type": "interactable" + }, + "Crystal 3": { + "x": [6], + "y": [7], + "type": "interactable" + }, + "Crystal 4": { + "x": [8], + "y": [6], + "type": "interactable" + }, + "Crystal 5": { + "x": [9], + "y": [4], + "type": "interactable" + } + }, "jojamart": { "Morris's Kiosk": { "x": [21], @@ -2062,6 +2094,13 @@ "type": "door" } }, + "skullcave": { + "Skull Cavern Door": { + "x": [3], + "y": [3], + "type": "door" + } + }, "slime hutch": { "Water Trough 1": { "x": [16], From 4751af8fec0529a8c56b1f4ac3d94eb7df00e3ce Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Tue, 8 Nov 2022 19:52:31 +0530 Subject: [PATCH 231/232] Fixed sapi --- stardew-access/Features/TileInfo.cs | 33 +++++++++++-------- .../ScreenReader/ScreenReaderWindows.cs | 1 + 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/stardew-access/Features/TileInfo.cs b/stardew-access/Features/TileInfo.cs index 51c78d6..8c1dfa6 100644 --- a/stardew-access/Features/TileInfo.cs +++ b/stardew-access/Features/TileInfo.cs @@ -158,26 +158,33 @@ namespace stardew_access.Features #region Track dropped items if (MainClass.Config.TrackDroppedItems) { - NetCollection droppedItems = Game1.currentLocation.debris; - if (droppedItems.Count() > 0) + try { - foreach (var item in droppedItems) + NetCollection droppedItems = Game1.currentLocation.debris; + if (droppedItems.Count() > 0) { - int xPos = ((int)item.Chunks[0].position.Value.X / Game1.tileSize) + 1; - int yPos = ((int)item.Chunks[0].position.Value.Y / Game1.tileSize) + 1; - if (xPos != x || yPos != y) continue; + foreach (var item in droppedItems) + { + int xPos = ((int)item.Chunks[0].position.Value.X / Game1.tileSize) + 1; + int yPos = ((int)item.Chunks[0].position.Value.Y / Game1.tileSize) + 1; + if (xPos != x || yPos != y) continue; - if (item.item == null) continue; + if (item.item == null) continue; - string name = item.item.DisplayName; - int count = item.item.Stack; + string name = item.item.DisplayName; + int count = item.item.Stack; - if (toReturn == "") - return ($"Dropped Item: {count} {name}", CATEGORY.DroppedItems); - else - toReturn = $"{toReturn}, Dropped Item: {count} {name}"; + if (toReturn == "") + return ($"Dropped Item: {count} {name}", CATEGORY.DroppedItems); + else + toReturn = $"{toReturn}, Dropped Item: {count} {name}"; + } } } + catch (Exception e) + { + MainClass.ErrorLog($"An error occured while detecting dropped items:\n{e.Message}"); + } } #endregion diff --git a/stardew-access/ScreenReader/ScreenReaderWindows.cs b/stardew-access/ScreenReader/ScreenReaderWindows.cs index 33035ce..d9b2dbc 100644 --- a/stardew-access/ScreenReader/ScreenReaderWindows.cs +++ b/stardew-access/ScreenReader/ScreenReaderWindows.cs @@ -16,6 +16,7 @@ namespace stardew_access.ScreenReader public void InitializeScreenReader() { MainClass.InfoLog("Initializing Tolk..."); + Tolk.TrySAPI(true); Tolk.Load(); MainClass.InfoLog("Querying for the active screen reader driver..."); From 6e5f9365f003b7edeb0f2d22c5da72151c5eb4d6 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Sat, 26 Nov 2022 15:42:56 +0530 Subject: [PATCH 232/232] Adding missing entries --- stardew-access/assets/static-tiles.json | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/stardew-access/assets/static-tiles.json b/stardew-access/assets/static-tiles.json index 8e37f1e..7ac8f23 100644 --- a/stardew-access/assets/static-tiles.json +++ b/stardew-access/assets/static-tiles.json @@ -315,6 +315,11 @@ "x": [3], "y": [27], "type": "door" + }, + "Pool Entrance": { + "x": [15], + "y": [27], + "type": "door" } }, "bathhouse_pool": { @@ -369,6 +374,11 @@ "x": [13], "y": [27], "type": "door" + }, + "Pool Entrance": { + "x": [2], + "y": [27], + "type": "door" } }, "beach": { @@ -800,6 +810,11 @@ } }, "hospital": { + "Harvey's Room Entrance Door": { + "x": [10], + "y": [5], + "type": "door" + }, "Main Area Door": { "x": [10], "y": [13], @@ -1962,6 +1977,21 @@ "y": [19], "type": "interactable" }, + "Sebastian's Room Entrance": { + "x": [12], + "y": [21], + "type": "door" + }, + "Robin and Demetrius's Room Entrance": { + "x": [13], + "y": [10], + "type": "door" + }, + "Maru's Room Entrance": { + "x": [7], + "y": [10], + "type": "door" + }, "Robin's Wood Pile": { "x": [11], "y": [19],