diff --git a/stardew-access/Game/CurrentPlayer.cs b/stardew-access/Game/CurrentPlayer.cs
index c5d563f..27b5848 100644
--- a/stardew-access/Game/CurrentPlayer.cs
+++ b/stardew-access/Game/CurrentPlayer.cs
@@ -5,7 +5,7 @@ namespace stardew_access.Game
{
internal class CurrentPlayer
{
- private static Farmer player = null;
+ private static Farmer? player = null;
CurrentPlayer()
{
@@ -40,5 +40,23 @@ namespace stardew_access.Game
return staminaPercentage;
}
+
+ public static int getPositionX()
+ {
+ if (player == null)
+ initPlayer();
+
+ int x = (int)player.getTileLocation().X;
+ return x;
+ }
+
+ public static int getPositionY()
+ {
+ if (player == null)
+ initPlayer();
+
+ int y = (int)player.getTileLocation().Y;
+ return y;
+ }
}
}
diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs
index e204d76..1e348fe 100644
--- a/stardew-access/ModEntry.cs
+++ b/stardew-access/ModEntry.cs
@@ -1,26 +1,20 @@
-using AccessibleOutput;
-using stardew_access.Game;
+using stardew_access.Game;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using HarmonyLib;
using StardewValley.Menus;
using Microsoft.Xna.Framework.Graphics;
-using System.Text;
-using StardewValley.BellsAndWhistles;
-using Microsoft.Xna.Framework;
+using stardew_access.Patches;
namespace stardew_access
{
- /// The mod entry point.
+
public class MainClass : Mod
{
- public static IAccessibleOutput screenReader;
- Harmony harmony;
- public static IMonitor monitor;
- private static string prevText = "";
- private static DialogueBox? dialogueBox = null;
- private int index = 0;
+ private Harmony? harmony;
+ public static IMonitor? monitor;
+
/*********
** Public methods
*********/
@@ -32,7 +26,7 @@ namespace stardew_access
monitor = Monitor;
// Initialize the screen reader
- initializeScreenReader();
+ ScreenReader.initializeScreenReader();
// Init harmony
harmony = new Harmony(ModManifest.UniqueID);
@@ -40,225 +34,29 @@ namespace stardew_access
// Add patches
harmony.Patch(
original: AccessTools.Method(typeof(DialogueBox), nameof(DialogueBox.draw), new Type[] {typeof(SpriteBatch)}),
- postfix: new HarmonyMethod(typeof(MainClass), nameof(MainClass.DialoguePatch))
+ postfix: new HarmonyMethod(typeof(DialoguePatch), nameof(DialoguePatch.CharachterDialoguePatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(IClickableMenu), nameof(IClickableMenu.drawHoverText), new Type[] { typeof(SpriteBatch), typeof(string), typeof(SpriteFont), typeof(int), typeof(int), typeof(int) , typeof(string) , typeof(int) , typeof(string[]) , typeof(Item) , typeof(int) , typeof(int), typeof(int), typeof(int), typeof(int), typeof(float), typeof(CraftingRecipe) , typeof(IList < Item >) }),
- postfix: new HarmonyMethod(typeof(MainClass), nameof(MainClass.HoverTextPatch))
+ postfix: new HarmonyMethod(typeof(DialoguePatch), nameof(DialoguePatch.HoverTextPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(TitleMenu), nameof(TitleMenu.draw) , new Type[] {typeof(SpriteBatch)}),
- postfix: new HarmonyMethod(typeof(MainClass), nameof(TitleMenuPatch))
+ postfix: new HarmonyMethod(typeof(MenuPatch), nameof(MenuPatch.TitleMenuPatch))
+ );
+
+ harmony.Patch(
+ original: AccessTools.Method(typeof(LoadGameMenu.SaveFileSlot), nameof(LoadGameMenu.SaveFileSlot.Draw), new Type[] { typeof(SpriteBatch), typeof(int) }),
+ postfix: new HarmonyMethod(typeof(MenuPatch), nameof(MenuPatch.LoadGameMenuPatch))
);
helper.Events.Input.ButtonPressed += this.OnButtonPressed;
- //helper.Events.GameLoop.OneSecondUpdateTicked += this.OnOneSecondUpdateTicked;
- }
-
- private static void TitleMenuPatch(TitleMenu __instance, SpriteBatch b)
- {
- try
- {
- __instance.allClickableComponents.ForEach(component =>
- {
- if(component.containsPoint(Game1.getMousePosition(true).X, Game1.getMousePosition(true).Y))
- {
- string name = component.name;
- string label = component.label;
- string toSpeak = $"{name} {label}";
-
- if(prevText != toSpeak)
- {
- prevText = toSpeak;
- screenReader.Speak(toSpeak, true);
- }
- }
- });
- }
- catch (Exception)
- {
- }
- }
-
- private static void DialoguePatch(DialogueBox __instance, SpriteBatch b)
- {
- try
- {
- Dialogue dialogue = __instance.characterDialogue;
- string speakerName = dialogue.speaker.Name;
- List dialogues = dialogue.dialogues;
- int dialogueIndex = dialogue.currentDialogueIndex;
- monitor.Log("" + dialogue.isCurrentStringContinuedOnNextScreen, LogLevel.Debug);
-
- if (prevText != $"{speakerName} said, {dialogues[dialogueIndex]}")
- {
- prevText = $"{speakerName} said, {dialogues[dialogueIndex]}";
- screenReader.Speak($"{speakerName} said, {dialogues[dialogueIndex]}", false);
- }
- }
- catch (Exception e)
- {
- monitor.Log($"Unable to narrate dialog:\n{e.StackTrace}", LogLevel.Error);
- }
-
- }
-
- private void OnOneSecondUpdateTicked(object sender, OneSecondUpdateTickedEventArgs e)
- {
- if (Game1.activeClickableMenu != null)
- {
- string name = Game1.activeClickableMenu.getCurrentlySnappedComponent().name;
- string label = Game1.activeClickableMenu.getCurrentlySnappedComponent().label;
- string toSpeeak = $"{name} {label}";
-
- if (prevText != toSpeeak)
- {
- prevText = toSpeeak;
- screenReader.Speak(toSpeeak, true);
- }
- }
- }
-
- private static void HoverTextPatch(SpriteBatch b, string text, SpriteFont font, int xOffset = 0, int yOffset = 0, int moneyAmountToDisplayAtBottom = -1, string boldTitleText = null, int healAmountToDisplay = -1, string[] buffIconsToDisplay = null, Item hoveredItem = null, int currencySymbol = 0, int extraItemToShowIndex = -1, int extraItemToShowAmount = -1, int overrideX = -1, int overrideY = -1, float alpha = 1f, CraftingRecipe craftingIngredients = null, IList- additional_craft_materials = null)
- {
- try
- {
- StringBuilder toSpeak = new StringBuilder();
-
- #region Add title if any
- if (boldTitleText != null)
- toSpeak.Append($"{boldTitleText}.\n");
- #endregion
-
- #region Add the base text
- toSpeak.Append(text);
- #endregion
-
- #region Add crafting ingredients
- if (craftingIngredients != null)
- {
-
- toSpeak.Append($"\n{craftingIngredients.description}");
- toSpeak.Append("\nIngredients\n");
-
- craftingIngredients.recipeList.ToList().ForEach(recipe =>
- {
- int count = recipe.Value;
- int item = recipe.Key;
- string name = craftingIngredients.getNameFromIndex(item);
-
- toSpeak.Append($" ,{count} {name}");
- });
- }
- #endregion
-
- #region Add health & stamina
- if (hoveredItem is StardewValley.Object && (hoveredItem as StardewValley.Object).Edibility != -300)
- {
- int stamina_recovery = (hoveredItem as StardewValley.Object).staminaRecoveredOnConsumption();
- toSpeak.Append($"{stamina_recovery} Energy\n");
- if (stamina_recovery >= 0)
- {
- int health_recovery = (hoveredItem as StardewValley.Object).healthRecoveredOnConsumption();
- toSpeak.Append($"{health_recovery} Health");
- }
- }
- #endregion
-
- #region Add buff items (effects like +1 walking speed)
- if (buffIconsToDisplay != null)
- {
- for (int i = 0; i < buffIconsToDisplay.Length; i++)
- {
- string buffName = ((Convert.ToInt32(buffIconsToDisplay[i]) > 0) ? "+" : "") + buffIconsToDisplay[i] + " ";
- if (i <= 11)
- {
- buffName = Game1.content.LoadString("Strings\\UI:ItemHover_Buff" + i, buffName);
- }
- try
- {
- int count = int.Parse(buffName.Substring(0, buffName.IndexOf(' ')));
- monitor.Log("" + count);
- if (count != 0)
- toSpeak.Append($"{buffName}\n");
- }
- catch (Exception) { }
- }
- }
- #endregion
-
- #region Add money
- if (moneyAmountToDisplayAtBottom != -1)
- toSpeak.Append($"\nValue: {moneyAmountToDisplayAtBottom} coins\n");
- #endregion
-
- #region Narrate toSpeak
- if (prevText != toSpeak.ToString())
- {
- prevText = toSpeak.ToString();
- screenReader.Speak(toSpeak.ToString(), true);
- }
- #endregion
- }
- catch (Exception e)
- {
- monitor.Log($"Unable to narrate dialog:\n{e.StackTrace}", LogLevel.Error);
- }
- }
-
- private void initializeScreenReader()
- {
- NvdaOutput nvdaOutput = null;
- JawsOutput jawsOutput = null;
- SapiOutput sapiOutput = null;
-
- // Initialize NVDA
- try{
- nvdaOutput = new NvdaOutput();
- }catch(Exception ex){
- Monitor.Log($"Error initializing NVDA:\n{ex.StackTrace}", LogLevel.Error);
- }
-
- // Initialize JAWS
- try
- {
- jawsOutput = new JawsOutput();
- }catch (Exception ex){
- Monitor.Log($"Error initializing JAWS:\n{ex.StackTrace}", LogLevel.Error);
- }
-
- // Initialize SAPI
- try
- {
- sapiOutput = new SapiOutput();
- }catch (Exception ex){
- Monitor.Log($"Error initializing SAPI:\n{ex.StackTrace}", LogLevel.Error);
- }
-
- if (nvdaOutput != null && nvdaOutput.IsAvailable())
- screenReader = nvdaOutput;
-
- if(jawsOutput != null && jawsOutput.IsAvailable())
- screenReader = jawsOutput;
-
- if (sapiOutput != null && sapiOutput.IsAvailable())
- screenReader = sapiOutput;
}
private void OnButtonPressed(object sender, ButtonPressedEventArgs e)
{
- if (Equals(e.Button, SButton.K))
- {
- if (Game1.activeClickableMenu != null)
- {
- string name = Game1.activeClickableMenu.getCurrentlySnappedComponent().label;
- screenReader.Speak(name, true);
- }
- }
-
-
// ignore if player hasn't loaded a save yet
if (!Context.IsWorldReady)
return;
@@ -266,7 +64,15 @@ namespace stardew_access
// Narrate Health And Energy
if (Equals(e.Button, SButton.I))
{
- screenReader.Speak($"Health is {CurrentPlayer.getHealth()} and Stamina is {CurrentPlayer.getStamina()}");
+ string toSpeak = $"Health is {CurrentPlayer.getHealth()} and Stamina is {CurrentPlayer.getStamina()}";
+ ScreenReader.say(toSpeak, true);
+ }
+
+ // Narrate Position
+ if (Equals(e.Button, SButton.K))
+ {
+ string toSpeak = $"X: {CurrentPlayer.getPositionX()} , Y: {CurrentPlayer.getPositionX()}";
+ ScreenReader.say(toSpeak, true);
}
}
diff --git a/stardew-access/Patches/DialoguePatch.cs b/stardew-access/Patches/DialoguePatch.cs
new file mode 100644
index 0000000..3ebe4aa
--- /dev/null
+++ b/stardew-access/Patches/DialoguePatch.cs
@@ -0,0 +1,115 @@
+using Microsoft.Xna.Framework.Graphics;
+using StardewModdingAPI;
+using StardewValley;
+using StardewValley.Menus;
+using System.Text;
+
+namespace stardew_access.Patches
+{
+ internal class DialoguePatch
+ {
+ public static void CharachterDialoguePatch(DialogueBox __instance, SpriteBatch b)
+ {
+ try
+ {
+ Dialogue dialogue = __instance.characterDialogue;
+ string speakerName = dialogue.speaker.Name;
+ List dialogues = dialogue.dialogues;
+ int dialogueIndex = dialogue.currentDialogueIndex;
+ MainClass.monitor.Log("" + dialogue.isCurrentStringContinuedOnNextScreen, LogLevel.Debug);
+ string toSpeak = $"{speakerName} said, {dialogues[dialogueIndex]}";
+
+ ScreenReader.sayWithChecker(toSpeak, false);
+ }
+ catch (Exception e)
+ {
+ MainClass.monitor.Log($"Unable to narrate dialog:\n{e.StackTrace}", LogLevel.Error);
+ }
+
+ }
+
+
+ public static void HoverTextPatch(string? text, int moneyAmountToDisplayAtBottom = -1, string? boldTitleText = null, string[]? buffIconsToDisplay = null, Item? hoveredItem = null, CraftingRecipe? craftingIngredients = null)
+ {
+ try
+ {
+ StringBuilder toSpeak = new StringBuilder();
+
+ #region Add title if any
+ if (boldTitleText != null)
+ toSpeak.Append($"{boldTitleText}.\n");
+ #endregion
+
+ #region Add the base text
+ toSpeak.Append(text);
+ #endregion
+
+ #region Add crafting ingredients
+ if (craftingIngredients != null)
+ {
+
+ toSpeak.Append($"\n{craftingIngredients.description}");
+ toSpeak.Append("\nIngredients\n");
+
+ craftingIngredients.recipeList.ToList().ForEach(recipe =>
+ {
+ int count = recipe.Value;
+ int item = recipe.Key;
+ string name = craftingIngredients.getNameFromIndex(item);
+
+ toSpeak.Append($" ,{count} {name}");
+ });
+ }
+ #endregion
+
+ #region Add health & stamina
+ if (hoveredItem is StardewValley.Object && (hoveredItem as StardewValley.Object).Edibility != -300)
+ {
+ int stamina_recovery = (hoveredItem as StardewValley.Object).staminaRecoveredOnConsumption();
+ toSpeak.Append($"{stamina_recovery} Energy\n");
+ if (stamina_recovery >= 0)
+ {
+ int health_recovery = (hoveredItem as StardewValley.Object).healthRecoveredOnConsumption();
+ toSpeak.Append($"{health_recovery} Health");
+ }
+ }
+ #endregion
+
+ #region Add buff items (effects like +1 walking speed)
+ if (buffIconsToDisplay != null)
+ {
+ for (int i = 0; i < buffIconsToDisplay.Length; i++)
+ {
+ string buffName = ((Convert.ToInt32(buffIconsToDisplay[i]) > 0) ? "+" : "") + buffIconsToDisplay[i] + " ";
+ if (i <= 11)
+ {
+ buffName = Game1.content.LoadString("Strings\\UI:ItemHover_Buff" + i, buffName);
+ }
+ try
+ {
+ int count = int.Parse(buffName.Substring(0, buffName.IndexOf(' ')));
+ MainClass.monitor.Log("" + count);
+ if (count != 0)
+ toSpeak.Append($"{buffName}\n");
+ }
+ catch (Exception) { }
+ }
+ }
+ #endregion
+
+ #region Add money
+ if (moneyAmountToDisplayAtBottom != -1)
+ toSpeak.Append($"\nValue: {moneyAmountToDisplayAtBottom} coins\n");
+ #endregion
+
+ #region Narrate toSpeak
+ ScreenReader.sayWithChecker(toSpeak.ToString(), true);
+ #endregion
+ }
+ catch (Exception e)
+ {
+ MainClass.monitor.Log($"Unable to narrate dialog:\n{e.StackTrace}", LogLevel.Error);
+ }
+ }
+ }
+}
diff --git a/stardew-access/Patches/MenuPatch.cs b/stardew-access/Patches/MenuPatch.cs
new file mode 100644
index 0000000..26f4937
--- /dev/null
+++ b/stardew-access/Patches/MenuPatch.cs
@@ -0,0 +1,91 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using StardewModdingAPI;
+using StardewValley;
+using StardewValley.Menus;
+
+namespace stardew_access.Patches
+{
+ internal class MenuPatch
+ {
+
+ public static void TitleMenuPatch(TitleMenu __instance)
+ {
+ try
+ {
+ string toSpeak = "";
+
+ __instance.buttons.ForEach(component =>
+ {
+ if (component.containsPoint(Game1.getMousePosition(true).X, Game1.getMousePosition(true).Y))
+ {
+ string name = component.name;
+ string label = component.label;
+ toSpeak = $"{name} {label} Button";
+ }
+ });
+
+ if(__instance.muteMusicButton.containsPoint(Game1.getMousePosition(true).X,Game1.getMousePosition(true).Y))
+ {
+ toSpeak = "Mute Music Button";
+ }
+
+ if (__instance.aboutButton.containsPoint(Game1.getMousePosition(true).X, Game1.getMousePosition(true).Y))
+ {
+ toSpeak = "About Button";
+ }
+
+ if (__instance.languageButton.containsPoint(Game1.getMousePosition(true).X, Game1.getMousePosition(true).Y))
+ {
+ toSpeak = "Language Button";
+ }
+
+ if (__instance.windowedButton.containsPoint(Game1.getMousePosition(true).X, Game1.getMousePosition(true).Y))
+ {
+ toSpeak = "Fullscreen toggle Button";
+ }
+
+ if (TitleMenu.subMenu != null && __instance.backButton.containsPoint(Game1.getMousePosition(true).X, Game1.getMousePosition(true).Y))
+ {
+ string text = "Back Button";
+ ScreenReader.sayWithChecker(text, true);
+ }
+
+ if (TitleMenu.subMenu == null && toSpeak != "")
+ ScreenReader.sayWithChecker(toSpeak, true);
+ }
+ catch (Exception e)
+ {
+ MainClass.monitor.Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
+ }
+ }
+
+ public static void LoadGameMenuPatch(LoadGameMenu.SaveFileSlot __instance, LoadGameMenu ___menu, int i)
+ {
+ try
+ {
+ if (___menu.slotButtons[i].containsPoint(Game1.getMousePosition(true).X, Game1.getMousePosition(true).Y))
+ {
+ if (__instance.Farmer == null)
+ return;
+
+ String farmerName = __instance.Farmer.Name;
+ String farmName = __instance.Farmer.farmName;
+ String money = __instance.Farmer.Money.ToString();
+ String hoursPlayed = Utility.getHoursMinutesStringFromMilliseconds(__instance.Farmer.millisecondsPlayed);
+ string dateStringForSaveGame = ((!__instance.Farmer.dayOfMonthForSaveGame.HasValue ||
+ !__instance.Farmer.seasonForSaveGame.HasValue ||
+ !__instance.Farmer.yearForSaveGame.HasValue) ? __instance.Farmer.dateStringForSaveGame : Utility.getDateStringFor(__instance.Farmer.dayOfMonthForSaveGame.Value, __instance.Farmer.seasonForSaveGame.Value, __instance.Farmer.yearForSaveGame.Value));
+
+ string toSpeak = $"{farmName} Farm, \t\n Farmer:{farmerName}, \t\nMoney:{money}, \t\nHours Played:{hoursPlayed}, \t\nDate:{dateStringForSaveGame}";
+
+ ScreenReader.sayWithChecker(toSpeak, true);
+ }
+ }
+ catch (Exception e)
+ {
+ MainClass.monitor.Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
+ }
+ }
+ }
+}
diff --git a/stardew-access/ScreenReader.cs b/stardew-access/ScreenReader.cs
new file mode 100644
index 0000000..8e53ff4
--- /dev/null
+++ b/stardew-access/ScreenReader.cs
@@ -0,0 +1,77 @@
+using AccessibleOutput;
+using StardewModdingAPI;
+
+namespace stardew_access
+{
+ internal class ScreenReader
+ {
+ public static IAccessibleOutput? screenReader = null;
+ private static string prevText = "";
+
+ public static void initializeScreenReader()
+ {
+ NvdaOutput? nvdaOutput = null;
+ JawsOutput? jawsOutput = null;
+ SapiOutput? sapiOutput = null;
+
+ // Initialize NVDA
+ try
+ {
+ nvdaOutput = new NvdaOutput();
+ }
+ catch (Exception ex)
+ {
+ MainClass.monitor.Log($"Error initializing NVDA:\n{ex.StackTrace}", LogLevel.Error);
+ }
+
+ // Initialize JAWS
+ try
+ {
+ jawsOutput = new JawsOutput();
+ }
+ catch (Exception ex)
+ {
+ MainClass.monitor.Log($"Error initializing JAWS:\n{ex.StackTrace}", LogLevel.Error);
+ }
+
+ // Initialize SAPI
+ try
+ {
+ sapiOutput = new SapiOutput();
+ }
+ catch (Exception ex)
+ {
+ MainClass.monitor.Log($"Error initializing SAPI:\n{ex.StackTrace}", LogLevel.Error);
+ }
+
+ if (nvdaOutput != null && nvdaOutput.IsAvailable())
+ screenReader = nvdaOutput;
+
+ if (jawsOutput != null && jawsOutput.IsAvailable())
+ screenReader = jawsOutput;
+
+ if (sapiOutput != null && sapiOutput.IsAvailable())
+ screenReader = sapiOutput;
+ }
+
+ public static void say(string text, bool interrupt)
+ {
+ if (screenReader == null)
+ return;
+
+ screenReader.Speak(text, interrupt);
+ }
+
+ public static void sayWithChecker(string text, bool interrupt)
+ {
+ if (screenReader == null)
+ return;
+
+ if (prevText != text)
+ {
+ prevText = text;
+ screenReader.Speak(text, interrupt);
+ }
+ }
+ }
+}