diff --git a/StardewArchipelago/Goals/GoalManager.cs b/StardewArchipelago/Goals/GoalManager.cs index b28f8616a..a2703efc4 100644 --- a/StardewArchipelago/Goals/GoalManager.cs +++ b/StardewArchipelago/Goals/GoalManager.cs @@ -20,6 +20,7 @@ public class GoalManager private readonly Harmony _harmony; private readonly StardewArchipelagoClient _archipelago; private LocationChecker _locationChecker; + private GrandpaIndicators _grandpaIndicators; public GoalManager(LogHandler logger, IModHelper modHelper, Harmony harmony, StardewArchipelagoClient archipelago, LocationChecker locationChecker) { @@ -28,11 +29,12 @@ public GoalManager(LogHandler logger, IModHelper modHelper, Harmony harmony, Sta _harmony = harmony; _archipelago = archipelago; _locationChecker = locationChecker; + _grandpaIndicators = new GrandpaIndicators(logger, modHelper, archipelago); } public void CheckGoalCompletion(bool vanillaGoal = false) { - EvaluateGrandpaToday(Game1.getFarm()); + _grandpaIndicators.EvaluateGrandpaToday(Game1.getFarm()); switch (_archipelago.SlotData.Goal) { case Goal.CommunityCenter: @@ -198,85 +200,5 @@ private void InjectPerfectionGoalMethods() postfix: new HarmonyMethod(typeof(GoalCodeInjection), nameof(GoalCodeInjection.PercentGameComplete_PerfectionGoal_Postfix)) ); } - - private void EvaluateGrandpaToday(Farm farm) - { - var score = Utility.getGrandpaScore(); - var candlesFromScore = Utility.getGrandpaCandlesFromScore(score); - farm.grandpaScore.Value = candlesFromScore; - for (var index = 0; index < candlesFromScore; ++index) - { - DelayedAction.playSoundAfterDelay("fireball", 100 * index); - } - farm.addGrandpaCandles(); - AddArchipelagoIconsForPoints(farm, score); - } - - private void AddArchipelagoIconsForPoints(Farm farm, int score) - { - var preference = ModEntry.Instance.Config.ShowGrandpaShrineIndicators; - if (preference == GrandpaShrinePreferenrce.Never) - { - return; - } - if (preference == GrandpaShrinePreferenrce.GrandpaGoal && _archipelago.SlotData.Goal != Goal.GrandpaEvaluation) - { - return; - } - - var size = 24; - var whiteArchipelagoIcon = ArchipelagoTextures.GetArchipelagoLogo(_logger, _modHelper, size, ArchipelagoTextures.WHITE); - var coloredArchipelagoIcon = ArchipelagoTextures.GetArchipelagoLogo(_logger, _modHelper, size, ArchipelagoTextures.COLOR); - - var grandpaShrinePosition = farm.GetGrandpaShrinePosition(); - var localId = 6666 + _archipelago.SlotData.Seed.GetHashCode(); - farm.removeTemporarySpritesWithIDLocal(localId); - - for (var i = 1; i <= 21; i++) - { - var texture = whiteArchipelagoIcon; - var textureName = $"{ArchipelagoTextures.WHITE}_{size}"; - if (score >= i) - { - texture = coloredArchipelagoIcon; - textureName = $"{ArchipelagoTextures.COLOR}_{size}"; - } - - var sprite = new TemporaryAnimatedSprite(); - sprite.texture = texture; - sprite.textureName = textureName; - - var sourceRect = new Rectangle(0, 0, size, size); - var column = (i - 1) % 7; - var row = (i - 1) / 7; - var positionX = ((grandpaShrinePosition.X - 2) * 64) + 50 + (column * 32); - var positionY = ((grandpaShrinePosition.Y - 3) * 64) + 8 + (row * 32); - var position = new Vector2(positionX, positionY); - - sprite.currentParentTileIndex = 0; - sprite.initialParentTileIndex = 0; - sprite.position = position; - sprite.flicker = false; - sprite.flipped = false; - - sprite.sourceRect = sourceRect; - sprite.sourceRectStartingPos = new Vector2(sourceRect.X, sourceRect.Y); - sprite.initialPosition = position; - sprite.alphaFade = 0.0f; - sprite.color = Color.White; - - sprite.interval = 50f; - sprite.totalNumberOfLoops = 99999; - sprite.animationLength = 1; - sprite.lightId = $"Farm_GrandpaArchipelagoScore_{i}"; - sprite.id = localId; - sprite.lightRadius = 1f; - sprite.scale = 1f; - sprite.layerDepth = 0.03f; - sprite.delayBeforeAnimationStart = i * 50; - - farm.temporarySprites.Add(sprite); - } - } } } diff --git a/StardewArchipelago/Goals/GrandpaIndicators.cs b/StardewArchipelago/Goals/GrandpaIndicators.cs new file mode 100644 index 000000000..50c64ef48 --- /dev/null +++ b/StardewArchipelago/Goals/GrandpaIndicators.cs @@ -0,0 +1,267 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using HarmonyLib; +using Microsoft.Xna.Framework; +using StardewArchipelago.Archipelago; +using StardewArchipelago.Logging; +using StardewArchipelago.Textures; +using StardewModdingAPI; +using StardewModdingAPI.Events; +using StardewModdingAPI.Utilities; +using StardewValley; +using StardewValley.Buildings; +using StardewValley.ItemTypeDefinitions; +using StardewValley.Menus; +using StardewValley.Objects; +using StardewValley.TerrainFeatures; +using StardewValley.TokenizableStrings; +using Object = System.Object; + +namespace StardewArchipelago.Goals +{ + public class GrandpaIndicators + { + private const int INDICATOR_SIZE = 24; + + private Vector2 _currentMousePosition; + + private readonly LogHandler _logger; + private readonly IModHelper _modHelper; + private readonly StardewArchipelagoClient _archipelago; + + public GrandpaIndicators(LogHandler logger, IModHelper modHelper, StardewArchipelagoClient archipelago) + { + _logger = logger; + _modHelper = modHelper; + _archipelago = archipelago; + + RegisterEventHandlers(); + } + + private void RegisterEventHandlers() + { + var preference = ModEntry.Instance.Config.ShowGrandpaShrineIndicators; + if (preference == GrandpaShrinePreference.Never) + { + return; + } + if (preference == GrandpaShrinePreference.GrandpaGoal && _archipelago.SlotData.Goal != Goal.GrandpaEvaluation) + { + return; + } + _modHelper.Events.Display.RenderingHud += OnRenderingHud; + _modHelper.Events.GameLoop.UpdateTicked += OnUpdateTicked; + } + + public void EvaluateGrandpaToday(Farm farm) + { + var score = Utility.getGrandpaScore(); + var candlesFromScore = Utility.getGrandpaCandlesFromScore(score); + farm.grandpaScore.Value = candlesFromScore; + for (var index = 0; index < candlesFromScore; ++index) + { + DelayedAction.playSoundAfterDelay("fireball", 100 * index); + } + farm.addGrandpaCandles(); + AddArchipelagoIconsForPoints(farm); + } + + private void AddArchipelagoIconsForPoints(Farm farm) + { + var preference = ModEntry.Instance.Config.ShowGrandpaShrineIndicators; + if (preference == GrandpaShrinePreference.Never) + { + return; + } + if (preference == GrandpaShrinePreference.GrandpaGoal && _archipelago.SlotData.Goal != Goal.GrandpaEvaluation) + { + return; + } + + var whiteArchipelagoIcon = ArchipelagoTextures.GetArchipelagoLogo(_logger, _modHelper, INDICATOR_SIZE, ArchipelagoTextures.WHITE); + var coloredArchipelagoIcon = ArchipelagoTextures.GetArchipelagoLogo(_logger, _modHelper, INDICATOR_SIZE, ArchipelagoTextures.COLOR); + + var grandpaShrinePosition = farm.GetGrandpaShrinePosition(); + var localId = 6666 + _archipelago.SlotData.Seed.GetHashCode(); + farm.removeTemporarySpritesWithIDLocal(localId); + + for (var i = 0; i < 21; i++) + { + var texture = whiteArchipelagoIcon; + var textureName = $"{ArchipelagoTextures.WHITE}_{INDICATOR_SIZE}"; + if (_indicatorTestMethods[i]()) + { + texture = coloredArchipelagoIcon; + textureName = $"{ArchipelagoTextures.COLOR}_{INDICATOR_SIZE}"; + } + + var sprite = new TemporaryAnimatedSprite(); + sprite.texture = texture; + sprite.textureName = textureName; + + var sourceRect = new Rectangle(0, 0, INDICATOR_SIZE, INDICATOR_SIZE); + var column = i % 7; + var row = i / 7; + var position = GetIndicatorPosition(grandpaShrinePosition, column, row); + + sprite.currentParentTileIndex = 0; + sprite.initialParentTileIndex = 0; + sprite.position = position; + sprite.flicker = false; + sprite.flipped = false; + + sprite.sourceRect = sourceRect; + sprite.sourceRectStartingPos = new Vector2(sourceRect.X, sourceRect.Y); + sprite.initialPosition = position; + sprite.alphaFade = 0.0f; + sprite.color = Color.White; + + sprite.interval = 50f; + sprite.totalNumberOfLoops = 99999; + sprite.animationLength = 1; + sprite.lightId = $"Farm_GrandpaArchipelagoScore_{i}"; + sprite.id = localId; + sprite.lightRadius = 1f; + sprite.scale = 1f; + sprite.layerDepth = 0.03f; + sprite.delayBeforeAnimationStart = i * 50; + + farm.temporarySprites.Add(sprite); + } + } + + private static Vector2 GetIndicatorPosition(Point grandpaShrinePosition, int column, int row) + { + var positionX = ((grandpaShrinePosition.X - 2) * 64) + 50 + (column * 32); + var positionY = ((grandpaShrinePosition.Y - 3) * 64) + 8 + (row * 32); + var position = new Vector2(positionX, positionY); + return position; + } + + private static bool TryGetIndicatorIndex(Point grandpaShrinePosition, Vector2 position, out int index) + { + index = -1; + + var originX = ((grandpaShrinePosition.X - 2) * 64) + 50; + var originY = ((grandpaShrinePosition.Y - 3) * 64) + 8; + var width = (7 * 32) + 24; + var height = (3 * 32) + 24; + if (position.X < originX || position.X > originX + width || position.Y < originY || position.Y > originY + height) + { + return false; + } + + var offsetPositionX = position.X - originX; + var offsetPositionY = position.Y - originY; + if (offsetPositionX % 32 > 24 || offsetPositionY % 32 > 24) + { + return false; + } + + var column = (int)Math.Floor(offsetPositionX / 32); + var row = (int)Math.Floor(offsetPositionY / 32); + + index = (row * 7) + column; + return true; + } + + /// Raised after the game state is updated (≈60 times per second). + /// The event sender. + /// The event arguments. + private void OnUpdateTicked(object? sender, UpdateTickedEventArgs e) + { + if (!e.IsMultipleOf(4)) + { + return; + } + + if (Game1.currentLocation is not Farm) + { + _currentMousePosition = new Vector2(-1, -1); + return; + } + + _currentMousePosition = new Vector2(Game1.viewport.X + Game1.getOldMouseX(), Game1.viewport.Y + Game1.getOldMouseY()); + } + + /// + /// Raised before drawing the HUD (item toolbar, clock, etc) to the screen. The vanilla HUD may be hidden at this + /// point (e.g. because a menu is open). + /// + /// The event sender. + /// The event arguments. + private void OnRenderingHud(object? sender, RenderingHudEventArgs e) + { + if (Game1.activeClickableMenu != null) + { + return; + } + + if (!TryGetIndicatorIndex(Game1.getFarm().GetGrandpaShrinePosition(), _currentMousePosition, out var indicatorIndex)) + { + return; + } + + IClickableMenu.drawHoverText( + Game1.spriteBatch, + string.Join('\n', _indicatorDescriptions[indicatorIndex]), + Game1.smallFont, + overrideX: -1, + overrideY: -1 + ); + } + + private readonly Dictionary _indicatorDescriptions = new() + { + { 0, "50,000g" }, + { 1, "100,000g" }, + { 2, "200,000g" }, + { 3, "300,000g" }, + { 4, "500,000g" }, + { 5, "1,000,000g" }, + { 6, "1,000,000g" }, + { 7, "30 Levels Total" }, + { 8, "50 Levels Total" }, + { 9, "Complete Collection" }, + { 10, "Master Angler" }, + { 11, "Full Shipment" }, + { 12, "Married with two house upgrades" }, + { 13, "5 good friends" }, + { 14, "10 good friends" }, + { 15, "Pet loves you" }, + { 16, "Community Center" }, + { 17, "Community Center Ceremony" }, + { 18, "Community Center Ceremony" }, + { 19, "Skull Key" }, + { 20, "Rusty Key" }, + }; + + private readonly Dictionary> _indicatorTestMethods = new() + { + { 0, () => Game1.player.totalMoneyEarned >= 50000U }, + { 1, () => Game1.player.totalMoneyEarned >= 100000U }, + { 2, () => Game1.player.totalMoneyEarned >= 200000U }, + { 3, () => Game1.player.totalMoneyEarned >= 300000U }, + { 4, () => Game1.player.totalMoneyEarned >= 500000U }, + { 5, () => Game1.player.totalMoneyEarned >= 1000000U }, + { 6, () => Game1.player.totalMoneyEarned >= 1000000U }, + { 7, () => Game1.player.Level >= 15 }, + { 8, () => Game1.player.Level >= 25 }, + { 9, () => Game1.player.achievements.Contains(5) }, + { 10, () => Game1.player.achievements.Contains(26) }, + { 11, () => Game1.player.achievements.Contains(34) }, + { 12, () => Game1.player.isMarriedOrRoommates() && Utility.getHomeOfFarmer(Game1.player).upgradeLevel >= 2 }, + { 13, () => Utility.getNumberOfFriendsWithinThisRange(Game1.player, 1975, 999999) >= 5 }, + { 14, () => Utility.getNumberOfFriendsWithinThisRange(Game1.player, 1975, 999999) >= 10 }, + { 15, () => Game1.player.mailReceived.Contains("petLoveMessage") }, + { 16, () => Game1.player.hasCompletedCommunityCenter() }, + { 17, () => Utility.HasAnyPlayerSeenEvent("191393") }, + { 18, () => Utility.HasAnyPlayerSeenEvent("191393") }, + { 19, () => Game1.player.hasSkullKey }, + { 20, () => Game1.player.hasRustyKey }, + }; + } +} \ No newline at end of file diff --git a/StardewArchipelago/Integrations/GenericModConfigMenu/ConfigMenu.cs b/StardewArchipelago/Integrations/GenericModConfigMenu/ConfigMenu.cs index af2818a09..55a5ff903 100644 --- a/StardewArchipelago/Integrations/GenericModConfigMenu/ConfigMenu.cs +++ b/StardewArchipelago/Integrations/GenericModConfigMenu/ConfigMenu.cs @@ -261,7 +261,7 @@ public void RegisterConfig() formatValue: (value) => ((SeasonPreference)value).ToString() ); - var grandpaShrinePreferenceValues = Enum.GetValues(typeof(GrandpaShrinePreferenrce)).Cast().ToArray(); + var grandpaShrinePreferenceValues = Enum.GetValues(typeof(GrandpaShrinePreference)).Cast().ToArray(); var grandpaShrinePreferenceMin = grandpaShrinePreferenceValues.Min(); var grandpaShrinePreferenceMax = grandpaShrinePreferenceValues.Max(); configMenu.AddNumberOption( @@ -272,8 +272,8 @@ public void RegisterConfig() max: grandpaShrinePreferenceMax, interval: 1, getValue: () => (int)Config.ShowGrandpaShrineIndicators, - setValue: (value) => Config.ShowGrandpaShrineIndicators = (GrandpaShrinePreferenrce)value, - formatValue: (value) => ((GrandpaShrinePreferenrce)value).ToString() + setValue: (value) => Config.ShowGrandpaShrineIndicators = (GrandpaShrinePreference)value, + formatValue: (value) => ((GrandpaShrinePreference)value).ToString() ); } } diff --git a/StardewArchipelago/ModConfig.cs b/StardewArchipelago/ModConfig.cs index 02c4072bb..8d4c9b553 100644 --- a/StardewArchipelago/ModConfig.cs +++ b/StardewArchipelago/ModConfig.cs @@ -91,7 +91,7 @@ public class ModConfig /// /// Whether to display archipelago icons on grandpa's shrine to see current points /// - public GrandpaShrinePreferenrce ShowGrandpaShrineIndicators { get; set; } = GrandpaShrinePreferenrce.GrandpaGoal; + public GrandpaShrinePreference ShowGrandpaShrineIndicators { get; set; } = GrandpaShrinePreference.GrandpaGoal; } public enum ItemIndicatorPreference @@ -108,7 +108,7 @@ public enum SeasonPreference Cycle = 2, } - public enum GrandpaShrinePreferenrce + public enum GrandpaShrinePreference { Never = 0, GrandpaGoal = 1,