From 5996fa09f224cf93350c43f7b2a63c910e43ac79 Mon Sep 17 00:00:00 2001 From: Katie Durden Date: Mon, 30 Jan 2023 04:42:54 -0800 Subject: [PATCH 1/5] Update harmony and Stardew.ModConfig; add newtonsoft.json as dependency. --- stardew-access/stardew-access.csproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stardew-access/stardew-access.csproj b/stardew-access/stardew-access.csproj index 719f9a1..ab03cfc 100644 --- a/stardew-access/stardew-access.csproj +++ b/stardew-access/stardew-access.csproj @@ -12,8 +12,9 @@ - - + + + From fcf81004d3ad538f56feb855d2d9bece94edc33b Mon Sep 17 00:00:00 2001 From: Katie Durden Date: Mon, 30 Jan 2023 17:43:07 -0800 Subject: [PATCH 2/5] Typo: `isCustomizingChrachter` should be `isCustomizingCharacter`. Updated all occurrences. --- stardew-access/ModEntry.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stardew-access/ModEntry.cs b/stardew-access/ModEntry.cs index a7009f9..7049d76 100644 --- a/stardew-access/ModEntry.cs +++ b/stardew-access/ModEntry.cs @@ -212,7 +212,7 @@ namespace stardew_access #region Simulate left and right clicks if (Game1.activeClickableMenu != null) { - bool isCustomizingChrachter = Game1.activeClickableMenu is CharacterCustomization || (TitleMenu.subMenu != null && TitleMenu.subMenu is CharacterCustomization); + bool isCustomizingCharacter = Game1.activeClickableMenu is CharacterCustomization || (TitleMenu.subMenu != null && TitleMenu.subMenu is CharacterCustomization); #region Mouse Click Simulation // Main Keybinds @@ -226,11 +226,11 @@ namespace stardew_access } // Alternate Keybinds - if (!isCustomizingChrachter && Game1.activeClickableMenu is not AnimalQueryMenu && Config.LeftClickAlternateKey.JustPressed()) // Excluding the character creation menu + if (!isCustomizingCharacter && Game1.activeClickableMenu is not AnimalQueryMenu && Config.LeftClickAlternateKey.JustPressed()) // Excluding the character creation menu { Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true)); } - if (!isCustomizingChrachter && Game1.activeClickableMenu is not AnimalQueryMenu && Config.RightClickAlternateKey.JustPressed()) // Excluding the character creation menu + if (!isCustomizingCharacter && Game1.activeClickableMenu is not AnimalQueryMenu && Config.RightClickAlternateKey.JustPressed()) // Excluding the character creation menu { Game1.activeClickableMenu.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true)); } @@ -239,7 +239,7 @@ namespace stardew_access if (Game1.currentMinigame != null) { - bool isCustomizingChrachter = Game1.activeClickableMenu is CharacterCustomization || (TitleMenu.subMenu != null && TitleMenu.subMenu is CharacterCustomization); + bool isCustomizingCharacter = Game1.activeClickableMenu is CharacterCustomization || (TitleMenu.subMenu != null && TitleMenu.subMenu is CharacterCustomization); #region Mouse Click Simulation // Main Keybinds From 4c336c36ed756f630f37c1768cec7dcc114f2be3 Mon Sep 17 00:00:00 2001 From: Katie Durden Date: Mon, 30 Jan 2023 17:58:12 -0800 Subject: [PATCH 3/5] Enable access to the character appearance controls. Added accessible buttons to rotate the farmer, change skin tone, hair style, shirt, pants style, and accessory choice. Added new accessible slider controls to adjust hue, saturation and value for eye, hair and pants color. Up/Down to adjust 1%; PageUp/PageDown to adjust by 10%. Changed spoken modifiable fields (such as current pet, current shirt) to only announce when relevant controls have focus. This includes Random Skin Button for appearance controls. Added toggle key (LeftControl + Space) to show / hide the appearance controls; hidden by default. Added announcement when entering customization menu to inform of toggle key. --- stardew-access/ModConfig.cs | 7 +- stardew-access/Patches/TitleMenuPatches.cs | 598 ++++++++++++++++++++- 2 files changed, 591 insertions(+), 14 deletions(-) diff --git a/stardew-access/ModConfig.cs b/stardew-access/ModConfig.cs index bce66e6..d8530fd 100644 --- a/stardew-access/ModConfig.cs +++ b/stardew-access/ModConfig.cs @@ -49,9 +49,14 @@ namespace stardew_access #region Menu Keys public KeybindList PrimaryInfoKey { get; set; } = KeybindList.Parse("C"); - // Charachter Creatinon menu (new game menu) keys + // Character Creation menu (new game menu) keys public KeybindList CharacterCreationMenuNextKey { get; set; } = KeybindList.Parse("Right"); public KeybindList CharacterCreationMenuPreviousKey { get; set; } = KeybindList.Parse("Left"); + public KeybindList CharacterCreationMenuSliderIncreaseKey { get; set; } = KeybindList.Parse("Up"); + public KeybindList CharacterCreationMenuSliderLargeIncreaseKey { get; set; } = KeybindList.Parse("PageUp"); + public KeybindList CharacterCreationMenuSliderDecreaseKey { get; set; } = KeybindList.Parse("Down"); + public KeybindList CharacterCreationMenuSliderLargeDecreaseKey { get; set; } = KeybindList.Parse("PageDown"); + public KeybindList CharacterCreationMenuDesignToggleKey { get; set; } = KeybindList.Parse("LeftControl + Space"); // Bundle menu keys public KeybindList BundleMenuIngredientsKey { get; set; } = KeybindList.Parse("I"); diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index ae17c55..ea0903d 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -12,7 +12,27 @@ namespace stardew_access.Patches private static bool isRunning = false; public static string characterCreationMenuQueryKey = " "; public static string advancedGameOptionsQueryKey = " "; + public static string prevPants = " "; + public static string prevShirt = " "; + public static string prevHair = " "; + public static string prevAccessory = " "; + public static string prevSkin = " "; + public static string prevEyeColor = " "; + public static string prevEyeColorHue = " "; + public static string prevEyeColorSaturation = " "; + public static string prevEyeColorValue = " "; + public static string prevHairColor = " "; + public static string prevHairColorHue = " "; + public static string prevHairColorSaturation = " "; + public static string prevHairColorValue = " "; + public static string prevPantsColor = " "; + public static string prevPantsColorHue = " "; + public static string prevPantsColorSaturation = " "; + public static string prevPantsColorValue = " "; public static string prevPetName = " "; + public static bool characterDesignToggle = false; + public static bool characterDesignToggleShouldSpeak = true; + public static ClickableComponent? currentComponent = null; internal static void AdvancedGameOptionsPatch(AdvancedGameOptions __instance) { @@ -248,8 +268,14 @@ 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 - string toSpeak = " "; - string currentPetName = getCurrentPetName(); + string toSpeak = ""; + if (characterDesignToggleShouldSpeak) + { + toSpeak = "Press left control + space to toggle character appearance controls"; + characterDesignToggleShouldSpeak = false; + } + string itemsToSpeak = ""; + string changesToSpeak = ""; if (___nameBox.Selected) { @@ -281,23 +307,65 @@ namespace stardew_access.Patches else if (MainClass.Config.CharacterCreationMenuNextKey.JustPressed() && !isRunning) { isRunning = true; - CycleThroughItems(true, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel); + itemsToSpeak =CycleThroughItems(true, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel); + if (itemsToSpeak != "") + toSpeak = $"{itemsToSpeak} \n {toSpeak}"; Task.Delay(200).ContinueWith(_ => { isRunning = false; }); } else if (MainClass.Config.CharacterCreationMenuPreviousKey.JustPressed() && !isRunning) { isRunning = true; - CycleThroughItems(false, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel); + toSpeak = CycleThroughItems(false, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel); Task.Delay(200).ContinueWith(_ => { isRunning = false; }); } - if (prevPetName != currentPetName) + else if (characterDesignToggle && MainClass.Config.CharacterCreationMenuSliderIncreaseKey.JustPressed() && !isRunning) { - prevPetName = currentPetName; - toSpeak = $"Current Pet: {currentPetName} \n {toSpeak}"; + isRunning = true; + AdjustCurrentSlider(true, __instance); + Task.Delay(200).ContinueWith(_ => { isRunning = false; }); } - if (characterCreationMenuQueryKey != toSpeak && toSpeak != " ") + else if (characterDesignToggle && MainClass.Config.CharacterCreationMenuSliderLargeIncreaseKey.JustPressed() && !isRunning) + { + isRunning = true; + AdjustCurrentSlider(true, __instance, 10); + Task.Delay(200).ContinueWith(_ => { isRunning = false; }); + } + + else if (characterDesignToggle && MainClass.Config.CharacterCreationMenuSliderDecreaseKey.JustPressed() && !isRunning) + { + isRunning = true; + AdjustCurrentSlider(false, __instance); + Task.Delay(200).ContinueWith(_ => { isRunning = false; }); + } + + else if (characterDesignToggle && MainClass.Config.CharacterCreationMenuSliderLargeDecreaseKey.JustPressed() && !isRunning) + { + isRunning = true; + AdjustCurrentSlider(false, __instance, 10); + Task.Delay(200).ContinueWith(_ => { isRunning = false; }); + } + + else if (Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl) && MainClass.Config.CharacterCreationMenuDesignToggleKey.JustPressed() && !isRunning) + { + string displayState = ""; + characterDesignToggle = !characterDesignToggle; + saveGameIndex = Math.Min(saveGameIndex, 5); // move to random skin button if focus was beyond that point + if (characterDesignToggle) + { + displayState = "shown"; + } else { + displayState = "hidden"; + } + toSpeak = $"Character design controls {displayState}. \n {toSpeak}"; + } + + changesToSpeak = getChangesToSpeak(__instance); + if (changesToSpeak != "") + toSpeak = $"{toSpeak} \n {changesToSpeak}"; + + if (characterCreationMenuQueryKey != toSpeak && toSpeak.Trim() != "") { characterCreationMenuQueryKey = toSpeak; MainClass.ScreenReader.Say(toSpeak, true); @@ -309,10 +377,219 @@ namespace stardew_access.Patches } } - private static void CycleThroughItems(bool increase, CharacterCustomization __instance, bool ___skipIntro, + private static string getChangesToSpeak(CharacterCustomization __instance) + { + string toSpeak = ""; + string currentPetName = getCurrentPetName(); + string currentSkin = getCurrentSkin(); + string currentHair = getCurrentHair(); + string currentShirt = getCurrentShirt(); + string currentPants = getCurrentPants(); + string currentAccessory = getCurrentAccessory(); + string currentEyeColor = getCurrentEyeColor(); + string currentEyeColorHue = getCurrentEyeColorHue(__instance); + string currentEyeColorSaturation = getCurrentEyeColorSaturation(__instance); + string currentEyeColorValue = getCurrentEyeColorValue(__instance); + string currentHairColor = getCurrentHairColor(); + string currentHairColorHue = getCurrentHairColorHue(__instance); + string currentHairColorSaturation = getCurrentHairColorSaturation(__instance); + string currentHairColorValue = getCurrentHairColorValue(__instance); + string currentPantsColor = getCurrentPantsColor(); + string currentPantsColorHue = getCurrentPantsColorHue(__instance); + string currentPantsColorSaturation = getCurrentPantsColorSaturation(__instance); + string currentPantsColorValue = getCurrentPantsColorValue(__instance); + + if (characterDesignToggle) + { + if (prevSkin != currentSkin) + { + prevSkin = currentSkin; + if (currentSkin != "") + toSpeak = $"{toSpeak} \n {currentSkin}"; + } + + if (prevHair != currentHair) + { + prevHair = currentHair; + if (currentHair != "") + toSpeak = $"{toSpeak} \n {currentHair}"; + } + + if (prevShirt != currentShirt) + { + prevShirt = currentShirt; + if (currentShirt != "") + toSpeak = $"{toSpeak} \n {currentShirt}"; + } + + if (prevPants != currentPants) + { + prevPants = currentPants; + if (currentPants != "") + toSpeak = $"{toSpeak} \n {currentPants}"; + } + + if (prevAccessory != currentAccessory) + { + prevAccessory = currentAccessory; + if (currentAccessory != "") + toSpeak = $"{toSpeak} \n {currentAccessory}"; + } + + if (prevEyeColorHue != currentEyeColorHue) + { + if (currentComponent != null && currentComponent.myID == 522) + { + prevEyeColorHue = currentEyeColorHue; + if (currentEyeColorHue != "") + toSpeak = $"{toSpeak} \n Hue: {currentEyeColorHue}"; + } else { + prevEyeColorHue = ""; + } + } + + if (prevEyeColorSaturation != currentEyeColorSaturation) + { + if (currentComponent != null && currentComponent.myID == 523) + { + prevEyeColorSaturation = currentEyeColorSaturation; + if (currentEyeColorSaturation != "") + toSpeak = $"{toSpeak} \n Saturation: {currentEyeColorSaturation}"; + } else { + prevEyeColorSaturation = ""; + } + } + + if (prevEyeColorValue != currentEyeColorValue) + { + if (currentComponent != null && currentComponent.myID == 524) + { + prevEyeColorValue = currentEyeColorValue; + if (currentEyeColorValue != "") + toSpeak = $"{toSpeak} \n Value: {currentEyeColorValue}"; + } else { + prevEyeColorValue = ""; + } + } + + if (prevEyeColor != currentEyeColor) + { + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 522 || currentComponent.myID <= 524))) + { + prevEyeColor = currentEyeColor; + if (currentEyeColor != "") + toSpeak = $"{toSpeak} \n {currentEyeColor}"; + } + } + + if (prevHairColorHue != currentHairColorHue) + { + if (currentComponent != null && currentComponent.myID == 525) + { + prevHairColorHue = currentHairColorHue; + if (currentHairColorHue != "") + toSpeak = $"{toSpeak} \n Hue: {currentHairColorHue}"; + } else { + prevHairColorHue = ""; + } + } + + if (prevHairColorSaturation != currentHairColorSaturation) + { + if (currentComponent != null && currentComponent.myID == 526) + { + prevHairColorSaturation = currentHairColorSaturation; + if (currentHairColorSaturation != "") + toSpeak = $"{toSpeak} \n Saturation: {currentHairColorSaturation}"; + } else { + prevHairColorSaturation = ""; + } + } + + if (prevHairColorValue != currentHairColorValue) + { + if (currentComponent != null && currentComponent.myID == 527) + { + prevHairColorValue = currentHairColorValue; + if (currentHairColorValue != "") + toSpeak = $"{toSpeak} \n Value: {currentHairColorValue}"; + } else { + prevHairColorValue = ""; + } + } + + if (prevHairColor != currentHairColor) + { + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 525 || currentComponent.myID <= 527))) + { + prevHairColor = currentHairColor; + if (currentHairColor != "") + toSpeak = $"{toSpeak} \n {currentHairColor}"; + } + } + + if (prevPantsColorHue != currentPantsColorHue) + { + if (currentComponent != null && currentComponent.myID == 528) + { + prevPantsColorHue = currentPantsColorHue; + if (currentPantsColorHue != "") + toSpeak = $"{toSpeak} \n Hue: {currentPantsColorHue}"; + } else { + prevPantsColorHue = ""; + } + } + + if (prevPantsColorSaturation != currentPantsColorSaturation) + { + if (currentComponent != null && currentComponent.myID == 529) + { + prevPantsColorSaturation = currentPantsColorSaturation; + if (currentPantsColorSaturation != "") + toSpeak = $"{toSpeak} \n Saturation: {currentPantsColorSaturation}"; + } else { + prevPantsColorSaturation = ""; + } + } + + if (prevPantsColorValue != currentPantsColorValue) + { + if (currentComponent != null && currentComponent.myID == 530) + { + prevPantsColorValue = currentPantsColorValue; + if (currentPantsColorValue != "") + toSpeak = $"{toSpeak} \n Value: {currentPantsColorValue}"; + } else { + prevPantsColorValue = ""; + } + } + + if (prevPantsColor != currentPantsColor) + { + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 528 || currentComponent.myID <= 530))) + { + prevPantsColor = currentPantsColor; + if (currentPantsColor != "") + toSpeak = $"{toSpeak} \n {currentPantsColor}"; + } + } + } + + if (prevPetName != currentPetName) + { + prevPetName = currentPetName; + if (currentPetName != "") + toSpeak = $"{toSpeak} \n Current Pet: {currentPetName}"; + } + return toSpeak.Trim(); + } + + + private static string CycleThroughItems(bool increase, CharacterCustomization __instance, bool ___skipIntro, ClickableComponent ___startingCabinsLabel, ClickableComponent ___difficultyModifierLabel) { string toSpeak = " "; + int DesignControlsIndex = 0; Dictionary buttons = new(); #region Add buttons with their names IF they are available @@ -339,11 +616,94 @@ namespace stardew_access.Patches if (__instance.randomButton != null && __instance.randomButton.visible) buttons.Add(__instance.randomButton, "Random Skin Button"); + // Controls to rotate the farmer (Potentially useful for low vision players) are first if they're available. + // They also appear above the gender buttons, so we handle them separately here. + if (characterDesignToggle && new[] {__instance.leftSelectionButtons.Count, __instance.rightSelectionButtons.Count }.All(c => c >= 0)) // both have Count > 0 + { + if (new[] {__instance.leftSelectionButtons[DesignControlsIndex].visible, __instance.rightSelectionButtons[DesignControlsIndex].visible }.All(v => v == true) // both visible + && new[] {__instance.leftSelectionButtons[DesignControlsIndex].name, __instance.rightSelectionButtons[DesignControlsIndex].name }.All(n => n == "Direction")) // both named "Direction" + { + buttons.Add(__instance.leftSelectionButtons[DesignControlsIndex], "Rotate Left Button"); + buttons.Add(__instance.rightSelectionButtons[DesignControlsIndex], "Rotate Right Button"); + ++DesignControlsIndex; + } + } + if (__instance.genderButtons.Count > 0) { buttons.Add(__instance.genderButtons[0], ((Game1.player.IsMale) ? "Selected " : "") + "Gender: Male Button"); buttons.Add(__instance.genderButtons[1], ((!Game1.player.IsMale) ? "Selected " : "") + "Gender: Female Button"); } + + if (characterDesignToggle&& new[] {__instance.leftSelectionButtons.Count, __instance.rightSelectionButtons.Count }.All(c => c >= DesignControlsIndex) && new[] {__instance.leftSelectionButtons[DesignControlsIndex].visible, __instance.rightSelectionButtons[DesignControlsIndex].visible }.All(v => v == true)) + { + while(DesignControlsIndex < __instance.leftSelectionButtons.Count) + { + ClickableComponent left = __instance.leftSelectionButtons[DesignControlsIndex]; + ClickableComponent right = __instance.rightSelectionButtons[DesignControlsIndex]; + string name = left.name; + // minor cleanup on names to be slightly more descriptive + switch (name) + { + case "Skin": + name += " Tone"; + break; + case "Hair": + name += " Style"; + break; + case "Acc": + name = "Accessory"; + break; + default: + break; + } + if (!buttons.ContainsKey(left) || !buttons.ContainsKey(right)) + { + buttons.Add(left, $"Previous {name} button"); + buttons.Add(right, $"Next {name} button"); + } + //MainClass.ScreenReader.Say($"Left {DesignControlsIndex}: {__instance.leftSelectionButtons[DesignControlsIndex]} {__instance.leftSelectionButtons[DesignControlsIndex].name}\n", true); + //MainClass.ScreenReader.Say($"Right {DesignControlsIndex}: {__instance.rightSelectionButtons[DesignControlsIndex]} {__instance.rightSelectionButtons[DesignControlsIndex].name}\n", true); + ++DesignControlsIndex; + } + + ClickableComponent eyeColorHue = __instance.getComponentWithID(522); + if (eyeColorHue != null && eyeColorHue.visible) + buttons.Add(eyeColorHue, "eye color hue slider"); + + ClickableComponent eyeColorSaturation = __instance.getComponentWithID(523); + if (eyeColorSaturation != null && eyeColorSaturation.visible) + buttons.Add(eyeColorSaturation, "eye color saturation slider"); + + ClickableComponent eyeColorValue = __instance.getComponentWithID(524); + if (eyeColorValue != null && eyeColorValue.visible) + buttons.Add(eyeColorValue, "eye color Value slider"); + + ClickableComponent hairColorHue = __instance.getComponentWithID(525); + if (hairColorHue != null && hairColorHue.visible) + buttons.Add(hairColorHue, "hair color hue slider"); + + ClickableComponent hairColorSaturation = __instance.getComponentWithID(526); + if (hairColorSaturation != null && hairColorSaturation.visible) + buttons.Add(hairColorSaturation, "hair color saturation slider"); + + ClickableComponent hairColorValue = __instance.getComponentWithID(527); + if (hairColorValue != null && hairColorValue.visible) + buttons.Add(hairColorValue, "hair color Value slider"); + + ClickableComponent pantsColorHue = __instance.getComponentWithID(528); + if (pantsColorHue != null && pantsColorHue.visible) + buttons.Add(pantsColorHue, "pants color hue slider"); + + ClickableComponent pantsColorSaturation = __instance.getComponentWithID(529); + if (pantsColorSaturation != null && pantsColorSaturation.visible) + buttons.Add(pantsColorSaturation, "pants color saturation slider"); + + ClickableComponent pantsColorValue = __instance.getComponentWithID(530); + if (pantsColorValue != null && pantsColorValue.visible) + buttons.Add(pantsColorValue, "pants color Value slider"); + } + #endregion #region Farm layout related @@ -420,18 +780,230 @@ namespace stardew_access.Patches saveGameIndex = size; } - buttons.ElementAt(saveGameIndex).Key.snapMouseCursor(); + currentComponent = buttons.ElementAt(saveGameIndex).Key; + currentComponent!.snapMouseCursor(); + __instance.setCurrentlySnappedComponentTo(currentComponent!.myID); + toSpeak = buttons.ElementAt(saveGameIndex).Value; - if (toSpeak != " ") + return toSpeak.Trim(); + } + + private static SliderBar? getCurrentSliderBar(int id, CharacterCustomization __instance) + { + if (id >= 522 && id <= 530) { - MainClass.ScreenReader.Say(toSpeak, true); + // Three ColorPickers with 3 SliderBars each. + // First group ids by ColorPicker. + // Maps 522-524 -> 0, 525-527 -> 1, 528-530 -> 2 + int whichColorPicker = (int)Math.Floor(((float)id - 522f) / 3f); + // Next group ids by slider type. + // Maps [522,525,528] -> 0, [523,526,529] -> 1, [524,527,530] -> 2 + int whichSliderBar = (int)Math.Floor((float)id % 3f); + ColorPicker cp; + switch (whichColorPicker) + { + default: + case 0: + // 522-524 == eye color + cp = __instance.eyeColorPicker; + break; + case 1: + // 525-527 == hair color + cp = __instance.hairColorPicker; + break; + case 2: + // 528-530 == pants color + cp = __instance.pantsColorPicker; + break; + } + SliderBar sb; + switch (whichSliderBar) + { + default: + case 0: + // 522, 525, 528 == hue slider + sb = cp.hueBar; + break; + case 1: + // 523, 526, 529 == saturation slider + sb = cp.saturationBar; + break; + case 2: + // 524, 527, 530 == value slider + sb = cp.valueBar; + break; + } + return sb; + } else { + return null; } } + private static void AdjustCurrentSlider(bool increase, CharacterCustomization __instance, int amount=1) + { + if (currentComponent != null && currentComponent.myID >= 522 && currentComponent.myID <= 530) + { + SliderBar sb = getCurrentSliderBar(currentComponent.myID, __instance) !; + if (sb != null) + { + double step = ((double)sb.bounds.Width / 100d); // size of 1% change in slider value + double value = (double)sb.value; + double x = 0d; + int y = currentComponent.bounds.Center.Y; + if (increase) + { + value = Math.Min(value + amount, 99d); + x = Math.Min(Math.Ceiling((value * step)), (double)sb.bounds.Width); + } else { + value = Math.Max(value - amount, 0d); + x = Math.Max(Math.Ceiling((value * step)), 0d); + } + x += (double)currentComponent.bounds.Left; + Game1.setMousePosition((int)x, y); + Game1.activeClickableMenu.receiveLeftClick((int)x, y); + } + } + } + + // Most values (exception noted below) are 0 indexed internally but visually start from 1. Thus we increment before returning. + private static string getCurrentSkin() + { + if (currentComponent != null && (currentComponent.myID == 507 || currentComponent.name == "Skin")) + return $"Skin tone: {Game1.player.skin.Value + 1}"; + return ""; + } + + private static string getCurrentHair() + { + if (currentComponent != null && (currentComponent.myID == 507 || currentComponent.name == "Hair")) + return $"hair style: {Game1.player.hair.Value + 1}"; + return ""; + } + + private static string getCurrentShirt() + { + if (currentComponent != null && (currentComponent.myID == 507 || currentComponent.name == "Shirt")) + return $"Shirt: {Game1.player.shirt.Value + 1}"; + return ""; + } + + private static string getCurrentPants() + { + if (currentComponent != null && (currentComponent.myID == 507 || currentComponent.name == "Pants Style")) + return $"Pants: {Game1.player.pants.Value + 1}"; + return ""; + } + + private static string getCurrentAccessory() + { + // Internally accessory starts from -1 while displaying +1 on screen. + if (currentComponent != null && (currentComponent.myID == 507 || currentComponent.name == "Acc")) + return $"accessory: {Game1.player.accessory.Value + 2}"; + return ""; + } + + private static string getCurrentEyeColor() + { + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 522 && currentComponent.myID <= 524))) + return $"Eye color: {Game1.player.newEyeColor.R}, {Game1.player.newEyeColor.G}, {Game1.player.newEyeColor.B}"; + return ""; + } + + private static string getCurrentEyeColorHue(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(522, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 522 && currentComponent.myID <= 524))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentEyeColorSaturation(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(523, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 522 && currentComponent.myID <= 524))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentEyeColorValue(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(524, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 522 && currentComponent.myID <= 524))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentHairColor() + { + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 525 && currentComponent.myID <= 527))) + return $"Hair color: {Game1.player.hairstyleColor.R}, {Game1.player.hairstyleColor.G}, {Game1.player.hairstyleColor.B}"; + return ""; + } + + private static string getCurrentHairColorHue(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(525, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 525 && currentComponent.myID <= 527))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentHairColorSaturation(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(526, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 525 && currentComponent.myID <= 527))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentHairColorValue(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(527, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 525 && currentComponent.myID <= 527))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentPantsColor() + { + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 528 && currentComponent.myID <= 530))) + return $"Pants color: {Game1.player.pantsColor.R}, {Game1.player.pantsColor.G}, {Game1.player.pantsColor.B}"; + return ""; + } + + private static string getCurrentPantsColorHue(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(528, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 528 && currentComponent.myID <= 530))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentPantsColorSaturation(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(529, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 528 && currentComponent.myID <= 530))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentPantsColorValue(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(530, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 528 && currentComponent.myID <= 530))) + return sb.value!.ToString(); + return ""; + } + private static string getCurrentPetName() { - return ((Game1.player.catPerson) ? "Cat" : "Dog") + " Breed: " + Game1.player.whichPetBreed; + if (currentComponent != null && currentComponent.name == "Pet") + { + return ((Game1.player.catPerson) ? "Cat" : "Dog") + " Breed: " + Game1.player.whichPetBreed; + } else { + return ""; + } } private static string getFarmHoverText(ClickableTextureComponent farm) From baedf69d808fee259c71b9a5f247905b97386f62 Mon Sep 17 00:00:00 2001 From: Katie Durden Date: Tue, 7 Feb 2023 20:45:44 -0800 Subject: [PATCH 4/5] Character creation text boxes announce content if set. Causes text boxes to report their content instead of " text box" when text was already entered. E.G. after entering a name, "Farmer's Name Text Box" becomes "Farmer's Name: " --- stardew-access/Patches/TitleMenuPatches.cs | 38 ++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index ea0903d..e9666c3 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -307,7 +307,7 @@ namespace stardew_access.Patches else if (MainClass.Config.CharacterCreationMenuNextKey.JustPressed() && !isRunning) { isRunning = true; - itemsToSpeak =CycleThroughItems(true, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel); + itemsToSpeak =CycleThroughItems(true, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel, ___nameBox, ___farmnameBox, ___favThingBox); if (itemsToSpeak != "") toSpeak = $"{itemsToSpeak} \n {toSpeak}"; Task.Delay(200).ContinueWith(_ => { isRunning = false; }); @@ -315,7 +315,7 @@ namespace stardew_access.Patches else if (MainClass.Config.CharacterCreationMenuPreviousKey.JustPressed() && !isRunning) { isRunning = true; - toSpeak = CycleThroughItems(false, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel); + toSpeak = CycleThroughItems(false, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel, ___nameBox, ___farmnameBox, ___favThingBox); Task.Delay(200).ContinueWith(_ => { isRunning = false; }); } @@ -586,7 +586,8 @@ namespace stardew_access.Patches private static string CycleThroughItems(bool increase, CharacterCustomization __instance, bool ___skipIntro, - ClickableComponent ___startingCabinsLabel, ClickableComponent ___difficultyModifierLabel) + ClickableComponent ___startingCabinsLabel, ClickableComponent ___difficultyModifierLabel, TextBox ___nameBox, + TextBox ___farmnameBox, TextBox ___favThingBox) { string toSpeak = " "; int DesignControlsIndex = 0; @@ -595,14 +596,39 @@ namespace stardew_access.Patches #region Add buttons with their names IF they are available #region Character related + string postText = ""; if (__instance.nameBoxCC != null && __instance.nameBoxCC.visible) - buttons.Add(__instance.nameBoxCC, "Farmer's Name Text box"); + { + if (___nameBox.Text != "") + { + postText = $": {___nameBox.Text}"; + } else { + postText = " Text Box"; + } + buttons.Add(__instance.nameBoxCC, $"Farmer's Name{postText}"); + } if (__instance.farmnameBoxCC != null && __instance.farmnameBoxCC.visible) - buttons.Add(__instance.farmnameBoxCC, "Farm's Name Text box"); + { + if (___farmnameBox.Text != "") + { + postText = $": {___farmnameBox.Text}"; + } else { + postText = " Text Box"; + } + buttons.Add(__instance.farmnameBoxCC, $"Farm's Name{postText}"); + } if (__instance.favThingBoxCC != null && __instance.favThingBoxCC.visible) - buttons.Add(__instance.favThingBoxCC, "Favourite Thing Text box"); + { + if (___favThingBox.Text != "") + { + postText = $": {___favThingBox.Text}"; + } else { + postText = " Text Box"; + } + buttons.Add(__instance.favThingBoxCC, $"Favourite Thing{postText}"); + } if (__instance.petPortraitBox.HasValue) // Cannot get petButtons like with others { From 7409959c99f89b050f191693d3d5c9682b88d3e4 Mon Sep 17 00:00:00 2001 From: Mohammad Shoaib Khan Date: Fri, 10 Feb 2023 11:17:50 +0530 Subject: [PATCH 5/5] Moved character customization patch to separate file --- stardew-access/HarmonyPatches.cs | 2 +- .../CharacterCustomizationMenuPatches.cs | 829 ++++++++++++++++++ stardew-access/Patches/TitleMenuPatches.cs | 1 - 3 files changed, 830 insertions(+), 2 deletions(-) create mode 100644 stardew-access/Patches/CharacterCustomizationMenuPatches.cs diff --git a/stardew-access/HarmonyPatches.cs b/stardew-access/HarmonyPatches.cs index fbd1d92..dcdc7bd 100644 --- a/stardew-access/HarmonyPatches.cs +++ b/stardew-access/HarmonyPatches.cs @@ -48,7 +48,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.CharacterCustomizationMenuPatch)) + postfix: new HarmonyMethod(typeof(TitleMenuPatches), nameof(CharacterCustomizationPatches.CharacterCustomizationMenuPatch)) ); harmony.Patch( diff --git a/stardew-access/Patches/CharacterCustomizationMenuPatches.cs b/stardew-access/Patches/CharacterCustomizationMenuPatches.cs new file mode 100644 index 0000000..52c4323 --- /dev/null +++ b/stardew-access/Patches/CharacterCustomizationMenuPatches.cs @@ -0,0 +1,829 @@ +using StardewValley; +using StardewValley.Menus; + +namespace stardew_access.Patches { + internal class CharacterCustomizationPatches { + private static bool isRunning = false; + private static int saveGameIndex = -1; + public static string characterCreationMenuQueryKey = " "; + public static string prevPants = " "; + public static string prevShirt = " "; + public static string prevHair = " "; + public static string prevAccessory = " "; + public static string prevSkin = " "; + public static string prevEyeColor = " "; + public static string prevEyeColorHue = " "; + public static string prevEyeColorSaturation = " "; + public static string prevEyeColorValue = " "; + public static string prevHairColor = " "; + public static string prevHairColorHue = " "; + public static string prevHairColorSaturation = " "; + public static string prevHairColorValue = " "; + public static string prevPantsColor = " "; + public static string prevPantsColorHue = " "; + public static string prevPantsColorSaturation = " "; + public static string prevPantsColorValue = " "; + public static string prevPetName = " "; + public static bool characterDesignToggle = false; + public static bool characterDesignToggleShouldSpeak = true; + public static ClickableComponent? currentComponent = null; + + internal static void CharacterCustomizationMenuPatch(CharacterCustomization __instance, bool ___skipIntro, + ClickableComponent ___startingCabinsLabel, ClickableComponent ___difficultyModifierLabel, TextBox ___nameBox, + TextBox ___farmnameBox, TextBox ___favThingBox) + { + try + { + bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box + string toSpeak = ""; + if (characterDesignToggleShouldSpeak) + { + toSpeak = "Press left control + space to toggle character appearance controls"; + characterDesignToggleShouldSpeak = false; + } + string itemsToSpeak = ""; + string changesToSpeak = ""; + + 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; + itemsToSpeak =CycleThroughItems(true, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel, ___nameBox, ___farmnameBox, ___favThingBox); + if (itemsToSpeak != "") + toSpeak = $"{itemsToSpeak} \n {toSpeak}"; + Task.Delay(200).ContinueWith(_ => { isRunning = false; }); + } + else if (MainClass.Config.CharacterCreationMenuPreviousKey.JustPressed() && !isRunning) + { + isRunning = true; + toSpeak = CycleThroughItems(false, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel, ___nameBox, ___farmnameBox, ___favThingBox); + Task.Delay(200).ContinueWith(_ => { isRunning = false; }); + } + + else if (characterDesignToggle && MainClass.Config.CharacterCreationMenuSliderIncreaseKey.JustPressed() && !isRunning) + { + isRunning = true; + AdjustCurrentSlider(true, __instance); + Task.Delay(200).ContinueWith(_ => { isRunning = false; }); + } + + else if (characterDesignToggle && MainClass.Config.CharacterCreationMenuSliderLargeIncreaseKey.JustPressed() && !isRunning) + { + isRunning = true; + AdjustCurrentSlider(true, __instance, 10); + Task.Delay(200).ContinueWith(_ => { isRunning = false; }); + } + + else if (characterDesignToggle && MainClass.Config.CharacterCreationMenuSliderDecreaseKey.JustPressed() && !isRunning) + { + isRunning = true; + AdjustCurrentSlider(false, __instance); + Task.Delay(200).ContinueWith(_ => { isRunning = false; }); + } + + else if (characterDesignToggle && MainClass.Config.CharacterCreationMenuSliderLargeDecreaseKey.JustPressed() && !isRunning) + { + isRunning = true; + AdjustCurrentSlider(false, __instance, 10); + Task.Delay(200).ContinueWith(_ => { isRunning = false; }); + } + + else if (Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl) && MainClass.Config.CharacterCreationMenuDesignToggleKey.JustPressed() && !isRunning) + { + string displayState = ""; + characterDesignToggle = !characterDesignToggle; + saveGameIndex = Math.Min(saveGameIndex, 5); // move to random skin button if focus was beyond that point + if (characterDesignToggle) + { + displayState = "shown"; + } else { + displayState = "hidden"; + } + toSpeak = $"Character design controls {displayState}. \n {toSpeak}"; + } + + changesToSpeak = getChangesToSpeak(__instance); + if (changesToSpeak != "") + toSpeak = $"{toSpeak} \n {changesToSpeak}"; + + if (characterCreationMenuQueryKey != toSpeak && toSpeak.Trim() != "") + { + characterCreationMenuQueryKey = toSpeak; + MainClass.ScreenReader.Say(toSpeak, true); + } + } + catch (Exception e) + { + MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}"); + } + } + + private static string getChangesToSpeak(CharacterCustomization __instance) + { + string toSpeak = ""; + string currentPetName = getCurrentPetName(); + string currentSkin = getCurrentSkin(); + string currentHair = getCurrentHair(); + string currentShirt = getCurrentShirt(); + string currentPants = getCurrentPants(); + string currentAccessory = getCurrentAccessory(); + string currentEyeColor = getCurrentEyeColor(); + string currentEyeColorHue = getCurrentEyeColorHue(__instance); + string currentEyeColorSaturation = getCurrentEyeColorSaturation(__instance); + string currentEyeColorValue = getCurrentEyeColorValue(__instance); + string currentHairColor = getCurrentHairColor(); + string currentHairColorHue = getCurrentHairColorHue(__instance); + string currentHairColorSaturation = getCurrentHairColorSaturation(__instance); + string currentHairColorValue = getCurrentHairColorValue(__instance); + string currentPantsColor = getCurrentPantsColor(); + string currentPantsColorHue = getCurrentPantsColorHue(__instance); + string currentPantsColorSaturation = getCurrentPantsColorSaturation(__instance); + string currentPantsColorValue = getCurrentPantsColorValue(__instance); + + if (characterDesignToggle) + { + if (prevSkin != currentSkin) + { + prevSkin = currentSkin; + if (currentSkin != "") + toSpeak = $"{toSpeak} \n {currentSkin}"; + } + + if (prevHair != currentHair) + { + prevHair = currentHair; + if (currentHair != "") + toSpeak = $"{toSpeak} \n {currentHair}"; + } + + if (prevShirt != currentShirt) + { + prevShirt = currentShirt; + if (currentShirt != "") + toSpeak = $"{toSpeak} \n {currentShirt}"; + } + + if (prevPants != currentPants) + { + prevPants = currentPants; + if (currentPants != "") + toSpeak = $"{toSpeak} \n {currentPants}"; + } + + if (prevAccessory != currentAccessory) + { + prevAccessory = currentAccessory; + if (currentAccessory != "") + toSpeak = $"{toSpeak} \n {currentAccessory}"; + } + + if (prevEyeColorHue != currentEyeColorHue) + { + if (currentComponent != null && currentComponent.myID == 522) + { + prevEyeColorHue = currentEyeColorHue; + if (currentEyeColorHue != "") + toSpeak = $"{toSpeak} \n Hue: {currentEyeColorHue}"; + } else { + prevEyeColorHue = ""; + } + } + + if (prevEyeColorSaturation != currentEyeColorSaturation) + { + if (currentComponent != null && currentComponent.myID == 523) + { + prevEyeColorSaturation = currentEyeColorSaturation; + if (currentEyeColorSaturation != "") + toSpeak = $"{toSpeak} \n Saturation: {currentEyeColorSaturation}"; + } else { + prevEyeColorSaturation = ""; + } + } + + if (prevEyeColorValue != currentEyeColorValue) + { + if (currentComponent != null && currentComponent.myID == 524) + { + prevEyeColorValue = currentEyeColorValue; + if (currentEyeColorValue != "") + toSpeak = $"{toSpeak} \n Value: {currentEyeColorValue}"; + } else { + prevEyeColorValue = ""; + } + } + + if (prevEyeColor != currentEyeColor) + { + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 522 || currentComponent.myID <= 524))) + { + prevEyeColor = currentEyeColor; + if (currentEyeColor != "") + toSpeak = $"{toSpeak} \n {currentEyeColor}"; + } + } + + if (prevHairColorHue != currentHairColorHue) + { + if (currentComponent != null && currentComponent.myID == 525) + { + prevHairColorHue = currentHairColorHue; + if (currentHairColorHue != "") + toSpeak = $"{toSpeak} \n Hue: {currentHairColorHue}"; + } else { + prevHairColorHue = ""; + } + } + + if (prevHairColorSaturation != currentHairColorSaturation) + { + if (currentComponent != null && currentComponent.myID == 526) + { + prevHairColorSaturation = currentHairColorSaturation; + if (currentHairColorSaturation != "") + toSpeak = $"{toSpeak} \n Saturation: {currentHairColorSaturation}"; + } else { + prevHairColorSaturation = ""; + } + } + + if (prevHairColorValue != currentHairColorValue) + { + if (currentComponent != null && currentComponent.myID == 527) + { + prevHairColorValue = currentHairColorValue; + if (currentHairColorValue != "") + toSpeak = $"{toSpeak} \n Value: {currentHairColorValue}"; + } else { + prevHairColorValue = ""; + } + } + + if (prevHairColor != currentHairColor) + { + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 525 || currentComponent.myID <= 527))) + { + prevHairColor = currentHairColor; + if (currentHairColor != "") + toSpeak = $"{toSpeak} \n {currentHairColor}"; + } + } + + if (prevPantsColorHue != currentPantsColorHue) + { + if (currentComponent != null && currentComponent.myID == 528) + { + prevPantsColorHue = currentPantsColorHue; + if (currentPantsColorHue != "") + toSpeak = $"{toSpeak} \n Hue: {currentPantsColorHue}"; + } else { + prevPantsColorHue = ""; + } + } + + if (prevPantsColorSaturation != currentPantsColorSaturation) + { + if (currentComponent != null && currentComponent.myID == 529) + { + prevPantsColorSaturation = currentPantsColorSaturation; + if (currentPantsColorSaturation != "") + toSpeak = $"{toSpeak} \n Saturation: {currentPantsColorSaturation}"; + } else { + prevPantsColorSaturation = ""; + } + } + + if (prevPantsColorValue != currentPantsColorValue) + { + if (currentComponent != null && currentComponent.myID == 530) + { + prevPantsColorValue = currentPantsColorValue; + if (currentPantsColorValue != "") + toSpeak = $"{toSpeak} \n Value: {currentPantsColorValue}"; + } else { + prevPantsColorValue = ""; + } + } + + if (prevPantsColor != currentPantsColor) + { + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 528 || currentComponent.myID <= 530))) + { + prevPantsColor = currentPantsColor; + if (currentPantsColor != "") + toSpeak = $"{toSpeak} \n {currentPantsColor}"; + } + } + } + + if (prevPetName != currentPetName) + { + prevPetName = currentPetName; + if (currentPetName != "") + toSpeak = $"{toSpeak} \n Current Pet: {currentPetName}"; + } + return toSpeak.Trim(); + } + + + private static string CycleThroughItems(bool increase, CharacterCustomization __instance, bool ___skipIntro, + ClickableComponent ___startingCabinsLabel, ClickableComponent ___difficultyModifierLabel, TextBox ___nameBox, + TextBox ___farmnameBox, TextBox ___favThingBox) + { + string toSpeak = " "; + int DesignControlsIndex = 0; + Dictionary buttons = new(); + + #region Add buttons with their names IF they are available + + #region Character related + string postText = ""; + if (__instance.nameBoxCC != null && __instance.nameBoxCC.visible) + { + if (___nameBox.Text != "") + { + postText = $": {___nameBox.Text}"; + } else { + postText = " Text Box"; + } + buttons.Add(__instance.nameBoxCC, $"Farmer's Name{postText}"); + } + + if (__instance.farmnameBoxCC != null && __instance.farmnameBoxCC.visible) + { + if (___farmnameBox.Text != "") + { + postText = $": {___farmnameBox.Text}"; + } else { + postText = " Text Box"; + } + buttons.Add(__instance.farmnameBoxCC, $"Farm's Name{postText}"); + } + + if (__instance.favThingBoxCC != null && __instance.favThingBoxCC.visible) + { + if (___favThingBox.Text != "") + { + postText = $": {___favThingBox.Text}"; + } else { + postText = " Text Box"; + } + buttons.Add(__instance.favThingBoxCC, $"Favourite Thing{postText}"); + } + + if (__instance.petPortraitBox.HasValue) // Cannot get petButtons like with others + { + ClickableComponent petPrev = __instance.getComponentWithID(511); + buttons.Add(petPrev, "Previous pet button"); + + ClickableComponent petNext = __instance.getComponentWithID(510); + buttons.Add(petNext, "Next pet button"); + } + + if (__instance.randomButton != null && __instance.randomButton.visible) + buttons.Add(__instance.randomButton, "Random Skin Button"); + + // Controls to rotate the farmer (Potentially useful for low vision players) are first if they're available. + // They also appear above the gender buttons, so we handle them separately here. + if (characterDesignToggle && new[] {__instance.leftSelectionButtons.Count, __instance.rightSelectionButtons.Count }.All(c => c >= 0)) // both have Count > 0 + { + if (new[] {__instance.leftSelectionButtons[DesignControlsIndex].visible, __instance.rightSelectionButtons[DesignControlsIndex].visible }.All(v => v == true) // both visible + && new[] {__instance.leftSelectionButtons[DesignControlsIndex].name, __instance.rightSelectionButtons[DesignControlsIndex].name }.All(n => n == "Direction")) // both named "Direction" + { + buttons.Add(__instance.leftSelectionButtons[DesignControlsIndex], "Rotate Left Button"); + buttons.Add(__instance.rightSelectionButtons[DesignControlsIndex], "Rotate Right Button"); + ++DesignControlsIndex; + } + } + + if (__instance.genderButtons.Count > 0) + { + buttons.Add(__instance.genderButtons[0], ((Game1.player.IsMale) ? "Selected " : "") + "Gender: Male Button"); + buttons.Add(__instance.genderButtons[1], ((!Game1.player.IsMale) ? "Selected " : "") + "Gender: Female Button"); + } + + if (characterDesignToggle&& new[] {__instance.leftSelectionButtons.Count, __instance.rightSelectionButtons.Count }.All(c => c >= DesignControlsIndex) && new[] {__instance.leftSelectionButtons[DesignControlsIndex].visible, __instance.rightSelectionButtons[DesignControlsIndex].visible }.All(v => v == true)) + { + while(DesignControlsIndex < __instance.leftSelectionButtons.Count) + { + ClickableComponent left = __instance.leftSelectionButtons[DesignControlsIndex]; + ClickableComponent right = __instance.rightSelectionButtons[DesignControlsIndex]; + string name = left.name; + // minor cleanup on names to be slightly more descriptive + switch (name) + { + case "Skin": + name += " Tone"; + break; + case "Hair": + name += " Style"; + break; + case "Acc": + name = "Accessory"; + break; + default: + break; + } + if (!buttons.ContainsKey(left) || !buttons.ContainsKey(right)) + { + buttons.Add(left, $"Previous {name} button"); + buttons.Add(right, $"Next {name} button"); + } + //MainClass.ScreenReader.Say($"Left {DesignControlsIndex}: {__instance.leftSelectionButtons[DesignControlsIndex]} {__instance.leftSelectionButtons[DesignControlsIndex].name}\n", true); + //MainClass.ScreenReader.Say($"Right {DesignControlsIndex}: {__instance.rightSelectionButtons[DesignControlsIndex]} {__instance.rightSelectionButtons[DesignControlsIndex].name}\n", true); + ++DesignControlsIndex; + } + + ClickableComponent eyeColorHue = __instance.getComponentWithID(522); + if (eyeColorHue != null && eyeColorHue.visible) + buttons.Add(eyeColorHue, "eye color hue slider"); + + ClickableComponent eyeColorSaturation = __instance.getComponentWithID(523); + if (eyeColorSaturation != null && eyeColorSaturation.visible) + buttons.Add(eyeColorSaturation, "eye color saturation slider"); + + ClickableComponent eyeColorValue = __instance.getComponentWithID(524); + if (eyeColorValue != null && eyeColorValue.visible) + buttons.Add(eyeColorValue, "eye color Value slider"); + + ClickableComponent hairColorHue = __instance.getComponentWithID(525); + if (hairColorHue != null && hairColorHue.visible) + buttons.Add(hairColorHue, "hair color hue slider"); + + ClickableComponent hairColorSaturation = __instance.getComponentWithID(526); + if (hairColorSaturation != null && hairColorSaturation.visible) + buttons.Add(hairColorSaturation, "hair color saturation slider"); + + ClickableComponent hairColorValue = __instance.getComponentWithID(527); + if (hairColorValue != null && hairColorValue.visible) + buttons.Add(hairColorValue, "hair color Value slider"); + + ClickableComponent pantsColorHue = __instance.getComponentWithID(528); + if (pantsColorHue != null && pantsColorHue.visible) + buttons.Add(pantsColorHue, "pants color hue slider"); + + ClickableComponent pantsColorSaturation = __instance.getComponentWithID(529); + if (pantsColorSaturation != null && pantsColorSaturation.visible) + buttons.Add(pantsColorSaturation, "pants color saturation slider"); + + ClickableComponent pantsColorValue = __instance.getComponentWithID(530); + if (pantsColorValue != null && pantsColorValue.visible) + buttons.Add(pantsColorValue, "pants color Value slider"); + } + + #endregion + + #region Farm layout related + if (__instance.farmTypeButtons.Count > 0) + { + 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) + 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"); + #endregion + + #region Co-op related + if (__instance.source == CharacterCustomization.Source.HostNewFarm) + { + 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.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"); + + if (__instance.backButton != null && __instance.backButton.visible) + buttons.Add(__instance.backButton, "Back Button"); + #endregion + + int size = buttons.Count - 1; + + if (increase) + { + saveGameIndex++; + if (saveGameIndex > size) + saveGameIndex = 0; + } + else + { + saveGameIndex--; + if (saveGameIndex < 0) + saveGameIndex = size; + } + + currentComponent = buttons.ElementAt(saveGameIndex).Key; + currentComponent!.snapMouseCursor(); + __instance.setCurrentlySnappedComponentTo(currentComponent!.myID); + + toSpeak = buttons.ElementAt(saveGameIndex).Value; + + return toSpeak.Trim(); + } + + private static string getFarmHoverText(ClickableTextureComponent farm) + { + string hoverTitle = " ", hoverText = " "; + if (!farm.name.Contains("Gray")) + { + if (farm.hoverText.Contains('_')) + { + hoverTitle = farm.hoverText.Split('_')[0]; + hoverText = farm.hoverText.Split('_')[1]; + } + else + { + hoverTitle = " "; + hoverText = farm.hoverText; + } + } + else + { + if (farm.name.Contains("Gray")) + { + hoverText = "Reach level 10 " + Game1.content.LoadString("Strings\\UI:Character_" + farm.name.Split('_')[1]) + " to unlock."; + } + } + + return $"{hoverTitle}: {hoverText}"; + } + + private static SliderBar? getCurrentSliderBar(int id, CharacterCustomization __instance) + { + if (id >= 522 && id <= 530) + { + // Three ColorPickers with 3 SliderBars each. + // First group ids by ColorPicker. + // Maps 522-524 -> 0, 525-527 -> 1, 528-530 -> 2 + int whichColorPicker = (int)Math.Floor(((float)id - 522f) / 3f); + // Next group ids by slider type. + // Maps [522,525,528] -> 0, [523,526,529] -> 1, [524,527,530] -> 2 + int whichSliderBar = (int)Math.Floor((float)id % 3f); + ColorPicker cp; + switch (whichColorPicker) + { + default: + case 0: + // 522-524 == eye color + cp = __instance.eyeColorPicker; + break; + case 1: + // 525-527 == hair color + cp = __instance.hairColorPicker; + break; + case 2: + // 528-530 == pants color + cp = __instance.pantsColorPicker; + break; + } + SliderBar sb; + switch (whichSliderBar) + { + default: + case 0: + // 522, 525, 528 == hue slider + sb = cp.hueBar; + break; + case 1: + // 523, 526, 529 == saturation slider + sb = cp.saturationBar; + break; + case 2: + // 524, 527, 530 == value slider + sb = cp.valueBar; + break; + } + return sb; + } else { + return null; + } + } + + private static void AdjustCurrentSlider(bool increase, CharacterCustomization __instance, int amount=1) + { + if (currentComponent != null && currentComponent.myID >= 522 && currentComponent.myID <= 530) + { + SliderBar sb = getCurrentSliderBar(currentComponent.myID, __instance) !; + if (sb != null) + { + double step = ((double)sb.bounds.Width / 100d); // size of 1% change in slider value + double value = (double)sb.value; + double x = 0d; + int y = currentComponent.bounds.Center.Y; + if (increase) + { + value = Math.Min(value + amount, 99d); + x = Math.Min(Math.Ceiling((value * step)), (double)sb.bounds.Width); + } else { + value = Math.Max(value - amount, 0d); + x = Math.Max(Math.Ceiling((value * step)), 0d); + } + x += (double)currentComponent.bounds.Left; + Game1.setMousePosition((int)x, y); + Game1.activeClickableMenu.receiveLeftClick((int)x, y); + } + } + } + + // Most values (exception noted below) are 0 indexed internally but visually start from 1. Thus we increment before returning. + private static string getCurrentSkin() + { + if (currentComponent != null && (currentComponent.myID == 507 || currentComponent.name == "Skin")) + return $"Skin tone: {Game1.player.skin.Value + 1}"; + return ""; + } + + private static string getCurrentHair() + { + if (currentComponent != null && (currentComponent.myID == 507 || currentComponent.name == "Hair")) + return $"hair style: {Game1.player.hair.Value + 1}"; + return ""; + } + + private static string getCurrentShirt() + { + if (currentComponent != null && (currentComponent.myID == 507 || currentComponent.name == "Shirt")) + return $"Shirt: {Game1.player.shirt.Value + 1}"; + return ""; + } + + private static string getCurrentPants() + { + if (currentComponent != null && (currentComponent.myID == 507 || currentComponent.name == "Pants Style")) + return $"Pants: {Game1.player.pants.Value + 1}"; + return ""; + } + + private static string getCurrentAccessory() + { + // Internally accessory starts from -1 while displaying +1 on screen. + if (currentComponent != null && (currentComponent.myID == 507 || currentComponent.name == "Acc")) + return $"accessory: {Game1.player.accessory.Value + 2}"; + return ""; + } + + private static string getCurrentEyeColor() + { + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 522 && currentComponent.myID <= 524))) + return $"Eye color: {Game1.player.newEyeColor.R}, {Game1.player.newEyeColor.G}, {Game1.player.newEyeColor.B}"; + return ""; + } + + private static string getCurrentEyeColorHue(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(522, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 522 && currentComponent.myID <= 524))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentEyeColorSaturation(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(523, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 522 && currentComponent.myID <= 524))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentEyeColorValue(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(524, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 522 && currentComponent.myID <= 524))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentHairColor() + { + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 525 && currentComponent.myID <= 527))) + return $"Hair color: {Game1.player.hairstyleColor.R}, {Game1.player.hairstyleColor.G}, {Game1.player.hairstyleColor.B}"; + return ""; + } + + private static string getCurrentHairColorHue(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(525, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 525 && currentComponent.myID <= 527))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentHairColorSaturation(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(526, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 525 && currentComponent.myID <= 527))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentHairColorValue(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(527, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 525 && currentComponent.myID <= 527))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentPantsColor() + { + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 528 && currentComponent.myID <= 530))) + return $"Pants color: {Game1.player.pantsColor.R}, {Game1.player.pantsColor.G}, {Game1.player.pantsColor.B}"; + return ""; + } + + private static string getCurrentPantsColorHue(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(528, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 528 && currentComponent.myID <= 530))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentPantsColorSaturation(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(529, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 528 && currentComponent.myID <= 530))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentPantsColorValue(CharacterCustomization __instance) + { + SliderBar sb = getCurrentSliderBar(530, __instance)!; + if (currentComponent != null && (currentComponent.myID == 507 || (currentComponent.myID >= 528 && currentComponent.myID <= 530))) + return sb.value!.ToString(); + return ""; + } + + private static string getCurrentPetName() + { + if (currentComponent != null && currentComponent.name == "Pet") + { + return ((Game1.player.catPerson) ? "Cat" : "Dog") + " Breed: " + Game1.player.whichPetBreed; + } else { + return ""; + } + } + } +} diff --git a/stardew-access/Patches/TitleMenuPatches.cs b/stardew-access/Patches/TitleMenuPatches.cs index e9666c3..c93e2b6 100644 --- a/stardew-access/Patches/TitleMenuPatches.cs +++ b/stardew-access/Patches/TitleMenuPatches.cs @@ -1,5 +1,4 @@ using StardewValley; -using StardewValley.Characters; using StardewValley.Menus; using static StardewValley.Menus.CharacterCustomization; using static StardewValley.Menus.LoadGameMenu;