diff --git a/Config/ConfigurationManager.cs b/Config/ConfigurationManager.cs index db9a259..4f98938 100644 --- a/Config/ConfigurationManager.cs +++ b/Config/ConfigurationManager.cs @@ -675,6 +675,7 @@ namespace HSUI.Config // Other Elements typeof(ExperienceBarConfig), typeof(GCDIndicatorConfig), + typeof(MouseGCDIndicatorConfig), typeof(HotbarsConfig), typeof(Hotbar1BarConfig), typeof(Hotbar2BarConfig), diff --git a/HSUI.csproj b/HSUI.csproj index 221363e..ac0e55c 100644 --- a/HSUI.csproj +++ b/HSUI.csproj @@ -9,9 +9,9 @@ HSUI - 1.0.8.13 - 1.0.8.13 - 1.0.8.13 + 1.0.8.14 + 1.0.8.14 + 1.0.8.14 diff --git a/HSUI.json b/HSUI.json index c480bd2..53d3b0e 100644 --- a/HSUI.json +++ b/HSUI.json @@ -2,7 +2,7 @@ "Author": "Knack117", "Name": "HSUI", "InternalName": "HSUI", - "AssemblyVersion": "1.0.8.13", + "AssemblyVersion": "1.0.8.14", "Description": "HSUI provides a highly configurable HUD replacement for FFXIV, recreated from DelvUI using KamiToolKit, FFXIVClientStructs, and Dalamud. Features unit frames, castbars, job gauges, nameplates, party frames, status effects, enemy list, configurable hotbars with drag-and-drop, and profiles.", "ApplicableVersion": "any", "RepoUrl": "https://github.com/Knack117/HSUI", diff --git a/Interface/GeneralElements/MouseGCDIndicatorConfig.cs b/Interface/GeneralElements/MouseGCDIndicatorConfig.cs new file mode 100644 index 0000000..60cf9c4 --- /dev/null +++ b/Interface/GeneralElements/MouseGCDIndicatorConfig.cs @@ -0,0 +1,74 @@ +using HSUI.Config; +using HSUI.Config.Attributes; +using HSUI.Enums; +using System.Numerics; + +namespace HSUI.Interface.GeneralElements +{ + [DisableParentSettings("Position")] + [Section("Other Elements")] + [SubSection("Mouse GCD Indicator", 0)] + public class MouseGCDIndicatorConfig : MovablePluginConfigObject + { + [Checkbox("Always Show")] + [Order(3)] + public bool AlwaysShow = false; + + [ColorEdit4("Background Color")] + [Order(16)] + public PluginConfigColor BackgroundColor = new PluginConfigColor(new Vector4(0f / 255f, 0f / 255f, 0f / 255f, 50f / 100f)); + + [ColorEdit4("Color")] + [Order(17)] + public PluginConfigColor FillColor = new PluginConfigColor(new(220f / 255f, 220f / 255f, 220f / 255f, 100f / 100f)); + + [Checkbox("Show Border")] + [Order(18)] + public bool ShowBorder = true; + + [Checkbox("Instant GCDs only", spacing = true)] + [Order(19)] + public bool InstantGCDsOnly = false; + + [Checkbox("Only show when under GCD Threshold", spacing = true)] + [Order(20)] + public bool LimitGCDThreshold = false; + + [DragFloat("GCD Threshold", velocity = 0.01f)] + [Order(21, collapseWith = nameof(LimitGCDThreshold))] + public float GCDThreshold = 1.50f; + + [Checkbox("Show GCD Queue Indicator", spacing = true)] + [Order(24)] + public bool ShowGCDQueueIndicator = true; + + [ColorEdit4("GCD Queue Color")] + [Order(25, collapseWith = nameof(ShowGCDQueueIndicator))] + public PluginConfigColor QueueColor = new PluginConfigColor(new(13f / 255f, 207f / 255f, 31f / 255f, 100f / 100f)); + + [DragInt("Radius")] + [Order(35)] + public int CircleRadius = 40; + + [DragInt("Thickness")] + [Order(40)] + public int CircleThickness = 10; + + [DragInt("Start Angle", min = 0, max = 359)] + [Order(45)] + public int CircleStartAngle = 0; + + [Checkbox("Rotate CCW")] + [Order(50)] + public bool RotateCCW = false; + + [DragInt2("Cursor Center Offset", min = -64, max = 64)] + [Order(55)] + public Vector2 CursorCenterOffset = new Vector2(8, 16); + + [NestedConfig("Visibility", 70)] + public VisibilityConfig VisibilityConfig = new VisibilityConfig(); + + public new static MouseGCDIndicatorConfig DefaultConfig() { return new MouseGCDIndicatorConfig() { Enabled = false, Strata = StrataLevel.MID_HIGH }; } + } +} diff --git a/Interface/GeneralElements/MouseGCDIndicatorHud.cs b/Interface/GeneralElements/MouseGCDIndicatorHud.cs new file mode 100644 index 0000000..e5d0e92 --- /dev/null +++ b/Interface/GeneralElements/MouseGCDIndicatorHud.cs @@ -0,0 +1,140 @@ +using System; +using HSUI.Helpers; +using Dalamud.Bindings.ImGui; +using System.Collections.Generic; +using System.Numerics; +using HSUI.Config; +using Dalamud.Game.ClientState.Objects.SubKinds; +using Dalamud.Game.ClientState.Objects.Types; + +namespace HSUI.Interface.GeneralElements +{ + public class MouseGCDIndicatorHud : DraggableHudElement, IHudElementWithActor, IHudElementWithVisibilityConfig + { + private MouseGCDIndicatorConfig Config => (MouseGCDIndicatorConfig)_config; + public VisibilityConfig VisibilityConfig => Config.VisibilityConfig; + + public IGameObject? Actor { get; set; } = null; + + private float _lastTotalCastTime = 0; + + public MouseGCDIndicatorHud(MouseGCDIndicatorConfig config, string displayName) : base(config, displayName) { } + + protected override (List, List) ChildrenPositionsAndSizes() + { + return (new List(), new List()); + } + + protected override void DrawDraggableArea(Vector2 origin) + { + return; + } + + public override void DrawChildren(Vector2 origin) + { + if (!Config.Enabled || Actor == null || Actor is not IPlayerCharacter) + { + return; + } + + GCDHelper.GetGCDInfo((IPlayerCharacter)Actor, out var elapsed, out var total); + + if (!Config.AlwaysShow && total == 0) + { + _lastTotalCastTime = 0; + return; + } + + if (_lastTotalCastTime == 0 && Utils.IsActorCasting(Actor)) + { + _lastTotalCastTime = ((IBattleChara)Actor).TotalCastTime; + } + + var scale = elapsed / total; + if (scale <= 0) + { + _lastTotalCastTime = 0; + return; + } + + bool instantGCDsOnly = Config.InstantGCDsOnly && _lastTotalCastTime != 0; + bool thresholdGCDs = Config.LimitGCDThreshold && _lastTotalCastTime > Config.GCDThreshold; + + if (instantGCDsOnly || thresholdGCDs) + { + if (Config.AlwaysShow) + { + elapsed = 0; + total = 0; + } + else + { + return; + } + } + + Vector2 center = ImGui.GetMousePos() + Config.CursorCenterOffset; + AddDrawAction(_config.StrataLevel, () => + { + DrawCircularIndicator(center, Config.CircleRadius, elapsed, total); + }); + } + + private void DrawCircularIndicator(Vector2 position, float radius, float current, float total) + { + total = Config.AlwaysShow && total == 0 ? 1 : total; + current = Config.AlwaysShow && current == 0 ? total : current; + + var size = new Vector2(radius * 2); + DrawHelper.DrawInWindow(ID, position - size / 2, size, false, (drawList) => + { + current = Math.Min(current, total); + + const int segments = 100; + const float queueTime = 0.5f; + float startAngle = 0f; + float endAngle = 2f * (float)Math.PI; + float offset = (float)(-Math.PI / 2f + (Config.CircleStartAngle * (Math.PI / 180f))); + + if (Config.RotateCCW) + { + startAngle *= -1; + endAngle *= -1; + } + + if (Config.AlwaysShow && current == total) + { + drawList.PathArcTo(position, radius, startAngle + offset, endAngle + offset, segments); + drawList.PathStroke(Config.FillColor.Base, ImDrawFlags.None, Config.CircleThickness); + } + else + { + float progressAngle = Math.Min(current, total - (Config.ShowGCDQueueIndicator ? queueTime : 0f)) / total * endAngle; + + drawList.PathArcTo(position, radius, startAngle + offset, progressAngle + offset, segments); + drawList.PathStroke(Config.FillColor.Base, ImDrawFlags.None, Config.CircleThickness); + + if (Config.ShowGCDQueueIndicator && current > total - queueTime) + { + float oldAngle = progressAngle - 0.0003f * total * endAngle; + progressAngle = current / total * endAngle; + drawList.PathArcTo(position, radius, oldAngle + offset, progressAngle + offset, segments); + drawList.PathStroke(Config.QueueColor.Base, ImDrawFlags.None, Config.CircleThickness); + } + + drawList.PathArcTo(position, radius, progressAngle + offset, endAngle + offset, segments); + drawList.PathStroke(Config.BackgroundColor.Base, ImDrawFlags.None, Config.CircleThickness); + } + + if (Config.ShowBorder) + { + drawList.PathArcTo(position, radius - Config.CircleThickness / 2f, 0, endAngle, segments); + drawList.PathStroke(0xFF000000, ImDrawFlags.None, 1); + + drawList.PathArcTo(position, radius + Config.CircleThickness / 2f, 0, endAngle, segments); + drawList.PathStroke(0xFF000000, ImDrawFlags.None, 1); + } + }); + } + } +} diff --git a/Interface/HudManager.cs b/Interface/HudManager.cs index 2532e00..e75ff4d 100644 --- a/Interface/HudManager.cs +++ b/Interface/HudManager.cs @@ -379,6 +379,11 @@ namespace HSUI.Interface _hudElements.Add(gcdIndicatorConfig, gcdIndicator); _hudElementsUsingPlayer.Add(gcdIndicator); + var mouseGcdIndicatorConfig = ConfigurationManager.Instance.GetConfigObject(); + var mouseGcdIndicator = new MouseGCDIndicatorHud(mouseGcdIndicatorConfig, "Mouse GCD Indicator"); + _hudElements.Add(mouseGcdIndicatorConfig, mouseGcdIndicator); + _hudElementsUsingPlayer.Add(mouseGcdIndicator); + var mpTickerConfig = ConfigurationManager.Instance.GetConfigObject(); var mpTicker = new MPTickerHud(mpTickerConfig, "MP Ticker"); _hudElements.Add(mpTickerConfig, mpTicker); diff --git a/changelog.md b/changelog.md index 120d679..6f1b673 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,6 @@ +# 1.0.8.14 +- **Mouse GCD Indicator**: New HUD element (Other Elements → Mouse GCD Indicator) — circular GCD ring drawn around the cursor. Configurable radius, thickness, colors, queue indicator, and cursor center offset so the ring aligns with the cursor graphic. + # 1.0.8.13 - **Hotbars**: Fixed item tooltips not showing — use TryGetRow for Item sheet and base item ID so tooltip no longer fails for some items. diff --git a/pluginmaster.json b/pluginmaster.json index 406b058..88eb7cb 100644 --- a/pluginmaster.json +++ b/pluginmaster.json @@ -1 +1 @@ -[{"Author":"Knack117","Name":"HSUI","Punchline":"A modern HUD replacement built for customization.","Description":"HSUI provides a highly configurable HUD replacement for FFXIV, recreated from DelvUI using KamiToolKit, FFXIVClientStructs, and Dalamud. Features unit frames, castbars, job gauges, nameplates, party frames, status effects, enemy list, configurable hotbars with drag-and-drop, and profiles.","Changelog":"1.0.8.13: Item tooltips now show. 1.0.8.12: Item/HQ icons now draw on hotbar. 1.0.8.11: Hotbar tooltip crash fix (Lumina GetRow). 1.0.8.10: Hotbar crash fix when dragging inventory items; Release builds strip PDBs. 1.0.8.9: Gearset persists on slot (fix CommandId 0 treated as empty). 1.0.8.8: Gearset drag-drop fix; allow first gearset (index 0) on bar. 1.0.8.7: Fixed Gearset/Job Gear Set icon clearing when first equipment slot changes. 1.0.8.6: Crafting action tooltips now show full description instead of action name. 1.0.8.4: Alliance Frames 1 and 2 fix in raids; crafting action tooltips; Hide in duty no longer hides alliance frames. 1.0.8.3: Fix left-click staying broken after disable. 1.0.8.2: Charge icons stay lit until all charges spent.","InternalName":"HSUI","AssemblyVersion":"1.0.8.13","RepoUrl":"http://brassnet.ddns.net:33983/KnackAtNite/HSUI","ApplicableVersion":"any","Tags":["UI","HUD","Unit Frames","Nameplates","Party Frames","Hotbars"],"CategoryTags":["UI"],"DalamudApiLevel":14,"IconUrl":"http://brassnet.ddns.net:33983/KnackAtNite/HSUI/raw/branch/main/Media/Images/icon.png","ImageUrls":[],"DownloadLinkInstall":"http://brassnet.ddns.net:33983/KnackAtNite/HSUI/releases/download/v1.0.8.13/latest.zip","IsHide":false,"IsTestingExclusive":false,"DownloadLinkTesting":"http://brassnet.ddns.net:33983/KnackAtNite/HSUI/releases/download/v1.0.8.13/latest.zip","DownloadLinkUpdate":"http://brassnet.ddns.net:33983/KnackAtNite/HSUI/releases/download/v1.0.8.13/latest.zip","LastUpdate":"1760486400"}] +[{"Author":"Knack117","Name":"HSUI","Punchline":"A modern HUD replacement built for customization.","Description":"HSUI provides a highly configurable HUD replacement for FFXIV, recreated from DelvUI using KamiToolKit, FFXIVClientStructs, and Dalamud. Features unit frames, castbars, job gauges, nameplates, party frames, status effects, enemy list, configurable hotbars with drag-and-drop, and profiles.","Changelog":"1.0.8.14: Mouse GCD Indicator (ring around cursor). 1.0.8.13: Item tooltips now show. 1.0.8.12: Item/HQ icons now draw on hotbar. 1.0.8.11: Hotbar tooltip crash fix (Lumina GetRow). 1.0.8.10: Hotbar crash fix when dragging inventory items; Release builds strip PDBs. 1.0.8.9: Gearset persists on slot (fix CommandId 0 treated as empty). 1.0.8.8: Gearset drag-drop fix; allow first gearset (index 0) on bar. 1.0.8.7: Fixed Gearset/Job Gear Set icon clearing when first equipment slot changes. 1.0.8.6: Crafting action tooltips now show full description instead of action name. 1.0.8.4: Alliance Frames 1 and 2 fix in raids; crafting action tooltips; Hide in duty no longer hides alliance frames. 1.0.8.3: Fix left-click staying broken after disable. 1.0.8.2: Charge icons stay lit until all charges spent.","InternalName":"HSUI","AssemblyVersion":"1.0.8.14","RepoUrl":"http://brassnet.ddns.net:33983/KnackAtNite/HSUI","ApplicableVersion":"any","Tags":["UI","HUD","Unit Frames","Nameplates","Party Frames","Hotbars"],"CategoryTags":["UI"],"DalamudApiLevel":14,"IconUrl":"http://brassnet.ddns.net:33983/KnackAtNite/HSUI/raw/branch/main/Media/Images/icon.png","ImageUrls":[],"DownloadLinkInstall":"http://brassnet.ddns.net:33983/KnackAtNite/HSUI/releases/download/v1.0.8.14/latest.zip","IsHide":false,"IsTestingExclusive":false,"DownloadLinkTesting":"http://brassnet.ddns.net:33983/KnackAtNite/HSUI/releases/download/v1.0.8.14/latest.zip","DownloadLinkUpdate":"http://brassnet.ddns.net:33983/KnackAtNite/HSUI/releases/download/v1.0.8.14/latest.zip","LastUpdate":"1760486400"}]