diff --git a/UIInfoSuite2/Compatibility/ApiManager.cs b/UIInfoSuite2/Compatibility/ApiManager.cs index 3486c14a..7fa6052e 100644 --- a/UIInfoSuite2/Compatibility/ApiManager.cs +++ b/UIInfoSuite2/Compatibility/ApiManager.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using StardewModdingAPI; @@ -9,6 +10,7 @@ public static class ModCompat public const string CustomBush = "furyx639.CustomBush"; public const string Gmcm = "spacechase0.GenericModConfigMenu"; public const string DeluxeJournal = "MolsonCAD.DeluxeJournal"; + public const string BetterGameMenu = "leclair.bettergamemenu"; } public static class ApiManager @@ -37,7 +39,17 @@ public static class ApiManager return null; } - var api = helper.ModRegistry.GetApi(modId); + T? api; + try + { + // This can throw if the API cannot be mapped to type T by Pintail. + api = helper.ModRegistry.GetApi(modId); + } catch (Exception ex) + { + ModEntry.MonitorObject.Log($"Could not get API for mod {modId} due to error: {ex}", LogLevel.Warn); + api = null; + } + if (api is null) { if (warnIfNotPresent) diff --git a/UIInfoSuite2/Compatibility/IBetterGameMenu.cs b/UIInfoSuite2/Compatibility/IBetterGameMenu.cs new file mode 100644 index 00000000..9b604591 --- /dev/null +++ b/UIInfoSuite2/Compatibility/IBetterGameMenu.cs @@ -0,0 +1,293 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +using StardewModdingAPI.Events; + +using StardewValley; +using StardewValley.Menus; + +namespace Leclair.Stardew.BetterGameMenu; + + +/// +/// A page created event is emitted whenever a new page +/// is created for a tab by Better Game Menu. +/// +public interface IPageCreatedEvent +{ + + /// + /// The Better Game Menu instance involved in the event. You + /// can use + /// to get a more useful interface for this menu. + /// + IClickableMenu Menu { get; } + + /// + /// The id of the tab the page was created for. + /// + string Tab { get; } + + /// + /// The id of the provider the page was created with. + /// + string Source { get; } + + /// + /// The new page that was just created. + /// + IClickableMenu Page { get; } + + /// + /// If the page was previously created and is being replaced, + /// this will be the old page instance. Otherwise, this will + /// be null. + /// + IClickableMenu? OldPage { get; } + +} + + +/// +/// This interface represents a Better Game Menu. +/// +public interface IBetterGameMenu +{ + /// + /// The instance for this game menu. This is + /// the same object, but with a different type. This property is included + /// for convenience due to how API proxying works. + /// + IClickableMenu Menu { get; } + + /// + /// Whether or not the menu is currently drawing itself. This is typically + /// always false except when viewing the Map tab. + /// + bool Invisible { get; set; } + + /// + /// A list of ids of the currently visible tabs. + /// + IReadOnlyList VisibleTabs { get; } + + /// + /// The id of the currently active tab. + /// + string CurrentTab { get; } + + /// + /// The instance for the currently active tab. + /// This may be null if the page instance for the currently active + /// tab is still being initialized. + /// + IClickableMenu? CurrentPage { get; } + + /// + /// Whether or not the currently displayed page is an error page. Error + /// pages are used when a tab implementation's GetPageInstance method + /// throws an exception. + /// + bool CurrentTabHasErrored { get; } + + /// + /// Try to get the source for the specific tab. + /// + /// The id of the tab to get the source of. + /// The unique ID of the mod that registered the + /// implementation being used, or stardew if the base game's + /// implementation is being used. + /// Whether or not the tab is registered with the system. + bool TryGetSource(string target, [NotNullWhen(true)] out string? source); + + /// + /// Try to get the instance for a specific tab. + /// + /// The id of the tab to get the page for. + /// The page instance, if one exists. + /// If set to true, an instance will attempt to + /// be created if one has not already been created. + /// Whether or not a page instance for that tab exists. + bool TryGetPage(string target, [NotNullWhen(true)] out IClickableMenu? page, bool forceCreation = false); + + /// + /// Attempt to change the currently active tab to the target tab. + /// + /// The id of the tab to change to. + /// Whether or not to play a sound. + /// Whether or not the tab was changed successfully. + bool TryChangeTab(string target, bool playSound = true); + + /// + /// Force the menu to recalculate the visible tabs. This will not recreate + /// instances, but can be used to cause an + /// inactive tab to be removed, or a previously hidden tab to be added. + /// This can also be used to update tab decorations if necessary. + /// + /// Optionally, a specific tab to update rather than + /// updating all tabs. + void UpdateTabs(string? target = null); + +} + + +/// +/// This enum is included for reference and has the order value for +/// all the default tabs from the base game. These values are intentionally +/// spaced out to allow for modded tabs to be inserted at specific points. +/// +public enum BetterGameMenuTabs +{ + Inventory = 0, + Skills = 20, + Social = 40, + Map = 60, + Crafting = 80, + Animals = 100, + Powers = 120, + Collections = 140, + Options = 160, + Exit = 200 +} + + +public interface IBetterGameMenuApi +{ + + /// + /// A delegate for drawing something onto the screen. + /// + /// The to draw with. + /// The region where the thing should be drawn. + public delegate void DrawDelegate(SpriteBatch batch, Rectangle bounds); + + #region Helpers + + /// + /// Create a draw delegate that draws the provided texture to the + /// screen. This supports basic animations if required. + /// + /// The texture to draw from. + /// The source rectangle to draw. + /// The scale to draw the source at. + /// The number of frames to draw. + /// The amount of time each frame should be displayed. + DrawDelegate CreateDraw(Texture2D texture, Rectangle source, float scale = 1f, int frames = 1, int frameTime = 16); + + #endregion + + #region Tab Registration + + /// + /// Register a new tab with the system. + /// + /// The id of the tab to add. + /// The order of this tab relative to other tabs. + /// See for an example of existing values. + /// A method that returns the display name of + /// this tab, to be displayed in a tool-tip to the user. + /// A method that returns an icon to be displayed + /// on the tab UI for this tab, expecting both a texture and a Rectangle. + /// The priority of the default page instance + /// provider for this tab. When multiple page instance providers are + /// registered, and the user hasn't explicitly chosen one, then the + /// one with the highest priority is used. Please note that a given + /// mod can only register one provider for any given tab. + /// A method that returns a page instance + /// for the tab. This should never return a null value. + /// A method that returns a decoration for + /// the tab UI for this tab. This can be used to, for example, add a + /// sparkle to a tab to indicate that new content is available. The + /// expected output is either null if no decoration should be + /// displayed, or a texture, rectangle, number of animation frames + /// to display, and delay between frame advancements. Please note that + /// the decoration will be automatically cleared when the user navigates + /// to the tab. + /// A method that returns whether or not the + /// tab should be visible in the menu. This is called whenever a menu is + /// opened, as well as when + /// is called. + /// A method that returns the value that the + /// game menu should set its flag + /// to when this is the active tab. + /// A method that returns a specific width to use when + /// rendering this tab, in case the page instance requires a different width + /// than the standard value. + /// A method that returns a specific height to use + /// when rendering this tab, in case the page instance requires a different + /// height than the standard value. + /// A method that is called when the game window is + /// resized, in addition to the standard . + /// This can be used to recreate a menu page if necessary by returning a + /// new instance. Several menus in the vanilla + /// game use this logic. + /// A method that is called whenever a page instance + /// is cleaned up. The standard Game Menu doesn't call + /// of its pages, and only calls + /// of the currently active tab, and we're keeping that behavior for + /// compatibility. This method will always be called. This includes calling + /// it for menus that were replaced by the onResize method. + void RegisterTab( + string id, + int order, + Func getDisplayName, + Func<(DrawDelegate DrawMethod, bool DrawBackground)> getIcon, + int priority, + Func getPageInstance, + Func? getDecoration = null, + Func? getTabVisible = null, + Func? getMenuInvisible = null, + Func? getWidth = null, + Func? getHeight = null, + Func<(IClickableMenu Menu, IClickableMenu OldPage), IClickableMenu?>? onResize = null, + Action? onClose = null + ); + + #endregion + + #region Menu Class Access + + /// + /// The active screen's current Better Game Menu, if one is open, + /// else null. + /// + IBetterGameMenu? ActiveMenu { get; } + + /// + /// Attempt to cast the provided menu into an . + /// This can be useful if you're working with a menu that isn't currently + /// assigned to . + /// + /// The menu to attempt to cast + IBetterGameMenu? AsMenu(IClickableMenu menu); + + #endregion + + #region Menu Events + + public delegate void PageCreatedDelegate(IPageCreatedEvent evt); + + /// + /// This event fires whenever a new page instance is created. This can happen + /// the first time a page is accessed, whenever something calls + /// with the + /// forceCreation flag set to true, or when the menu has been resized + /// and the tab implementation's OnResize method returned a new + /// page instance. + /// + void OnPageCreated(PageCreatedDelegate handler, EventPriority priority = EventPriority.Normal); + + /// + /// Unregister a handler for the PageCreated event. + /// + void OffPageCreated(PageCreatedDelegate handler); + + #endregion + +} diff --git a/UIInfoSuite2/Infrastructure/Tools.cs b/UIInfoSuite2/Infrastructure/Tools.cs index 39e2afa0..4365190d 100644 --- a/UIInfoSuite2/Infrastructure/Tools.cs +++ b/UIInfoSuite2/Infrastructure/Tools.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; + +using Leclair.Stardew.BetterGameMenu; + using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using StardewValley; @@ -10,6 +13,9 @@ using StardewValley.Menus; using StardewValley.TerrainFeatures; using StardewValley.WorldMaps; + +using UIInfoSuite2.Compatibility; + using SObject = StardewValley.Object; namespace UIInfoSuite2.Infrastructure; @@ -87,21 +93,42 @@ public static void DrawMouseCursor() } } + public static IClickableMenu? GetCurrentMenuPage() + { + if (Game1.activeClickableMenu is GameMenu gameMenu) + { + return gameMenu.GetCurrentPage(); + } + if (ApiManager.GetApi(ModCompat.BetterGameMenu, out var bgm)) + { + return bgm.ActiveMenu?.CurrentPage; + } + return null; + } + + public static bool IsGameMenuOpen() + { + return Game1.activeClickableMenu is GameMenu || (ApiManager.GetApi(ModCompat.BetterGameMenu, out var bgm) && bgm.ActiveMenu != null); + } + public static Item? GetHoveredItem() { Item? hoverItem = null; + var page = GetCurrentMenuPage(); if (Game1.activeClickableMenu == null && Game1.onScreenMenus != null) { hoverItem = Game1.onScreenMenus.OfType().Select(tb => tb.hoverItem).FirstOrDefault(hi => hi is not null); } - - if (Game1.activeClickableMenu is GameMenu gameMenu && gameMenu.GetCurrentPage() is InventoryPage inventory) + else if (page is InventoryPage inventory) { hoverItem = inventory.hoveredItem; } - - if (Game1.activeClickableMenu is ItemGrabMenu itemMenu) + else if (page is CraftingPage crafting) + { + hoverItem = crafting.hoverItem; + } + else if (Game1.activeClickableMenu is ItemGrabMenu itemMenu) { hoverItem = itemMenu.hoveredItem; } diff --git a/UIInfoSuite2/ModEntry.cs b/UIInfoSuite2/ModEntry.cs index 1061979c..9b850141 100644 --- a/UIInfoSuite2/ModEntry.cs +++ b/UIInfoSuite2/ModEntry.cs @@ -1,4 +1,7 @@ using System; + +using Leclair.Stardew.BetterGameMenu; + using StardewModdingAPI; using StardewModdingAPI.Events; using StardewValley; @@ -49,6 +52,7 @@ public override void Entry(IModHelper helper) private void OnGameLaunched(object sender, GameLaunchedEventArgs e) { SoundHelper.Instance.Initialize(Helper); + RegisterBetterGameMenuTab(); // get Generic Mod Config Menu's API (if it's installed) var configMenu = ApiManager.TryRegisterApi(Helper, ModCompat.Gmcm, "1.6.0"); @@ -112,9 +116,45 @@ private void OnGameLaunched(object sender, GameLaunchedEventArgs e) setValue: value => _modConfig.ShowAllRange = value ); } -#endregion + #endregion + + #region Better Game Menu + + private void RegisterBetterGameMenuTab() + { + var api = ApiManager.TryRegisterApi(Helper, ModCompat.BetterGameMenu, "0.1.0"); + if (api is null) + return; + + api.RegisterTab( + id: ModManifest.UniqueID, + order: (int) BetterGameMenuTabs.Exit + 100, + getDisplayName: I18n.OptionsTabTooltip, + getIcon: () => (api.CreateDraw(Game1.mouseCursors, new Microsoft.Xna.Framework.Rectangle(32, 672, 16, 16), 3f), true), + priority: 0, + getPageInstance: menu => _modOptionsPageHandler.GetMenuInstance(), + getTabVisible: () => _modConfig.ShowOptionsTabInMenu && _modOptionsPageHandler != null, + onResize: input => + { + ModOptionsPageState? state = null; + if (input.OldPage is ModOptionsPage mop) + { + state = new(); + mop.SaveState(state); + } + + var result = _modOptionsPageHandler.GetMenuInstance(); + if (state is not null) + result.LoadState(state); + + return result; + } + ); + } + + #endregion -#region Event subscriptions + #region Event subscriptions private void OnReturnedToTitle(object sender, ReturnedToTitleEventArgs e) { // Unload if the main player quits. diff --git a/UIInfoSuite2/Options/ModOptionsPageHandler.cs b/UIInfoSuite2/Options/ModOptionsPageHandler.cs index 674f0295..4c4c08e6 100644 --- a/UIInfoSuite2/Options/ModOptionsPageHandler.cs +++ b/UIInfoSuite2/Options/ModOptionsPageHandler.cs @@ -1,12 +1,17 @@ using System; using System.Collections.Generic; using System.Reflection; + +using Leclair.Stardew.BetterGameMenu; + using Microsoft.Xna.Framework; using StardewModdingAPI; using StardewModdingAPI.Events; using StardewModdingAPI.Utilities; using StardewValley; using StardewValley.Menus; + +using UIInfoSuite2.Compatibility; using UIInfoSuite2.Infrastructure; using UIInfoSuite2.Infrastructure.Extensions; using UIInfoSuite2.UIElements; @@ -455,6 +460,11 @@ private void OnUpdateTicking(object? sender, EventArgs e) } } + internal ModOptionsPage GetMenuInstance() + { + return new ModOptionsPage(_optionsElements, _helper.Events); + } + private void OnUpdateTicked(object? sender, EventArgs e) { var gameMenu = Game1.activeClickableMenu as GameMenu; diff --git a/UIInfoSuite2/UIElements/LocationOfTownsfolk.cs b/UIInfoSuite2/UIElements/LocationOfTownsfolk.cs index 0b5a6426..19e9f049 100644 --- a/UIInfoSuite2/UIElements/LocationOfTownsfolk.cs +++ b/UIInfoSuite2/UIElements/LocationOfTownsfolk.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Leclair.Stardew.BetterGameMenu; + using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI; @@ -10,6 +12,8 @@ using StardewValley.Menus; using StardewValley.Quests; using StardewValley.WorldMaps; + +using UIInfoSuite2.Compatibility; using UIInfoSuite2.Infrastructure; using UIInfoSuite2.Infrastructure.Extensions; using UIInfoSuite2.Options; @@ -55,6 +59,13 @@ public void ToggleShowNPCLocationsOnMap(bool showLocations) _helper.Events.Input.ButtonPressed += OnButtonPressed_ForSocialPage; _helper.Events.GameLoop.UpdateTicked += OnUpdateTicked; } + + if (ApiManager.GetApi(ModCompat.BetterGameMenu, out var bgm)) + { + bgm.OffPageCreated(BetterGameMenu_OnPageCreated); + if (showLocations) + bgm.OnPageCreated(BetterGameMenu_OnPageCreated); + } } public void Dispose() @@ -69,9 +80,15 @@ private void OnMenuChanged(object? sender, MenuChangedEventArgs e) InitializeProperties(); } + private void BetterGameMenu_OnPageCreated(IPageCreatedEvent e) + { + if (e.Page is SocialPage) + InitializeProperties(); + } + private void OnButtonPressed_ForSocialPage(object? sender, ButtonPressedEventArgs e) { - if (Game1.activeClickableMenu is GameMenu && + if (Tools.GetCurrentMenuPage() is SocialPage && e.Button is SButton.MouseLeft or SButton.ControllerA or SButton.ControllerX) { CheckSelectedBox(e); @@ -80,7 +97,7 @@ private void OnButtonPressed_ForSocialPage(object? sender, ButtonPressedEventArg private void OnRenderedActiveMenu_DrawSocialPageOptions(object? sender, RenderedActiveMenuEventArgs e) { - if (Game1.activeClickableMenu is GameMenu gameMenu && gameMenu.currentTab == GameMenu.socialTab) + if (Tools.GetCurrentMenuPage() is SocialPage) { DrawSocialPageOptions(); } @@ -88,9 +105,9 @@ private void OnRenderedActiveMenu_DrawSocialPageOptions(object? sender, Rendered private void OnRenderedActiveMenu_DrawNPCLocationsOnMap(object? sender, RenderedActiveMenuEventArgs e) { - if (Game1.activeClickableMenu is GameMenu gameMenu && gameMenu.currentTab == GameMenu.mapTab) + if (Tools.GetCurrentMenuPage() is MapPage mapPage) { - DrawNPCLocationsOnMap(gameMenu); + DrawNPCLocationsOnMap(mapPage); } } @@ -124,25 +141,40 @@ private void OnUpdateTicked(object? sender, UpdateTickedEventArgs e) } } } -#endregion + #endregion + + #region Logic + private static SocialPage? GetSocialPage() + { + if (Game1.activeClickableMenu is GameMenu gm) + { + foreach (var page in gm.pages) + { + if (page is SocialPage socialPage) + return socialPage; + } + } + + if (ApiManager.GetApi(ModCompat.BetterGameMenu, out var bgm) && + bgm.ActiveMenu != null && + bgm.ActiveMenu.TryGetPage(nameof(BetterGameMenuTabs.Social), out var sp) + ) + return sp as SocialPage; + + return null; + } -#region Logic private void InitializeProperties() { - if (Game1.activeClickableMenu is GameMenu gameMenu) + if (Tools.IsGameMenuOpen()) { _friendNames.Clear(); - foreach (IClickableMenu? menu in gameMenu.pages) - { - if (menu is SocialPage socialPage) + var socialPage = GetSocialPage(); + if (socialPage != null) { + _socialPage = socialPage; + foreach (SocialPage.SocialEntry? SocialEntries in socialPage.SocialEntries) { - _socialPage = socialPage; - foreach (SocialPage.SocialEntry? SocialEntries in socialPage.SocialEntries) - { - _friendNames.Add(SocialEntries.InternalName); - } - - break; + _friendNames.Add(SocialEntries.InternalName); } } @@ -266,7 +298,7 @@ private void DrawSocialPageOptions() } } - private void DrawNPCLocationsOnMap(GameMenu gameMenu) + private void DrawNPCLocationsOnMap(MapPage mapPage) { var namesToShow = new List(); foreach (NPC character in _townsfolk) @@ -293,7 +325,7 @@ private void DrawNPCLocationsOnMap(GameMenu gameMenu) //The cursor needs to show up in front of the character faces Tools.DrawMouseCursor(); - string? hoverText = ((MapPage)gameMenu.pages[gameMenu.currentTab]).hoverText; + string? hoverText = mapPage.hoverText; IClickableMenu.drawHoverText(Game1.spriteBatch, hoverText, Game1.smallFont); } diff --git a/UIInfoSuite2/UIElements/ShowAccurateHearts.cs b/UIInfoSuite2/UIElements/ShowAccurateHearts.cs index a0f1b27f..e51cf19d 100644 --- a/UIInfoSuite2/UIElements/ShowAccurateHearts.cs +++ b/UIInfoSuite2/UIElements/ShowAccurateHearts.cs @@ -1,19 +1,24 @@ using System; + +using Leclair.Stardew.BetterGameMenu; + using Microsoft.Xna.Framework; using StardewModdingAPI.Events; using StardewValley; using StardewValley.Menus; +using UIInfoSuite2.Compatibility; +using UIInfoSuite2.Infrastructure; + namespace UIInfoSuite2.UIElements; internal class ShowAccurateHearts : IDisposable { #region Properties - private SocialPage? _socialPage; private readonly IModEvents _events; // @formatter:off - private readonly int[][] _numArray = + private static readonly int[][] _numArray = { new[] { 1, 1, 0, 1, 1 }, new[] { 1, 1, 1, 1, 1 }, @@ -36,69 +41,46 @@ public void Dispose() public void ToggleOption(bool showAccurateHearts) { - _events.Display.MenuChanged -= OnMenuChanged; _events.Display.RenderedActiveMenu -= OnRenderedActiveMenu; if (showAccurateHearts) { - _events.Display.MenuChanged += OnMenuChanged; _events.Display.RenderedActiveMenu += OnRenderedActiveMenu; } } #endregion #region Event subscriptions - private void OnRenderedActiveMenu(object sender, RenderedActiveMenuEventArgs e) + private void OnRenderedActiveMenu(object? sender, RenderedActiveMenuEventArgs e) { - if (_socialPage == null) + if (Tools.GetCurrentMenuPage() is not SocialPage socialPage) { - GetSocialPage(); return; } - if (Game1.activeClickableMenu is GameMenu gameMenu && gameMenu.currentTab == 2) - { - DrawHeartFills(); + DrawHeartFills(socialPage); + if (Game1.activeClickableMenu is GameMenu gameMenu) + { string hoverText = gameMenu.hoverText; IClickableMenu.drawHoverText(Game1.spriteBatch, hoverText, Game1.smallFont); } } - - private void OnMenuChanged(object sender, MenuChangedEventArgs e) - { - GetSocialPage(); - } #endregion #region Logic - private void GetSocialPage() - { - if (Game1.activeClickableMenu is GameMenu gameMenu) - { - foreach (IClickableMenu? menu in gameMenu.pages) - { - if (menu is SocialPage page) - { - _socialPage = page; - break; - } - } - } - } - - private void DrawHeartFills() + private static void DrawHeartFills(SocialPage? socialPage) { - if (_socialPage == null) + if (socialPage == null) { return; } - var yOffset = 0; + int yOffset = 0; - for (int i = _socialPage.slotPosition; i < _socialPage.slotPosition + 5 && i < _socialPage.SocialEntries.Count; ++i) + for (int i = socialPage.slotPosition; i < socialPage.slotPosition + 5 && i < socialPage.SocialEntries.Count; ++i) { - string internalName = _socialPage.SocialEntries[i].InternalName; + string internalName = socialPage.SocialEntries[i].InternalName; if (Game1.player.friendshipData.TryGetValue(internalName, out Friendship friendshipValues) && friendshipValues.Points > 0 && friendshipValues.Points < @@ -114,9 +96,9 @@ private void DrawHeartFills() } } - private void DrawEachIndividualSquare(int friendshipLevel, int friendshipPoints, int yPosition) + private static void DrawEachIndividualSquare(int friendshipLevel, int friendshipPoints, int yPosition) { - var numberOfPointsToDraw = (int)(friendshipPoints / 12.5); + int numberOfPointsToDraw = (int)(friendshipPoints / 12.5); int num2; if (friendshipLevel >= 10) @@ -129,9 +111,9 @@ private void DrawEachIndividualSquare(int friendshipLevel, int friendshipPoints, num2 = 32 * friendshipLevel; } - for (var i = 3; i >= 0 && numberOfPointsToDraw > 0; --i) + for (int i = 3; i >= 0 && numberOfPointsToDraw > 0; --i) { - for (var j = 0; j < 5 && numberOfPointsToDraw > 0; ++j, --numberOfPointsToDraw) + for (int j = 0; j < 5 && numberOfPointsToDraw > 0; ++j, --numberOfPointsToDraw) { if (_numArray[i][j] == 1) { diff --git a/UIInfoSuite2/UIElements/ShowCalendarAndBillboardOnGameMenuButton.cs b/UIInfoSuite2/UIElements/ShowCalendarAndBillboardOnGameMenuButton.cs index bf998deb..ef4a3142 100644 --- a/UIInfoSuite2/UIElements/ShowCalendarAndBillboardOnGameMenuButton.cs +++ b/UIInfoSuite2/UIElements/ShowCalendarAndBillboardOnGameMenuButton.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; using System.IO; + +using Leclair.Stardew.BetterGameMenu; + using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI; @@ -8,6 +11,8 @@ using StardewModdingAPI.Utilities; using StardewValley; using StardewValley.Menus; + +using UIInfoSuite2.Compatibility; using UIInfoSuite2.Infrastructure; namespace UIInfoSuite2.UIElements; @@ -56,28 +61,18 @@ public void ToggleOption(bool showCalendarAndBillboard) _helper.Events.GameLoop.UpdateTicked += OnUpdateTicked; } } -#endregion - + #endregion -#region Event subscriptions - private void OnUpdateTicked(object sender, EventArgs e) + #region Event subscriptions + private void OnUpdateTicked(object? sender, EventArgs e) { // Get hovered and hold item _hoverItem.Value = Tools.GetHoveredItem(); - if (Game1.activeClickableMenu is not GameMenu gameMenu) - { - return; - } - - List menuList = gameMenu.pages; - - if (menuList[0] is InventoryPage) - { + if (Tools.GetCurrentMenuPage() is InventoryPage) _heldItem.Value = Game1.player.CursorSlotItem; - } } - private void OnButtonPressed(object sender, ButtonPressedEventArgs e) + private void OnButtonPressed(object? sender, ButtonPressedEventArgs e) { if (e.Button == SButton.MouseLeft) { @@ -89,13 +84,12 @@ private void OnButtonPressed(object sender, ButtonPressedEventArgs e) } } - private void OnRenderedActiveMenu(object sender, EventArgs e) + private void OnRenderedActiveMenu(object? sender, EventArgs e) { if (_hoverItem.Value == null && - Game1.activeClickableMenu is GameMenu gameMenu && - gameMenu.currentTab == 0 && + Tools.GetCurrentMenuPage() is InventoryPage && _heldItem.Value == null && - gameMenu.GetChildMenu() == null) + Game1.activeClickableMenu.GetChildMenu() == null) { DrawBillboard(); } @@ -131,8 +125,7 @@ private void DrawBillboard() private void ActivateBillboard() { - if (Game1.activeClickableMenu is GameMenu gameMenu && - gameMenu.currentTab == 0 && + if (Tools.GetCurrentMenuPage() is InventoryPage && _heldItem.Value == null && _showBillboardButton.Value.containsPoint( (int)Utility.ModifyCoordinateForUIScale(Game1.getMouseX()), diff --git a/UIInfoSuite2/UIElements/ShowTodaysGifts.cs b/UIInfoSuite2/UIElements/ShowTodaysGifts.cs index 45dee0f7..856a679f 100644 --- a/UIInfoSuite2/UIElements/ShowTodaysGifts.cs +++ b/UIInfoSuite2/UIElements/ShowTodaysGifts.cs @@ -1,4 +1,6 @@ using System; +using Leclair.Stardew.BetterGameMenu; + using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI; @@ -6,12 +8,14 @@ using StardewValley; using StardewValley.Menus; +using UIInfoSuite2.Compatibility; +using UIInfoSuite2.Infrastructure; + namespace UIInfoSuite2.UIElements; internal class ShowTodaysGifts : IDisposable { #region Properties - private SocialPage? _socialPage; private readonly IModHelper _helper; #endregion @@ -28,12 +32,10 @@ public void Dispose() public void ToggleOption(bool showTodaysGift) { - _helper.Events.Display.MenuChanged -= OnMenuChanged; _helper.Events.Display.RenderedActiveMenu -= OnRenderedActiveMenu; if (showTodaysGift) { - _helper.Events.Display.MenuChanged += OnMenuChanged; _helper.Events.Display.RenderedActiveMenu += OnRenderedActiveMenu; } } @@ -42,68 +44,43 @@ public void ToggleOption(bool showTodaysGift) #region Event subscriptions private void OnRenderedActiveMenu(object? sender, RenderedActiveMenuEventArgs e) { - if (_socialPage == null) + if (Tools.GetCurrentMenuPage() is not SocialPage socialPage) { - GetSocialPage(); return; } - if (Game1.activeClickableMenu is GameMenu gameMenu && gameMenu.currentTab == GameMenu.socialTab) - { - DrawTodaysGifts(); + DrawTodaysGifts(socialPage); + if (Game1.activeClickableMenu is GameMenu gameMenu) + { string hoverText = gameMenu.hoverText; IClickableMenu.drawHoverText(Game1.spriteBatch, hoverText, Game1.smallFont); } } + #endregion - private void OnMenuChanged(object? sender, MenuChangedEventArgs e) - { - GetSocialPage(); - } -#endregion - -#region Logic - private void GetSocialPage() - { - if (Game1.activeClickableMenu is not GameMenu gameMenu) - { - return; - } - - foreach (IClickableMenu? menu in gameMenu.pages) - { - if (menu is not SocialPage page) - { - continue; - } - - _socialPage = page; - break; - } - } - - private void DrawTodaysGifts() + #region Logic + private static void DrawTodaysGifts(SocialPage? socialPage) { - if (_socialPage == null) + if (socialPage == null) { return; } - var yOffset = 25; + int yOffset = 25; - for (int i = _socialPage.slotPosition; i < _socialPage.slotPosition + 5 && i < _socialPage.SocialEntries.Count; ++i) + for (int i = socialPage.slotPosition; i < socialPage.slotPosition + 5 && i < socialPage.SocialEntries.Count; ++i) { int yPosition = Game1.activeClickableMenu.yPositionOnScreen + 130 + yOffset; yOffset += 112; - string internalName = _socialPage.SocialEntries[i].InternalName; + string internalName = socialPage.SocialEntries[i].InternalName; if (Game1.player.friendshipData.TryGetValue(internalName, out Friendship? data) && data.GiftsToday != 0 && data.GiftsThisWeek < 2) { Game1.spriteBatch.Draw( Game1.mouseCursors, - new Vector2(_socialPage.xPositionOnScreen + 384 + 296 + 4, yPosition + 6), + new Vector2(socialPage.xPositionOnScreen + 384 + 296 + 4, yPosition + 6), new Rectangle(106, 442, 9, 9), Color.LightGray, 0.0f,