From 053ea1360684cfda753375c583b68d362cc032ec Mon Sep 17 00:00:00 2001 From: SoulSilverJD <165866563+SoulSilverJD@users.noreply.github.com> Date: Sat, 27 Jun 2026 04:13:59 -0400 Subject: [PATCH 1/4] Add IWearMoreRingsApi.cs --- MoonShared/APIs/IWearMoreRingsApi.cs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 MoonShared/APIs/IWearMoreRingsApi.cs diff --git a/MoonShared/APIs/IWearMoreRingsApi.cs b/MoonShared/APIs/IWearMoreRingsApi.cs new file mode 100644 index 00000000..bb969fdb --- /dev/null +++ b/MoonShared/APIs/IWearMoreRingsApi.cs @@ -0,0 +1,9 @@ +using StardewValley.Objects; + +namespace MoonShared.APIs; + +public interface IWearMoreRingsAPI_2 +{ + int RingSlotCount(); + Ring GetRing(int slot); +} From b07d972d5780bda95a8a728744aef1e2b7151d11 Mon Sep 17 00:00:00 2001 From: SoulSilverJD <165866563+SoulSilverJD@users.noreply.github.com> Date: Sat, 27 Jun 2026 04:20:01 -0400 Subject: [PATCH 2/4] Detect IWearMoreRingsApi.cs --- MoonShared/MoonShared.projitems | 1 + 1 file changed, 1 insertion(+) diff --git a/MoonShared/MoonShared.projitems b/MoonShared/MoonShared.projitems index bb52b20c..92ef2d0d 100644 --- a/MoonShared/MoonShared.projitems +++ b/MoonShared/MoonShared.projitems @@ -21,6 +21,7 @@ + From f5cbc6bdccf66c2f93ce80b7e50d0b74d8fc455a Mon Sep 17 00:00:00 2001 From: SoulSilverJD <165866563+SoulSilverJD@users.noreply.github.com> Date: Sat, 27 Jun 2026 04:30:27 -0400 Subject: [PATCH 3/4] Vanilla or Custom ManaBar | MagicStardew(Beta) Added a way to switch between a more UI recolor friendly compact by using HealthBar and an Overlay (Same as before), or more of a custom ManaBar using 'manabg.png'. All the logic is the same between the two the only diff is visual. --- WizardryManaBar/Core/Config.cs | 11 ++++ WizardryManaBar/Core/Events.cs | 94 +++++++++++++++++++++++++++++++--- 2 files changed, 97 insertions(+), 8 deletions(-) diff --git a/WizardryManaBar/Core/Config.cs b/WizardryManaBar/Core/Config.cs index a68ad482..41b3339f 100644 --- a/WizardryManaBar/Core/Config.cs +++ b/WizardryManaBar/Core/Config.cs @@ -2,6 +2,12 @@ namespace WizardryManaBar.Core { + internal static class ManaBarModes + { + public const string Compact = "Compact"; + public const string Custom = "Custom"; + } + [SConfig] public class Config { @@ -9,6 +15,11 @@ public class Config public bool RenderManaBar { get; set; } = true; + // Compact uses the vanilla bar frame + ManaBarIcon; Custom uses manabg.png as the full frame. + [SConfig.Option(new string[] { ManaBarModes.Compact, ManaBarModes.Custom })] + public string ManaBarMode { get; set; } = ManaBarModes.Compact; + + [SConfig.Option(-500, 500, 1)] public int XManaBarOffset { get; set; } = 0; diff --git a/WizardryManaBar/Core/Events.cs b/WizardryManaBar/Core/Events.cs index 11aa4b48..03768b5d 100644 --- a/WizardryManaBar/Core/Events.cs +++ b/WizardryManaBar/Core/Events.cs @@ -1,6 +1,7 @@ using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using MoonShared.APIs; using MoonShared.Attributes; using SpaceCore.Events; using StardewModdingAPI; @@ -16,6 +17,7 @@ public class Events { private const string ManaFill = "moonslime.ManaBarApi.ManaFill"; private const string ManaRestore = "moonslime.ManaBarApi.ManaRestore"; + private const string MagicStardewSpellBookQualifiedItemId = "(O)MagicStardew.SpellBook"; private const int ManaBarOriginalBaseMana = 100; private const int ManaBarBaseHeight = 168; @@ -32,6 +34,14 @@ public class Events private static readonly Rectangle ManaPipSourceRect = new Rectangle(398, 497, ManaPipSourceWidth, ManaPipSourceHeight); + // Custom art mode uses manabg.png as a vanilla-style 12x56 bar frame. + private static readonly Rectangle CompactFrameTopSource = new Rectangle(268, 408, 12, 16); + private static readonly Rectangle CompactFrameMiddleSource = new Rectangle(268, 424, 12, 16); + private static readonly Rectangle CompactFrameBottomSource = new Rectangle(268, 448, 12, 16); + private static readonly Rectangle CustomFrameTopSource = new Rectangle(0, 0, 12, 16); + private static readonly Rectangle CustomFrameMiddleSource = new Rectangle(0, 16, 12, 24); + private static readonly Rectangle CustomFrameBottomSource = new Rectangle(0, 40, 12, 16); + private const int ManaPipDrawWidth = ManaPipSourceWidth * ManaPipDrawScale; private const int ManaPipDrawHeight = ManaPipSourceHeight * ManaPipDrawScale; private const int ManaPipOverlap = ManaPipSourceOverlap * ManaPipDrawScale; @@ -50,6 +60,7 @@ public class Events private static Vector2 CachedBottomOfBar; private static Rectangle CachedMiddleRect; private static Rectangle CachedBottomRect; + private static bool WarnedInvalidCustomFrameTexture; // --- Cached dynamic filler --- private static int CachedCurrentMana = -1; @@ -268,10 +279,31 @@ private static Rectangle[] BuildManaPipRects(int pipCount, int fillerBottom) private static void DrawStaticBar(SpriteBatch sb) { - sb.Draw(Game1.mouseCursors, CachedTopOfBar, new Rectangle(268, 408, 12, 16), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, 1f); + if (IsCustomManaBarMode()) + { + Texture2D customFrame = Assets.ManaBG; + if (customFrame.Width >= 12 && customFrame.Height >= 56) + { + DrawFrame(sb, customFrame, CustomFrameTopSource, CustomFrameMiddleSource, CustomFrameBottomSource); + return; + } + + if (!WarnedInvalidCustomFrameTexture) + { + WarnedInvalidCustomFrameTexture = true; + ModEntry.Instance.Monitor.Log($"Custom mana bar art must be at least 12x56 pixels, but Mods/moonslime.ManaBarApi/textures is {customFrame.Width}x{customFrame.Height}. Falling back to compact mode.", LogLevel.Warn); + } + } + + DrawFrame(sb, Game1.mouseCursors, CompactFrameTopSource, CompactFrameMiddleSource, CompactFrameBottomSource); sb.Draw(Assets.ManaBarIcon, CachedTopOfBar, new Rectangle(0, 0, 12, 12), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, 1f); - sb.Draw(Game1.mouseCursors, CachedMiddleRect, new Rectangle(256, 424, 12, 16), Color.White); - sb.Draw(Game1.mouseCursors, new Vector2(CachedBottomRect.X, CachedBottomRect.Y), new Rectangle(256, 448, 12, 16), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, 1f); + } + + private static void DrawFrame(SpriteBatch sb, Texture2D texture, Rectangle topSource, Rectangle middleSource, Rectangle bottomSource) + { + sb.Draw(texture, CachedTopOfBar, topSource, Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, 1f); + sb.Draw(texture, CachedMiddleRect, middleSource, Color.White); + sb.Draw(texture, new Vector2(CachedBottomRect.X, CachedBottomRect.Y), bottomSource, Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, 1f); } private static void DrawFiller(SpriteBatch sb) @@ -317,20 +349,66 @@ public static Color GetBlueToWhiteLerpColor(float power) public static float SetBarsPosition() { + if (ModEntry.Config.ManaBarExtraSnaps == -1) + return 56f; + int extraCount = 1; if (Game1.showingHealth) extraCount++; - if (ModEntry.MagicStardewLoaded) extraCount++; + if (ShouldSnapToMagicStardewManaBar()) extraCount++; extraCount += ModEntry.Config.ManaBarExtraSnaps; - if (ModEntry.Config.ManaBarExtraSnaps == -1) + return 56f * Math.Max(1, extraCount); + } + + private static bool IsCustomManaBarMode() + { + return string.Equals(ModEntry.Config.ManaBarMode, ManaBarModes.Custom, StringComparison.OrdinalIgnoreCase); + } + + private static bool ShouldSnapToMagicStardewManaBar() + { + if (!ModEntry.MagicStardewLoaded || !Context.IsWorldReady || Game1.currentLocation is null || Game1.eventUp) + return false; + + Farmer player = Game1.player; + if (player is null) + return false; + + return HasMagicStardewSpellBook(player); + } + + private static bool HasMagicStardewSpellBook(Farmer player) + { + if (player.leftRing.Value?.QualifiedItemId == MagicStardewSpellBookQualifiedItemId || player.rightRing.Value?.QualifiedItemId == MagicStardewSpellBookQualifiedItemId) + return true; + + if (HasMagicStardewSpellBookInWearMoreRingsSlot()) + return true; + + foreach (Item item in player.Items) { - return 56f; + if (item?.QualifiedItemId == MagicStardewSpellBookQualifiedItemId) + return true; } - else + + return false; + } + + private static bool HasMagicStardewSpellBookInWearMoreRingsSlot() + { + var wearMoreRingsApi = ModEntry.Instance.Helper.ModRegistry.GetApi("bcmpinc.WearMoreRings"); + if (wearMoreRingsApi is null) + return false; + + int slotCount = wearMoreRingsApi.RingSlotCount(); + for (int i = 0; i < slotCount; i++) { - return 56f * extraCount; + if (wearMoreRingsApi.GetRing(i)?.QualifiedItemId == MagicStardewSpellBookQualifiedItemId) + return true; } + + return false; } private static void OnDayStarted(object sender, DayStartedEventArgs e) From 8ac9268d753bb7032e0d2a95d419fdd122cd45d1 Mon Sep 17 00:00:00 2001 From: SoulSilverJD <165866563+SoulSilverJD@users.noreply.github.com> Date: Sat, 27 Jun 2026 04:36:10 -0400 Subject: [PATCH 4/4] Add Description --- WizardryManaBar/i18n/default.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/WizardryManaBar/i18n/default.json b/WizardryManaBar/i18n/default.json index 375ebef9..0e2368be 100644 --- a/WizardryManaBar/i18n/default.json +++ b/WizardryManaBar/i18n/default.json @@ -3,6 +3,9 @@ "config.RenderManaBar": "Render Mana Bar", "config.RenderManaBar.tooltip": "Should the mana bar be rendered at all?", + "config.ManaBarMode": "Mana Bar Mode", + "config.ManaBarMode.tooltip": "Compact: Stardew's Health Bar with ManaBarIcon.png overlaid.\nCustom: Uses manabg.png as the full Mana Bar.", + "config.XManaBarOffset": "Mana Bar X offset", "config.XManaBarOffset.tooltip": "Control where the mana bar is on the X axis of the screen.\nNote: If Snapping is not set to -1, bar will move when the player's health shows up.",