├── GaugeOMatic ├── Assets │ ├── icon.png │ └── showCase.jpg ├── TextureAssets │ ├── huton.tex │ ├── CircleMask.tex │ ├── SimpleGems.tex │ ├── iconFrame.png │ ├── iconFrame.tex │ ├── simpleGemUI.png │ ├── SimpleGemsBlack.tex │ ├── SimpleGemsSilver.tex │ └── MedDiamondSingleFrame.tex ├── GaugeOMatic.yaml ├── packages.lock.json ├── GaugeOMatic.json ├── Utility │ ├── MemoryHelper.cs │ ├── MiscMath.cs │ ├── Service.cs │ └── Json.cs ├── GameData │ ├── ItemRef.cs │ ├── Sheets.cs │ ├── Structs │ │ ├── AddonJobHud.cs │ │ ├── AddonJobHudWAR.cs │ │ ├── AddonJobHudPLD.cs │ │ ├── AddonJobHudGNB.cs │ │ ├── AddonJobHudDRG.cs │ │ ├── AddonJobHudMCH.cs │ │ ├── AddonJobHudAST.cs │ │ ├── AddonJobHudNIN.cs │ │ ├── AddonJobHudDRK.cs │ │ ├── AddonJobHudMNK.cs │ │ └── AddonJobHudSGE.cs │ ├── FrameworkData.cs │ ├── Job.cs │ └── ActionRef.DataGetters.cs ├── Dalamud.Plugin.Bootstrap.targets ├── CustomNodes │ ├── CustomNode.Res.cs │ ├── CustomNode.Text.cs │ ├── CustomNode.NineGrid.cs │ ├── CustomNode.Timeline.cs │ ├── AddonIndex.cs │ ├── CustomNodeManager.cs │ ├── CustomNode.Bounds.Line.cs │ ├── Animation │ │ ├── Animator.cs │ │ └── Tween.cs │ ├── CustomNode.Image.cs │ ├── CustomNode.Properties.cs │ └── CustomNode.cs ├── JobModules │ ├── Tweaks.cs │ ├── Other │ │ └── BLU.cs │ ├── Healer │ │ ├── AST.cs │ │ ├── WHM.cs │ │ ├── SGE.cs │ │ └── SCH.cs │ ├── Tank │ │ ├── GNB.cs │ │ ├── WAR.cs │ │ ├── PLD.cs │ │ └── DRK.cs │ ├── Melee │ │ ├── DRG.cs │ │ ├── RPR.cs │ │ ├── MNK.cs │ │ └── SAM.cs │ ├── Ranged │ │ ├── DNC.cs │ │ ├── MCH.cs │ │ └── BRD.cs │ └── Caster │ │ ├── SMN.cs │ │ ├── RDM.cs │ │ └── PCT.cs ├── Widgets │ ├── StateWidget.cs │ ├── Widget.Nodes.cs │ ├── Widget.Icon.cs │ ├── Widget.Sound.cs │ ├── WidgetConfig.cs │ ├── Widget.Events.cs │ ├── Widget.cs │ └── Common │ │ └── LabelTextNode.cs ├── Config │ └── Configuration.cs ├── Trackers │ ├── TrackerData.cs │ ├── TrackerConfig.cs │ ├── Presets │ │ └── Preset.cs │ ├── TrackerTypes.cs │ ├── TrackerManager.cs │ └── Tracker.cs ├── GaugeOMatic - Backup.csproj ├── GaugeOMatic - Backup (1).csproj ├── Windows │ ├── Dropdowns │ │ ├── AddonDropdown.cs │ │ ├── Dropdown.cs │ │ └── WidgetDropdown.cs │ └── Tooltips │ │ ├── StatusTooltip.cs │ │ ├── ItemRefTooltip.cs │ │ └── ActionTooltip.cs ├── GaugeOMatic.cs └── GaugeOMatic.csproj ├── .gitignore ├── GaugeOMatic.sln ├── .gitattributes └── README.md /GaugeOMatic/Assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyvorld/GaugeOMatic/main/GaugeOMatic/Assets/icon.png -------------------------------------------------------------------------------- /GaugeOMatic/Assets/showCase.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyvorld/GaugeOMatic/main/GaugeOMatic/Assets/showCase.jpg -------------------------------------------------------------------------------- /GaugeOMatic/TextureAssets/huton.tex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyvorld/GaugeOMatic/main/GaugeOMatic/TextureAssets/huton.tex -------------------------------------------------------------------------------- /GaugeOMatic/TextureAssets/CircleMask.tex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyvorld/GaugeOMatic/main/GaugeOMatic/TextureAssets/CircleMask.tex -------------------------------------------------------------------------------- /GaugeOMatic/TextureAssets/SimpleGems.tex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyvorld/GaugeOMatic/main/GaugeOMatic/TextureAssets/SimpleGems.tex -------------------------------------------------------------------------------- /GaugeOMatic/TextureAssets/iconFrame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyvorld/GaugeOMatic/main/GaugeOMatic/TextureAssets/iconFrame.png -------------------------------------------------------------------------------- /GaugeOMatic/TextureAssets/iconFrame.tex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyvorld/GaugeOMatic/main/GaugeOMatic/TextureAssets/iconFrame.tex -------------------------------------------------------------------------------- /GaugeOMatic/TextureAssets/simpleGemUI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyvorld/GaugeOMatic/main/GaugeOMatic/TextureAssets/simpleGemUI.png -------------------------------------------------------------------------------- /GaugeOMatic/TextureAssets/SimpleGemsBlack.tex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyvorld/GaugeOMatic/main/GaugeOMatic/TextureAssets/SimpleGemsBlack.tex -------------------------------------------------------------------------------- /GaugeOMatic/TextureAssets/SimpleGemsSilver.tex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyvorld/GaugeOMatic/main/GaugeOMatic/TextureAssets/SimpleGemsSilver.tex -------------------------------------------------------------------------------- /GaugeOMatic/TextureAssets/MedDiamondSingleFrame.tex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyvorld/GaugeOMatic/main/GaugeOMatic/TextureAssets/MedDiamondSingleFrame.tex -------------------------------------------------------------------------------- /GaugeOMatic/GaugeOMatic.yaml: -------------------------------------------------------------------------------- 1 | name: Gauge-O-Matic 2 | author: ItsBexy 3 | description: |- 4 | Gauge-O-Matic allows you to customize your job gauges by adding extra counters, bars, and indicators in a variety of styles 5 | punchline: Enhance and customize job gauges. 6 | repo_url: https://github.com/ItsBexy/GaugeOMatic 7 | dalamud_api_level: 12 8 | -------------------------------------------------------------------------------- /GaugeOMatic/packages.lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "dependencies": { 4 | "net9.0-windows7.0": { 5 | "DalamudPackager": { 6 | "type": "Direct", 7 | "requested": "[12.0.0, )", 8 | "resolved": "12.0.0", 9 | "contentHash": "J5TJLV3f16T/E2H2P17ClWjtfEBPpq3yxvqW46eN36JCm6wR+EaoaYkqG9Rm5sHqs3/nK/vKjWWyvEs/jhKoXw==" 10 | } 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /GaugeOMatic/GaugeOMatic.json: -------------------------------------------------------------------------------- 1 | { 2 | "Author": "ItsBexy", 3 | "Name": "Gauge-O-Matic", 4 | "Punchline": "Enhance and customize job gauges.", 5 | "Description": "Gauge-O-Matic allows you to customize your job gauges by adding extra counters, bars, and indicators in a variety of styles", 6 | "InternalName": "GaugeOMatic", 7 | "ApplicableVersion": "any", 8 | "Tags": [ 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /GaugeOMatic/Utility/MemoryHelper.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.System.Memory; 2 | 3 | namespace GaugeOMatic.Utility; 4 | 5 | internal static class MemoryHelper 6 | { 7 | public static unsafe T* Alloc() where T : unmanaged => (T*)Alloc((ulong)sizeof(T)); 8 | public static unsafe T* CleanAlloc() where T : unmanaged => (T*)CleanAlloc((ulong)sizeof(T)); 9 | public static unsafe void* Alloc(ulong size) => IMemorySpace.GetUISpace()->Malloc(size, 8); 10 | 11 | public static unsafe void* CleanAlloc(ulong size) 12 | { 13 | var alloc = Alloc(size); 14 | IMemorySpace.Memset(alloc, 0, size); 15 | return alloc; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/ItemRef.cs: -------------------------------------------------------------------------------- 1 | using GaugeOMatic.JobModules; 2 | using static GaugeOMatic.GameData.JobData; 3 | using static GaugeOMatic.GameData.JobData.Job; 4 | 5 | namespace GaugeOMatic.GameData; 6 | 7 | public abstract partial class ItemRef 8 | { 9 | public Job Job; 10 | public Role Role; 11 | public uint ID; 12 | public string Name = string.Empty; 13 | public bool HideFromDropdown; 14 | public uint? Icon { get; set; } 15 | 16 | public bool CheckJob(Job job, Job jobClass, Role role) => job == Job || (jobClass != None && jobClass == Job) || Role.HasFlag(role); 17 | 18 | public bool CheckJob(JobModule module) => CheckJob(module.Job, module.Class, module.Role); 19 | } 20 | -------------------------------------------------------------------------------- /GaugeOMatic/Dalamud.Plugin.Bootstrap.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(appdata)\XIVLauncher\addon\Hooks\dev\ 5 | $(HOME)/.xlcore/dalamud/Hooks/dev/ 6 | $(HOME)/Library/Application Support/XIV on Mac/dalamud/Hooks/dev/ 7 | $(DALAMUD_HOME)/ 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | obj/ 3 | bin/ 4 | *.user 5 | /GaugeOMatic/ideas.txt 6 | /GaugeOMatic/Widgets/GaugeBars/CrystalConfBar.cs 7 | /GaugeOMatic/Trackers/TrackerTypes.Mit.cs 8 | /GaugeOMatic/Widgets/State/DR.DragonEye.cs 9 | /GaugeOMatic/Widgets/State/FA.FaerieFrame.cs 10 | /GaugeOMatic/Widgets/Counters/PartyDash.cs 11 | /GaugeOMatic/Widgets/Special/PartyDash.cs 12 | /GaugeOMatic/Widgets/Special/PartyDash.cs 13 | /GaugeOMatic/GameData/StatusData.StatusHarvest.cs 14 | 15 | /GaugeOMatic/GameData/StatusData.StatusHarvest.cs 16 | /GaugeOMatic/Widgets/Counters/KZ.KazematoiKunai.cs 17 | /GaugeOMatic/Widgets/State/ShimmerHaloV2.cs 18 | /GaugeOMatic/Windows/ConfigWindow.Settings.cs 19 | /GaugeOMatic/CustomNodes/CustomNode.Bounds.Triangle.cs 20 | /GaugeOMatic/CustomNodes/CustomNode.Bounds.Concave.cs 21 | -------------------------------------------------------------------------------- /GaugeOMatic/Utility/MiscMath.cs: -------------------------------------------------------------------------------- 1 | using static System.Math; 2 | 3 | // ReSharper disable UnusedMember.Global 4 | 5 | namespace GaugeOMatic.Utility; 6 | 7 | public static class MiscMath 8 | { 9 | /// 10 | /// calculate Y as a polynomial function of X. coefficient params are ordered from lowest to highest power.
11 | /// helps indicators follow curved paths based on the tracker value. whats a bezier? never heard of it. go away 12 | ///
13 | public static float PolyCalc(float x, params double[] coeff) 14 | { 15 | var result = 0f; 16 | for (var i = 0; i < coeff.Length; i++) result += (float)(Pow(x, i) * coeff[i]); 17 | return result; 18 | } 19 | 20 | public static float Radians(float deg) => deg * 0.0174532925199433F; 21 | public static float Degrees(float rad) => rad / 0.0174532925199433F; 22 | } 23 | -------------------------------------------------------------------------------- /GaugeOMatic/CustomNodes/CustomNode.Res.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Component.GUI; 2 | using static FFXIVClientStructs.FFXIV.Component.GUI.NodeFlags; 3 | using static FFXIVClientStructs.FFXIV.Component.GUI.NodeType; 4 | using static GaugeOMatic.Utility.MemoryHelper; 5 | 6 | namespace CustomNodes; 7 | 8 | public unsafe partial class CustomNodeManager 9 | { 10 | public static AtkResNode* CreateResNode() 11 | { 12 | var node = CleanAlloc(); 13 | node->Ctor(); 14 | node->NodeId = GetFreeId(); 15 | 16 | RegisteredNodes.TryAdd(node->NodeId, node); 17 | 18 | node->Type = Res; 19 | node->NodeFlags = Visible | AnchorLeft | AnchorTop | Enabled; 20 | 21 | InitializePosition(node, 0, 0); 22 | 23 | return node; 24 | } 25 | 26 | private static void InitializePosition(AtkResNode* node, int x, int y) 27 | { 28 | node->SetPositionFloat(1, 1); 29 | node->SetPositionFloat(x, y); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /GaugeOMatic/CustomNodes/CustomNode.Text.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Component.GUI; 2 | using static GaugeOMatic.Utility.MemoryHelper; 3 | 4 | namespace CustomNodes; 5 | 6 | public unsafe partial class CustomNodeManager 7 | { 8 | public static AtkTextNode* CreateTextNode(string text, int fontSize, int alignFontType) 9 | { 10 | var node = CleanAlloc(); 11 | node->Ctor(); 12 | 13 | node->AlignmentFontType = (byte)alignFontType; 14 | node->FontSize = (byte)fontSize; 15 | node->TextFlags |= 24; 16 | node->AtkResNode.Width = (ushort)((fontSize - 3) * (text.Length + 1)); 17 | 18 | node->AtkResNode.NodeId = GetFreeId(); 19 | 20 | RegisteredNodes.Add(node->AtkResNode.NodeId, (AtkResNode*)node); 21 | node->AtkResNode.Type = NodeType.Text; 22 | node->AtkResNode.NodeFlags = NodeFlags.Visible | NodeFlags.AnchorLeft | NodeFlags.AnchorTop | NodeFlags.Enabled; 23 | node->AtkResNode.DrawFlags |= 8; 24 | 25 | InitializePosition((AtkResNode*)node, 0, 0); 26 | 27 | return node; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Tweaks.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Component.GUI; 2 | using Newtonsoft.Json; 3 | using static GaugeOMatic.GaugeOMatic; 4 | using static GaugeOMatic.Utility.Color; 5 | using static GaugeOMatic.Windows.ConfigWindow; 6 | 7 | namespace GaugeOMatic.JobModules; 8 | 9 | internal static class Tweaks 10 | { 11 | public static unsafe void VisibilityTweak(bool hide, bool simple, AtkResNode* standardNode, AtkResNode* simpleNode) 12 | { 13 | if (standardNode != null) standardNode->SetAlpha((byte)(hide || simple ? 0 : 255)); 14 | if (simpleNode != null) simpleNode->SetAlpha((byte)(hide || !simple ? 0 : 255)); 15 | } 16 | } 17 | 18 | public partial class TweakConfigs 19 | { 20 | public TweakConfigs() 21 | { 22 | if (SCHDissHideText) 23 | { 24 | SCH1FaerieLess = 1; 25 | SCHDissHideText = false; 26 | } 27 | } 28 | [JsonIgnore] public bool Preview = false; 29 | [JsonIgnore] public bool ShowPreviews => Preview && ConfigWindow.IsOpen && JobModuleTab == JobModuleTabs.Tweaks; 30 | [JsonIgnore] public AddRGB? TestColor; 31 | } 32 | -------------------------------------------------------------------------------- /GaugeOMatic/Widgets/StateWidget.cs: -------------------------------------------------------------------------------- 1 | using GaugeOMatic.Trackers; 2 | 3 | namespace GaugeOMatic.Widgets; 4 | 5 | public abstract class StateWidget(Tracker tracker) : Widget(tracker) 6 | { 7 | public bool FirstRun = true; 8 | public abstract void Activate(int current); 9 | public abstract void Deactivate(int previous); 10 | public abstract void OnFirstRun(int current); 11 | public virtual void PostUpdate() { } 12 | public abstract void StateChange(int current, int previous); 13 | 14 | public override void Update() 15 | { 16 | if (FirstRun) { OnFirstRun(Tracker.CurrentData.State); FirstRun = false; } 17 | else 18 | { 19 | var current = Tracker.CurrentData.State; 20 | var previous = Tracker.PreviousData.State; 21 | if (current != previous) 22 | { 23 | if (previous == 0) Activate(current); 24 | else if (current == 0) Deactivate(Tracker.PreviousData.State); 25 | else StateChange(current, previous); 26 | } 27 | } 28 | PostUpdate(); 29 | Animator.RunTweens(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Other/BLU.cs: -------------------------------------------------------------------------------- 1 | using GaugeOMatic.Trackers; 2 | using GaugeOMatic.Windows.Dropdowns; 3 | using System.Collections.Generic; 4 | using static GaugeOMatic.GameData.JobData; 5 | using static GaugeOMatic.GameData.JobData.Job; 6 | using static GaugeOMatic.GameData.JobData.Role; 7 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 8 | 9 | namespace GaugeOMatic.JobModules; 10 | 11 | public class BLUModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 12 | : JobModule(trackerManager, trackerConfigList, "JobHudBLU0", "JobHudBLU1") 13 | { 14 | public override Job Job => BLU; 15 | public override Job Class => Job.None; 16 | public override Role Role => Caster | Limited; 17 | 18 | public override List AddonOptions => 19 | [ 20 | new("JobHudBLU0", "Elemental Gauge"), 21 | new("_ParameterWidget", "Parameter Bar") 22 | ]; 23 | 24 | public override void Save() 25 | { 26 | Configuration.TrackerConfigs.BLU = SaveOrder; 27 | Configuration.Save(); 28 | } 29 | 30 | public override List JobGaugeMenu { get; } = []; 31 | 32 | public override void TweakUI() 33 | { 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /GaugeOMatic/Config/Configuration.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Configuration; 2 | using Dalamud.Plugin; 3 | using GaugeOMatic.JobModules; 4 | using GaugeOMatic.Trackers; 5 | using GaugeOMatic.Trackers.Presets; 6 | using Newtonsoft.Json; 7 | using System; 8 | using System.Collections.Generic; 9 | using static GaugeOMatic.GameData.JobData; 10 | using static GaugeOMatic.Windows.ConfigWindow; 11 | using static GaugeOMatic.Windows.ConfigWindow.GeneralTab; 12 | 13 | namespace GaugeOMatic.Config; 14 | 15 | [Serializable] 16 | public class Configuration : IPluginConfiguration 17 | { 18 | public int Version { get; set; } = 0; 19 | 20 | [JsonIgnore] public GeneralTab GeneralTab { get; set; } = Jobs; 21 | [JsonIgnore] public Job JobTab { get; set; } = Current; 22 | 23 | public List SavedPresets { get; set; } = []; 24 | public int PresetFiltering { get; set; } = 2; 25 | 26 | public TrackerConfigs TrackerConfigs { get; set; } = new(); 27 | public TweakConfigs TweakConfigs { get; set; } = new(); 28 | 29 | [NonSerialized] 30 | internal IDalamudPluginInterface? PluginInterface; 31 | 32 | public void Initialize(IDalamudPluginInterface pluginInterface) => PluginInterface = pluginInterface; 33 | public void Save() => PluginInterface!.SavePluginConfig(this); 34 | } 35 | -------------------------------------------------------------------------------- /GaugeOMatic/Trackers/TrackerData.cs: -------------------------------------------------------------------------------- 1 | namespace GaugeOMatic.Trackers; 2 | 3 | public abstract partial class Tracker 4 | { 5 | public TrackerData CurrentData; 6 | public TrackerData PreviousData; 7 | 8 | public struct TrackerData 9 | { 10 | public int Count = 0; // A counter value such as Charges or Stacks 11 | public int MaxCount; 12 | public float GaugeValue = 0; // Usually Time, but could also be job resources, HP/MP, etc 13 | public float MaxGauge; 14 | public int State = 0; // Status Active / Action Ready / etc 15 | public int MaxState; 16 | 17 | public bool HasLabelOverride = false; 18 | public string? LabelOverride = null; 19 | public uint? IconOverride = null; 20 | 21 | public TrackerData(int count, int maxCount, float gaugeValue, float maxGauge, int state, int maxState, float? preview = null) 22 | { 23 | Count = preview == null ? count : (int)(preview.Value * maxCount); 24 | MaxCount = maxCount; 25 | GaugeValue = preview == null ? gaugeValue : preview.Value * maxGauge; 26 | MaxGauge = maxGauge; 27 | State = preview == null ? state : (int)(preview.Value * maxState); 28 | MaxState = maxState; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /GaugeOMatic/GaugeOMatic - Backup.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ItsBexy 7 | 8 | 0.0.1.2 9 | Gauge-O-Matic 10 | 11 | https://github.com/ItsBexy/GaugeOMatic 12 | AGPL-3.0-or-later 13 | false 14 | preview 15 | README.md 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | True 31 | \ 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /GaugeOMatic/GaugeOMatic - Backup (1).csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ItsBexy 7 | 8 | 0.0.2.1 9 | Gauge-O-Matic 10 | 11 | https://github.com/ItsBexy/GaugeOMatic 12 | AGPL-3.0-or-later 13 | false 14 | preview 15 | README.md 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | True 31 | \ 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /GaugeOMatic/Windows/Dropdowns/AddonDropdown.cs: -------------------------------------------------------------------------------- 1 | using GaugeOMatic.Trackers; 2 | using System.Collections.Generic; 3 | 4 | namespace GaugeOMatic.Windows.Dropdowns; 5 | 6 | public struct AddonOption(string name, string displayName) 7 | { 8 | public string Name = name; 9 | public string DisplayName = displayName; 10 | } 11 | 12 | public class AddonDropdown : Dropdown 13 | { 14 | public AddonDropdown(Tracker tracker) 15 | { 16 | Tracker = tracker; 17 | Prepare(Tracker.JobModule.AddonOptions); 18 | } 19 | 20 | public sealed override List Values { get; } = []; 21 | public sealed override List DisplayNames { get; } = []; 22 | public Tracker Tracker; 23 | 24 | public void Prepare(List addonOptions) 25 | { 26 | Values.Clear(); 27 | DisplayNames.Clear(); 28 | 29 | var widgetInfo = Tracker.Widget?.GetAttributes; 30 | 31 | var whiteList = widgetInfo?.WhiteList; 32 | var blackList = widgetInfo?.BlackList; 33 | foreach (var option in addonOptions) 34 | { 35 | if (whiteList is { Count: > 0 } && !whiteList.Contains(option.Name)) continue; 36 | if (blackList is { Count: > 0 } && blackList.Contains(option.Name)) continue; 37 | Values.Add(option.Name); 38 | DisplayNames.Add(option.DisplayName); 39 | } 40 | 41 | Index = Values.IndexOf(Tracker.TrackerConfig.AddonName); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /GaugeOMatic.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.7.34031.279 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GaugeOMatic", "GaugeOMatic\GaugeOMatic.csproj", "{13C812E9-0D42-4B95-8646-40EEBF30636F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Release|Any CPU = Release|Any CPU 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|Any CPU.ActiveCfg = Debug|x64 17 | {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|Any CPU.Build.0 = Debug|x64 18 | {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|x64.ActiveCfg = Release|x64 19 | {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|x64.Build.0 = Release|x64 20 | {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|Any CPU.ActiveCfg = Release|x64 21 | {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|Any CPU.Build.0 = Release|x64 22 | {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|x64.ActiveCfg = Release|x64 23 | {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {B17E85B1-5F60-4440-9F9A-3DDE877E8CDF} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /GaugeOMatic/Utility/Service.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Game; 2 | using Dalamud.IoC; 3 | using Dalamud.Plugin.Services; 4 | 5 | // ReSharper disable UnusedAutoPropertyAccessor.Local 6 | // ReSharper disable UnusedMember.Global 7 | #pragma warning disable CS8618 8 | 9 | namespace GaugeOMatic; 10 | 11 | public sealed partial class GaugeOMatic 12 | { 13 | internal class Service 14 | { 15 | [PluginService] internal static IAddonLifecycle AddonLifecycle { get; private set; } 16 | [PluginService] internal static IClientState ClientState { get; private set; } 17 | [PluginService] internal static ICommandManager CommandManager { get; private set; } 18 | [PluginService] internal static ICondition Condition { get; private set; } 19 | [PluginService] internal static IDataManager DataManager { get; private set; } 20 | [PluginService] internal static IFramework Framework { get; private set; } 21 | [PluginService] internal static IGameConfig GameConfig { get; private set; } 22 | [PluginService] internal static IGameGui GameGui { get; private set; } 23 | [PluginService] internal static IGameInteropProvider GameInteropProvider { get; private set; } 24 | [PluginService] internal static IPartyList PartyList { get; private set; } 25 | [PluginService] internal static IPluginLog Log { get; private set; } 26 | [PluginService] internal static ISigScanner SigScanner { get; private set; } 27 | [PluginService] internal static ITextureProvider TextureProvider { get; private set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /GaugeOMatic/CustomNodes/CustomNode.NineGrid.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Component.GUI; 2 | using System; 3 | using static FFXIVClientStructs.FFXIV.Component.GUI.NodeFlags; 4 | using static FFXIVClientStructs.FFXIV.Component.GUI.NodeType; 5 | using static GaugeOMatic.Utility.MemoryHelper; 6 | 7 | namespace CustomNodes; 8 | 9 | public unsafe partial class CustomNodeManager 10 | { 11 | public static AtkNineGridNode* CreateNineGridNode(CustomPartsList customPartsList, ushort partId) 12 | { 13 | try 14 | { 15 | var partsList = customPartsList.AtkUldPartsList; 16 | var node = CleanAlloc(); 17 | 18 | node->Ctor(); 19 | node->PartId = partId; 20 | node->PartsList = partsList; 21 | 22 | node->AtkResNode.NodeId = GetFreeId(); 23 | 24 | RegisteredNodes.Add(node->AtkResNode.NodeId, (AtkResNode*)node); 25 | 26 | node->AtkResNode.Type = NineGrid; 27 | node->AtkResNode.NodeFlags = Visible | AnchorLeft | AnchorTop | Enabled; 28 | node->AtkResNode.Width = partsList->Parts[partId].Width; 29 | node->AtkResNode.Height = partsList->Parts[partId].Height; 30 | 31 | InitializePosition(node, 0, 0); 32 | 33 | return node; 34 | } 35 | catch (Exception ex) 36 | { 37 | Log.Error(ex + ""); 38 | return null; 39 | } 40 | } 41 | 42 | private static void InitializePosition(AtkNineGridNode* node, float x, float y) 43 | { 44 | node->AtkResNode.SetPositionFloat(1, 1); 45 | node->AtkResNode.SetPositionFloat(x, y); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /GaugeOMatic/CustomNodes/CustomNode.Timeline.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using GaugeOMatic.CustomNodes.Animation; 3 | using System.Linq; 4 | 5 | // ReSharper disable UnusedMember.Global 6 | // ReSharper disable UnusedMethodReturnValue.Global 7 | 8 | namespace CustomNodes; 9 | 10 | public partial class CustomNode 11 | { 12 | public Tween? Timeline; 13 | 14 | public float Progress 15 | { 16 | get => Prog; 17 | set 18 | { 19 | Prog = value; 20 | Timeline?.Update(value); 21 | } 22 | } 23 | 24 | internal float Prog; 25 | 26 | public CustomNode DefineTimeline(params KeyFrame[] keyFrames) 27 | { 28 | Timeline = new(this, keyFrames.Select(static k => k with { TimelineProg = null }).ToArray()); 29 | return this; 30 | } 31 | 32 | public CustomNode DefineTimeline(Tween tween) 33 | { 34 | tween.Target = this; 35 | Timeline = tween; 36 | return this; 37 | } 38 | 39 | public CustomNode SetProgress(float f) 40 | { 41 | Progress = f; 42 | return this; 43 | } 44 | 45 | public CustomNode SetProgress(CustomNode n) 46 | { 47 | Progress = n.Progress; 48 | return this; 49 | } 50 | 51 | public static implicit operator Tween(CustomNode n) => new(n, new(0) { TimelineProg = 0 }, new(n.Timeline?.Length ?? 1) { TimelineProg = 1 }); 52 | 53 | public IEnumerable GetDescendants() 54 | { 55 | var descendants = Children.ToList(); 56 | 57 | foreach (var node in descendants) descendants = descendants.Concat(node.GetDescendants()).ToList(); 58 | 59 | return descendants.Distinct().ToList(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /GaugeOMatic/Utility/Json.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using static GaugeOMatic.Utility.Color; 4 | 5 | namespace GaugeOMatic.Utility; 6 | 7 | public class Json 8 | { 9 | private static readonly JsonConverter[] JsonConverters = 10 | [ 11 | new ColorRGBConverter(), 12 | new AddRGBConverter() 13 | ]; 14 | 15 | internal static JsonSerializerSettings JsonSettings = new() 16 | { 17 | Converters = JsonConverters, 18 | 19 | TypeNameHandling = TypeNameHandling.None, 20 | TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple, 21 | Formatting = Formatting.None, 22 | FloatFormatHandling = FloatFormatHandling.Symbol, 23 | FloatParseHandling = FloatParseHandling.Double, 24 | StringEscapeHandling = StringEscapeHandling.Default, 25 | DefaultValueHandling = DefaultValueHandling.Ignore 26 | }; 27 | 28 | public class ColorRGBConverter : JsonConverter 29 | { 30 | public override void WriteJson(JsonWriter writer, ColorRGB value, JsonSerializer serializer) => writer.WriteValue((string)value); 31 | public override ColorRGB ReadJson(JsonReader reader, Type objectType, ColorRGB existingValue, bool hasExistingValue, JsonSerializer serializer) => reader.Value != null ? (string)reader.Value : existingValue; 32 | } 33 | 34 | public class AddRGBConverter : JsonConverter 35 | { 36 | public override void WriteJson(JsonWriter writer, AddRGB value, JsonSerializer serializer) => writer.WriteValue((string)value); 37 | public override AddRGB ReadJson(JsonReader reader, Type objectType, AddRGB existingValue, bool hasExistingValue, JsonSerializer serializer) => reader.Value != null ? (string)reader.Value : existingValue; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Healer/AST.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using static GaugeOMatic.GameData.JobData; 7 | using static GaugeOMatic.GameData.JobData.Job; 8 | using static GaugeOMatic.GameData.JobData.Role; 9 | using static GaugeOMatic.JobModules.Tweaks; 10 | using static GaugeOMatic.Widgets.Common.WidgetUI; 11 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 12 | 13 | namespace GaugeOMatic.JobModules; 14 | 15 | public class ASTModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 16 | : JobModule(trackerManager, trackerConfigList, "JobHudAST0") 17 | { 18 | public override Job Job => AST; 19 | public override Job Class => Job.None; 20 | public override Role Role => Healer; 21 | public override List AddonOptions => 22 | [ 23 | new("JobHudAST0", "Arcana Gauge"), 24 | new("_ParameterWidget", "Parameter Bar") 25 | ]; 26 | 27 | public override void Save() 28 | { 29 | Configuration.TrackerConfigs.AST = SaveOrder; 30 | Configuration.Save(); 31 | } 32 | 33 | public override List JobGaugeMenu { get; } = []; 34 | 35 | public override void TweakUI() 36 | { 37 | Heading("Arcana Gauge"); 38 | ToggleControls("Hide Arcana Gauge", ref TweakConfigs.ASTHide0); 39 | } 40 | 41 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 42 | { 43 | var gauge = (AddonJobHudAST0*)gaugeAddon; 44 | VisibilityTweak(TweakConfigs.ASTHide0, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 45 | } 46 | 47 | } 48 | 49 | public partial class TweakConfigs 50 | { 51 | public bool ASTHide0; 52 | } 53 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Tank/GNB.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using static GaugeOMatic.GameData.JobData; 7 | using static GaugeOMatic.GameData.JobData.Job; 8 | using static GaugeOMatic.GameData.JobData.Role; 9 | using static GaugeOMatic.JobModules.Tweaks; 10 | using static GaugeOMatic.Widgets.Common.WidgetUI; 11 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 12 | 13 | namespace GaugeOMatic.JobModules; 14 | 15 | public class GNBModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 16 | : JobModule(trackerManager, trackerConfigList, "JobHudGNB0") 17 | { 18 | public override Job Job => GNB; 19 | public override Job Class => Job.None; 20 | public override Role Role => Tank; 21 | public override List AddonOptions => 22 | [ 23 | new("JobHudGNB0", "Powder Gauge"), 24 | new("_ParameterWidget", "Parameter Bar") 25 | ]; 26 | 27 | public override List JobGaugeMenu { get; } = [new("Powder Gauge", nameof(PowderGaugeTracker))]; 28 | 29 | public override void Save() 30 | { 31 | Configuration.TrackerConfigs.GNB = SaveOrder; 32 | Configuration.Save(); 33 | } 34 | 35 | public override void TweakUI() 36 | { 37 | Heading("Powder Gauge"); 38 | ToggleControls("Hide Powder Gauge", ref TweakConfigs.GNBHide0); 39 | } 40 | 41 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 42 | { 43 | var gauge = (AddonJobHudGNB0*)gaugeAddon; 44 | VisibilityTweak(TweakConfigs.GNBHide0, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GetNodeById(19)); 45 | } 46 | } 47 | 48 | public partial class TweakConfigs 49 | { 50 | public bool GNBHide0; 51 | } 52 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Tank/WAR.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using static GaugeOMatic.GameData.JobData; 7 | using static GaugeOMatic.GameData.JobData.Job; 8 | using static GaugeOMatic.GameData.JobData.Role; 9 | using static GaugeOMatic.JobModules.Tweaks; 10 | using static GaugeOMatic.Widgets.Common.WidgetUI; 11 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 12 | 13 | namespace GaugeOMatic.JobModules; 14 | 15 | public class WARModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 16 | : JobModule(trackerManager, trackerConfigList, "JobHudWAR0") 17 | { 18 | public override Job Job => WAR; 19 | public override Job Class => MRD; 20 | public override Role Role => Tank; 21 | public override List AddonOptions => 22 | [ 23 | new("JobHudWAR0", "Beast Gauge"), 24 | new("_ParameterWidget", "Parameter Bar") 25 | ]; 26 | 27 | public override List JobGaugeMenu { get; } = [new("Beast Gauge", nameof(BeastGaugeTracker))]; 28 | 29 | public override void Save() 30 | { 31 | Configuration.TrackerConfigs.WAR = SaveOrder; 32 | Configuration.Save(); 33 | } 34 | 35 | public override void TweakUI() 36 | { 37 | Heading("Beast Gauge"); 38 | ToggleControls("Hide Beast Gauge", ref TweakConfigs.WARHide0); 39 | } 40 | 41 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 42 | { 43 | var gauge = (AddonJobHudWAR0*)gaugeAddon; 44 | VisibilityTweak(TweakConfigs.WARHide0, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.BarContainer); 45 | } 46 | } 47 | 48 | public partial class TweakConfigs 49 | { 50 | public bool WARHide0; 51 | } 52 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Melee/DRG.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using static GaugeOMatic.GameData.JobData; 7 | using static GaugeOMatic.GameData.JobData.Job; 8 | using static GaugeOMatic.GameData.JobData.Role; 9 | using static GaugeOMatic.JobModules.Tweaks; 10 | using static GaugeOMatic.Widgets.Common.WidgetUI; 11 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 12 | 13 | namespace GaugeOMatic.JobModules; 14 | 15 | public class DRGModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 16 | : JobModule(trackerManager, trackerConfigList, "JobHudDRG0") 17 | { 18 | public override Job Job => DRG; 19 | public override Job Class => LNC; 20 | public override Role Role => Melee; 21 | public override List AddonOptions => 22 | [ 23 | new("JobHudDRG0", "Dragon Gauge"), 24 | new("_ParameterWidget", "Parameter Bar") 25 | ]; 26 | 27 | public override List JobGaugeMenu { get; } = [new("Firstminds' Focus", nameof(FirstmindsFocusTracker))]; 28 | 29 | public override void Save() 30 | { 31 | Configuration.TrackerConfigs.DRG = SaveOrder; 32 | Configuration.Save(); 33 | } 34 | 35 | public override void TweakUI() 36 | { 37 | Heading("Dragon Gauge"); 38 | ToggleControls("Hide Dragon Gauge", ref TweakConfigs.DRGHide0); 39 | } 40 | 41 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 42 | { 43 | var gauge = (AddonJobHudDRG0*)gaugeAddon; 44 | VisibilityTweak(TweakConfigs.DRGHide0, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 45 | } 46 | } 47 | 48 | public partial class TweakConfigs 49 | { 50 | public bool DRGHide0; 51 | } 52 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Tank/PLD.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using System; 4 | using System.Collections.Generic; 5 | using CustomNodes; 6 | using static GaugeOMatic.GameData.JobData; 7 | using static GaugeOMatic.GameData.JobData.Job; 8 | using static GaugeOMatic.GameData.JobData.Role; 9 | using static GaugeOMatic.JobModules.Tweaks; 10 | using static GaugeOMatic.Widgets.Common.WidgetUI; 11 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 12 | using GaugeOMatic.Windows.Dropdowns; 13 | 14 | namespace GaugeOMatic.JobModules; 15 | 16 | public class PLDModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 17 | : JobModule(trackerManager, trackerConfigList, "JobHudPLD0") 18 | { 19 | public override Job Job => PLD; 20 | public override Job Class => GLA; 21 | public override Role Role => Tank; 22 | public override List AddonOptions => 23 | [ 24 | new("JobHudPLD0", "Oath Gauge"), 25 | new("_ParameterWidget", "Parameter Bar") 26 | ]; 27 | 28 | public override List JobGaugeMenu { get; } = [new("Oath Gauge", nameof(OathGaugeTracker))]; 29 | 30 | public override void Save() 31 | { 32 | Configuration.TrackerConfigs.PLD = SaveOrder; 33 | Configuration.Save(); 34 | } 35 | 36 | public override void TweakUI() 37 | { 38 | Heading("Oath Gauge"); 39 | ToggleControls("Hide Oath Gauge", ref TweakConfigs.PLDHide0); 40 | } 41 | 42 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 43 | { 44 | var gauge = (AddonJobHudPLD0*)gaugeAddon; 45 | var gaugeIndex = new AddonIndex(gaugeAddon); 46 | VisibilityTweak(TweakConfigs.PLDHide0, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gaugeIndex[14u]); 47 | } 48 | } 49 | 50 | public partial class TweakConfigs 51 | { 52 | public bool PLDHide0; 53 | } 54 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Healer/WHM.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using static GaugeOMatic.GameData.JobData; 7 | using static GaugeOMatic.GameData.JobData.Job; 8 | using static GaugeOMatic.GameData.JobData.Role; 9 | using static GaugeOMatic.JobModules.Tweaks; 10 | using static GaugeOMatic.Widgets.Common.WidgetUI; 11 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 12 | 13 | namespace GaugeOMatic.JobModules; 14 | 15 | public class WHMModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 16 | : JobModule(trackerManager, trackerConfigList, "JobHudWHM0") 17 | { 18 | public override Job Job => WHM; 19 | public override Job Class => CNJ; 20 | public override Role Role => Healer; 21 | public override List AddonOptions => 22 | [ 23 | new("JobHudWHM0", "Healing Gauge"), 24 | new("_ParameterWidget", "Parameter Bar") 25 | ]; 26 | 27 | public override List JobGaugeMenu => 28 | [ 29 | new("Lilies", nameof(LilyTracker)), 30 | new("Blood Lily", nameof(BloodLilyTracker)) 31 | ]; 32 | 33 | public override void Save() 34 | { 35 | Configuration.TrackerConfigs.WHM = SaveOrder; 36 | Configuration.Save(); 37 | } 38 | 39 | public override void TweakUI() 40 | { 41 | Heading("Healing Gauge"); 42 | ToggleControls("Hide Healing Gauge", ref TweakConfigs.WHMHide0); 43 | } 44 | 45 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 46 | { 47 | var gauge = (AddonJobHudWHM0*)gaugeAddon; 48 | VisibilityTweak(TweakConfigs.WHMHide0, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 49 | } 50 | } 51 | 52 | public partial class TweakConfigs 53 | { 54 | public bool WHMHide0; 55 | } 56 | -------------------------------------------------------------------------------- /GaugeOMatic/Widgets/Widget.Nodes.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using CustomNodes; 3 | using FFXIVClientStructs.FFXIV.Component.GUI; 4 | using GaugeOMatic.CustomNodes.Animation; 5 | using static CustomNodes.CustomNodeManager; 6 | 7 | namespace GaugeOMatic.Widgets; 8 | 9 | public abstract unsafe partial class Widget 10 | { 11 | public virtual CustomPartsList[] PartsLists { get; } = []; 12 | 13 | public virtual CustomNode BuildContainer() => new(CreateResNode()); 14 | 15 | public Animator Animator = new(); 16 | public CustomNode WidgetContainer; 17 | public CustomNode WidgetRoot; 18 | 19 | public CustomNode ImageNodeFromPart(int list, ushort partId) => new(CreateImageNode(PartsLists[list], partId)); 20 | public CustomNode ClippingMaskFromPart(int list, ushort partId) => new(CreateClippingMaskNode(PartsLists[list], partId)); 21 | public CustomNode NineGridFromPart(int list, ushort partId) => new(CreateNineGridNode(PartsLists[list], partId)); 22 | public CustomNode NineGridFromPart(int list, ushort partId, int x, int y, int z, int w) => new CustomNode(CreateNineGridNode(PartsLists[list], partId)).SetNineGridOffset(x, y, z, w); 23 | 24 | public virtual CustomNode.Bounds GetBounds() => WidgetRoot.GetDescendants().Where(static n => n.Size is { X: > 0, Y: > 0 } && n.Visible).ToList(); 25 | 26 | public void DrawBounds(uint col = 0xffffffffu, int thickness = 1) => GetBounds().Draw(col, thickness); 27 | 28 | public virtual void ChangeScale(float amt) 29 | { 30 | Config.Scale += 0.05f * amt; 31 | } 32 | 33 | public AtkUnitBase* Addon; 34 | 35 | public void Attach() 36 | { 37 | if (Addon == null || WidgetRoot.Node == null) return; 38 | WidgetRoot.AttachTo(Addon); 39 | } 40 | 41 | public void Detach() 42 | { 43 | if (WidgetRoot.Node != null) WidgetRoot.Detach(); 44 | if (Addon != null) Addon->UldManager.UpdateDrawNodeList(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/Sheets.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Lumina.Excel; 5 | using static GaugeOMatic.GameData.JobData; 6 | using static GaugeOMatic.GameData.JobData.Role; 7 | using static GaugeOMatic.GameData.StatusRef.StatusActor; 8 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 9 | using Lumina.Excel.Sheets; 10 | using Action = Lumina.Excel.Sheets.Action; 11 | using ActionIndirection = Lumina.Excel.Sheets.ActionIndirection; 12 | using Status = Lumina.Excel.Sheets.Status; 13 | using ActionProcStatus = Lumina.Excel.Sheets.ActionProcStatus; 14 | 15 | namespace GaugeOMatic.GameData; 16 | 17 | internal static class Sheets 18 | { 19 | public static ExcelSheet? ActionSheet { get; } = DataManager.Excel.GetSheet(null,"Action"); 20 | public static ExcelSheet? StatusSheet { get; } = DataManager.Excel.GetSheet(null,"Status"); 21 | public static ExcelSheet? ApsSheet { get; } = DataManager.Excel.GetSheet(null,"ActionProcStatus"); 22 | public static SubrowExcelSheet? ActionUiSheet { get; } = DataManager.Excel.GetSubrowSheet(null,"ClassJobActionUI"); 23 | public static ExcelSheet? ActionIndirectionSheet { get; } = DataManager.Excel.GetSheet(null,"ActionIndirection"); 24 | 25 | public static readonly List WhiteList = [3]; // Sprint sure is lonely 26 | 27 | public static Func ActionFilter = static a => WhiteList.Contains(a.RowId) || 28 | (!a.IsPvP && (a.IsPlayerAction || a.ActionProcStatus.RowId > 0) && 29 | (GetJobByCategory(a.ClassJobCategory.RowId) != Job.None || GetRoleByCategory(a.ClassJobCategory.RowId) != None)); 30 | 31 | 32 | public static List AllStatuses { get; } = 33 | [ 34 | ..StatusSheet.Select(static r => (MenuOption)new StatusRef(r.RowId, Job.None, Self, Self, 1, All)) 35 | ]; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /GaugeOMatic/CustomNodes/AddonIndex.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Component.GUI; 2 | using System; 3 | 4 | // ReSharper disable UnusedMember.Global 5 | 6 | namespace CustomNodes; 7 | 8 | public sealed unsafe class AddonIndex 9 | { 10 | public readonly AtkUnitBase* AtkUnitBase; 11 | 12 | public AddonIndex(string addonName) => AtkUnitBase = (AtkUnitBase*)GameGui.GetAddonByName(addonName); 13 | public AddonIndex(AtkUnitBase* atkUnitBase) => AtkUnitBase = atkUnitBase; 14 | public AddonIndex(IntPtr ptr) => AtkUnitBase = (AtkUnitBase*)ptr; 15 | 16 | public AtkResNode** NodeList => AtkUnitBase->UldManager.NodeList; 17 | public ushort NodeListSize => AtkUnitBase->UldManager.NodeListSize; 18 | 19 | public CustomNode this[uint id] 20 | { 21 | get 22 | { 23 | try 24 | { 25 | return AtkUnitBase->GetNodeById(id); 26 | } 27 | catch (Exception ex) 28 | { 29 | Log.Warning($"Error retrieving child node width ID {id}\n{ex}"); 30 | return new(); 31 | } 32 | } 33 | } 34 | 35 | public CustomNode this[int i] 36 | { 37 | get 38 | { 39 | try 40 | { 41 | return new(NodeListSize > i ? NodeList[i] : null); 42 | } 43 | catch (Exception ex) 44 | { 45 | Log.Warning($"Error retrieving child node at index {i}\n{ex}"); 46 | return new(); 47 | } 48 | } 49 | } 50 | 51 | public CustomNode this[params dynamic[] arr] 52 | { 53 | get 54 | { 55 | CustomNode result = this[arr[0]]; 56 | for (var i = 1; i < arr.Length; i++) result = result[arr[i]]; 57 | return result; 58 | } 59 | } 60 | 61 | public static implicit operator AtkUnitBase*(AddonIndex ai) => ai.AtkUnitBase; 62 | public static implicit operator AddonIndex(AtkUnitBase* aub) => new(aub); 63 | public static implicit operator AddonIndex(IntPtr ptr) => new((AtkUnitBase*)ptr); 64 | } 65 | -------------------------------------------------------------------------------- /GaugeOMatic/Widgets/Widget.Icon.cs: -------------------------------------------------------------------------------- 1 | using CustomNodes; 2 | using GaugeOMatic.CustomNodes.Animation; 3 | using GaugeOMatic.Trackers; 4 | using static CustomNodes.CustomNodeManager; 5 | using static GaugeOMatic.GameData.ParamRef; 6 | using static GaugeOMatic.Widgets.Common.CommonParts; 7 | 8 | namespace GaugeOMatic.Widgets; 9 | 10 | public abstract unsafe partial class Widget 11 | { 12 | public CustomNode WidgetIconContainer; 13 | public CustomNode WidgetIcon = null!; 14 | public CustomNode WidgetIconFrame = null!; 15 | 16 | public void UpdateIcon() 17 | { 18 | if (Tracker.CurrentData.IconOverride != null) 19 | { 20 | WidgetIcon.SetIcon(Tracker.CurrentData.IconOverride.Value); 21 | } 22 | } 23 | 24 | public void FadeIcon(bool show, int time = 300) => 25 | Animator.Add(new Tween(WidgetIconContainer, 26 | new(0, WidgetIconContainer), 27 | new(time) { Alpha = show ? 255 : 0 }) { Label = "ShowHide" }); 28 | 29 | public CustomNode BuildWidgetIcon(Tracker tracker) 30 | { 31 | WidgetIcon = new CustomNode(CreateIconNode(tracker.DisplayAttr.GameIcon)) 32 | .SetImageWrap(1) 33 | .SetSize(tracker.RefType switch 34 | { 35 | RefType.Status => new(24,32), 36 | RefType.JobGauge => new(32,32), 37 | _ => new(40) 38 | }); 39 | 40 | WidgetIconFrame = new CustomNode(CreateImageNode(IconFrame,0)) 41 | .SetScale(0.5f) 42 | .SetPos(-4,-3) 43 | .SetVis(tracker.RefType switch 44 | { 45 | RefType.Action => true, 46 | RefType.Parameter when tracker.ItemRef?.ID == (uint)ParamTypes.Castbar => true, 47 | _ => false 48 | }); 49 | 50 | return new CustomNode(CreateResNode(), WidgetIcon, WidgetIconFrame).SetScale(1f,1f).RemoveFlags(CustomNode.CustomNodeFlags.SetVisByAlpha); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/Structs/AddonJobHud.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using FFXIVClientStructs.FFXIV.Component.GUI; 3 | 4 | // ReSharper disable All 5 | 6 | namespace FFXIVClientStructs.FFXIV.Client.UI; 7 | 8 | /// 9 | /// Base struct for all job gauge addons. 10 | /// 11 | [StructLayout(LayoutKind.Explicit, Size = 0x260)] 12 | public unsafe partial struct AddonJobHud { 13 | [FieldOffset(0x000)] public AtkUnitBase AtkUnitBase; 14 | 15 | [FieldOffset(0x220)] public byte Unk220; 16 | [FieldOffset(0x221)] public bool UseSimpleGauge; 17 | [FieldOffset(0x222)] public byte Unk222; 18 | 19 | // these 4 pointers get set in vf72, and point to varying offsets for each type of gauge 20 | [FieldOffset(0x228)] public AddonJobHudGauge* GaugeStandard; 21 | [FieldOffset(0x230)] public AddonJobHudGauge* GaugeSimple; 22 | [FieldOffset(0x238)] public AddonJobHudGaugeData* DataPrevious; 23 | [FieldOffset(0x240)] public AddonJobHudGaugeData* DataCurrent; // Current is always used to apply updates, and then copied onto Previous. The two are compared to detect changes. 24 | 25 | [FieldOffset(0x248)] public AtkResNode* RootNode; 26 | 27 | [FieldOffset(0x250)] public int TimelineLabelStandard; // always set to 19 by vf75 28 | [FieldOffset(0x254)] public int TimelineLabelSimple; // always set to 101 by vf75 29 | 30 | /// 31 | /// Base struct containing the data that each particular gauge relies on.
32 | ///
33 | [StructLayout(LayoutKind.Explicit, Size = 0x08)] 34 | public unsafe partial struct AddonJobHudGaugeData { 35 | [FieldOffset(0x0)] public void* vtbl; 36 | } 37 | 38 | /// 39 | /// Base struct for the gauges themselves.
40 | /// The majority of the fields in any given gauge will be pointers to Nodes and Components within its addon, but data values are usually sprinkled in too. 41 | ///
42 | [StructLayout(LayoutKind.Explicit, Size = 0x10)] 43 | public unsafe partial struct AddonJobHudGauge { 44 | [FieldOffset(0x0)] public void* vtbl; 45 | [FieldOffset(0x8)] public AddonJobHud* JobHud; 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /GaugeOMatic/Widgets/Widget.Sound.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Numerics; 4 | using Dalamud.Interface.Utility.Raii; 5 | using FFXIVClientStructs.FFXIV.Client.UI; 6 | using ImGuiNET; 7 | using static GaugeOMatic.Widgets.MilestoneType; 8 | using static GaugeOMatic.Widgets.Common.WidgetUI; 9 | using static GaugeOMatic.Widgets.Common.WidgetUI.UpdateFlags; 10 | 11 | namespace GaugeOMatic.Widgets; 12 | 13 | public abstract partial class Widget 14 | { 15 | public static readonly List SoundBlackList = [19,21,74]; 16 | 17 | // ReSharper disable once UnusedMethodReturnValue.Global 18 | public static bool SoundControls(ref MilestoneType soundType, ref float soundMilestone, ref uint soundId, float max, bool percent = false) 19 | { 20 | var input1 = RadioControls("Play Sound", ref soundType, [None, Above, Below], ["Never", "Above Threshold", "Below Threshold"]); 21 | 22 | var scaledMilestone = soundMilestone * max; 23 | 24 | var input2 = soundType > 0 && FloatControls("Threshold", ref scaledMilestone, 0, max, 1, $"%.0f{(percent ? $" ({Math.Round(soundMilestone * 100)}%%)" : "")}"); 25 | if (input2) 26 | { 27 | soundMilestone = scaledMilestone / max; 28 | } 29 | 30 | using var col = ImRaii.PushColor(ImGuiCol.Text, new Vector4(1f, 0.35f, 0.35f, 1), SoundBlackList.Contains(soundId)); 31 | var input3 = soundType > 0 && IntControls("Sound ID (0-79)", ref soundId, 0, 79, 1); 32 | 33 | if (SoundBlackList.Contains(soundId)) 34 | { 35 | ImGui.TableNextColumn(); 36 | using (ImRaii.TextWrapPos(ImGui.GetWindowSize().X - 10)) 37 | { 38 | ImGui.TextWrapped($"Sound effect #{soundId} will not be played. It should never be played. We will not play it. We will not help you play it."); 39 | } 40 | } 41 | else 42 | { 43 | if (input3) 44 | { 45 | UIGlobals.PlaySoundEffect(soundId); 46 | } 47 | } 48 | 49 | if (input1 || input2 || input3) 50 | { 51 | UpdateFlag |= Save; 52 | return true; 53 | } 54 | 55 | return false; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /GaugeOMatic/CustomNodes/CustomNodeManager.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Component.GUI; 2 | using FFXIVClientStructs.Interop; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace CustomNodes; 7 | 8 | public unsafe partial class CustomNodeManager 9 | { 10 | public static void AttachNode(AtkUnitBase* parentAddon, AtkResNode* newChildNode) 11 | { 12 | if (parentAddon == null) return; 13 | AttachNode(parentAddon->RootNode, newChildNode); 14 | parentAddon->UldManager.UpdateDrawNodeList(); 15 | } 16 | 17 | public static void AttachNode(AtkResNode* parentNode, AtkResNode* newChildNode) 18 | { 19 | if (parentNode == null) return; 20 | try 21 | { 22 | var lastChildNode = parentNode->ChildNode; 23 | if (lastChildNode == null) return; 24 | 25 | while (lastChildNode->PrevSiblingNode != null) 26 | { 27 | if (lastChildNode == newChildNode) return; 28 | lastChildNode = lastChildNode->PrevSiblingNode; 29 | } 30 | 31 | if (lastChildNode == newChildNode) return; 32 | 33 | LinkSiblings(lastChildNode, newChildNode); 34 | newChildNode->ParentNode = parentNode; 35 | } 36 | catch (Exception ex) { Log.Error(ex + ""); } 37 | } 38 | 39 | public static void LinkSiblings(AtkResNode* nodeA, AtkResNode* nodeB) 40 | { 41 | if (nodeA == null || nodeB == null) return; 42 | nodeA->PrevSiblingNode = nodeB; 43 | nodeB->NextSiblingNode = nodeA; 44 | } 45 | 46 | public static void DetachNode(AtkResNode* node) 47 | { 48 | var sibling = node->NextSiblingNode; 49 | if (sibling != null && sibling->PrevSiblingNode == node) sibling->PrevSiblingNode = null; 50 | node->NextSiblingNode = null; 51 | } 52 | } 53 | 54 | public partial class CustomNodeManager 55 | { 56 | internal static Dictionary> RegisteredNodes = new(); 57 | 58 | public static unsafe uint GetFreeId() 59 | { 60 | for (uint i = 10000; i < 90000; i++) 61 | { 62 | if (RegisteredNodes.TryGetValue(i, out var node) && node.Value != null && node.Value->NodeId == i) continue; 63 | 64 | RegisteredNodes.Remove(i); 65 | return i; 66 | } 67 | 68 | return 90001; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/FrameworkData.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Game.ClientState.Objects.Types; 2 | using Dalamud.Game.ClientState.Statuses; 3 | using Dalamud.Plugin.Services; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using static Dalamud.Game.ClientState.Objects.Enums.ObjectKind; 7 | using static GaugeOMatic.GameData.JobData; 8 | 9 | namespace GaugeOMatic.GameData; 10 | 11 | public static class FrameworkData 12 | { 13 | internal struct PlayerData 14 | { 15 | internal Job Job = 0; 16 | internal byte Lvl = 1; 17 | internal uint CurHp; 18 | internal uint MaxHp; 19 | internal uint CurMp; 20 | internal uint MaxMp; 21 | internal bool IsCasting; 22 | internal float CurCastTime; 23 | internal float TotalCastTime; 24 | internal ulong? ObjId; 25 | internal uint? CastActionId; 26 | internal ulong? TargetObjId; 27 | internal List? PlayerStatus; 28 | internal List? EnemyStatus; 29 | 30 | public PlayerData(IBattleChara? localPlayer) 31 | { 32 | Job = (Job)(localPlayer?.ClassJob.RowId ?? 0); 33 | Lvl = localPlayer?.Level ?? 1; 34 | CurHp = localPlayer?.CurrentHp ?? 0; 35 | MaxHp = localPlayer?.MaxHp ?? 1; 36 | CurMp = localPlayer?.CurrentMp ?? 0; 37 | MaxMp = localPlayer?.MaxMp ?? 1; 38 | IsCasting = localPlayer?.IsCasting ?? false; 39 | CurCastTime = localPlayer?.CurrentCastTime ?? 0; 40 | TotalCastTime = localPlayer?.TotalCastTime ?? 1; 41 | ObjId = localPlayer?.GameObjectId ?? null; 42 | CastActionId = localPlayer?.CastActionId ?? null; 43 | TargetObjId = localPlayer?.TargetObject?.GameObjectId ?? null; 44 | 45 | PlayerStatus = localPlayer?.StatusList.ToList(); 46 | 47 | var target = localPlayer?.TargetObject; 48 | if (target?.ObjectKind == BattleNpc) 49 | { 50 | var enemyTarget = (IBattleNpc)target; 51 | EnemyStatus = enemyTarget.StatusList.ToList(); 52 | } 53 | else 54 | { 55 | EnemyStatus = null; 56 | } 57 | 58 | } 59 | } 60 | 61 | internal static PlayerData LocalPlayer; 62 | 63 | public static void UpdatePlayerData(IFramework framework) => LocalPlayer = new PlayerData(ClientState.LocalPlayer); 64 | } 65 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/Structs/AddonJobHudWAR.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using FFXIVClientStructs.Attributes; 3 | using FFXIVClientStructs.FFXIV.Component.GUI; 4 | using static FFXIVClientStructs.FFXIV.Client.UI.AddonJobHud; 5 | // ReSharper disable All 6 | namespace FFXIVClientStructs.FFXIV.Client.UI; 7 | 8 | /// 9 | /// WAR - Beast Gauge 10 | /// 11 | [Addon("JobHudWAR0")] 12 | [StructLayout(LayoutKind.Explicit, Size = 0x328)] 13 | public unsafe partial struct AddonJobHudWAR0 { 14 | [FieldOffset(0x000)] public AddonJobHud JobHud; 15 | 16 | [StructLayout(LayoutKind.Explicit, Size = 0x18)] 17 | public partial struct BeastGaugeData { 18 | [FieldOffset(0x00)] public AddonJobHudGaugeData GaugeData; 19 | [FieldOffset(0x08)] public fixed byte Prerequisites[3]; 20 | [FieldOffset(0x0C)] public int BeastValue; 21 | [FieldOffset(0x10)] public int BeastMax; 22 | } 23 | 24 | [StructLayout(LayoutKind.Explicit, Size = 0x58)] 25 | public partial struct BeastGauge { 26 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 27 | [FieldOffset(0x10)] public AtkResNode* Container; 28 | [FieldOffset(0x18)] public AtkResNode* Container2; 29 | [FieldOffset(0x20)] public AtkResNode* Container3; 30 | [FieldOffset(0x28)] public AtkComponentGaugeBar* BeastGaugeBar; 31 | [FieldOffset(0x30)] public AtkTextNode* BeastValueText; 32 | [FieldOffset(0x38)] public AtkComponentBase* StancePlateContainer; 33 | [FieldOffset(0x40)] public AtkResNode* StancePlate; 34 | [FieldOffset(0x48)] public AtkResNode* StanceGemLowLevel; 35 | } 36 | 37 | [StructLayout(LayoutKind.Explicit, Size = 0x40)] 38 | public partial struct BeastGaugeSimple { 39 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 40 | [FieldOffset(0x10)] public AtkResNode* BarContainer; 41 | [FieldOffset(0x18)] public AtkComponentGaugeBar* BeastGaugeBar; 42 | [FieldOffset(0x20)] public AtkResNode* BeastGaugeBarFill; 43 | [FieldOffset(0x28)] public AtkComponentTextNineGrid* BeastValueDisplay; 44 | [FieldOffset(0x30)] public AtkComponentBase* StanceIcon; 45 | } 46 | 47 | [FieldOffset(0x260)] public BeastGaugeData DataPrevious; 48 | [FieldOffset(0x278)] public BeastGaugeData DataCurrent; 49 | [FieldOffset(0x290)] public BeastGauge GaugeStandard; 50 | [FieldOffset(0x2E8)] public BeastGaugeSimple GaugeSimple; 51 | } 52 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/Structs/AddonJobHudPLD.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using FFXIVClientStructs.Attributes; 3 | using FFXIVClientStructs.FFXIV.Component.GUI; 4 | using static FFXIVClientStructs.FFXIV.Client.UI.AddonJobHud; 5 | // ReSharper disable All 6 | namespace FFXIVClientStructs.FFXIV.Client.UI; 7 | 8 | /// 9 | /// PLD - Oath Gauge 10 | /// 11 | [Addon("JobHudPLD0")] 12 | [StructLayout(LayoutKind.Explicit, Size = 0x330)] 13 | public unsafe partial struct AddonJobHudPLD0 { 14 | [FieldOffset(0x000)] public AddonJobHud JobHud; 15 | 16 | [StructLayout(LayoutKind.Explicit, Size = 0x18)] 17 | public partial struct OathGaugeData { 18 | [FieldOffset(0x00)] public AddonJobHudGaugeData GaugeData; 19 | [FieldOffset(0x08)] public fixed byte Prerequisites[3]; 20 | [FieldOffset(0x0C)] public int OathValue; 21 | [FieldOffset(0x10)] public int OathMax; 22 | } 23 | 24 | [StructLayout(LayoutKind.Explicit, Size = 0x58)] 25 | public partial struct OathGauge { 26 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 27 | [FieldOffset(0x10)] public AtkResNode* Container; 28 | [FieldOffset(0x18)] public AtkResNode* Container2; 29 | [FieldOffset(0x20)] public AtkComponentGaugeBar* OathGaugeBar; 30 | [FieldOffset(0x28)] public AtkComponentBase* OathMarker; 31 | [FieldOffset(0x30)] public AtkTextNode* OathValueText; 32 | [FieldOffset(0x38)] public AtkComponentBase* StanceSigilContainer; 33 | [FieldOffset(0x40)] public AtkResNode* StanceSigil; 34 | [FieldOffset(0x48)] public AtkResNode* StanceGemLowLevel; 35 | } 36 | 37 | [StructLayout(LayoutKind.Explicit, Size = 0x48)] 38 | public partial struct OathGaugeSimple { 39 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 40 | [FieldOffset(0x18)] public AtkResNode* Container; 41 | [FieldOffset(0x20)] public AtkComponentGaugeBar* OathGaugeBar; 42 | [FieldOffset(0x28)] public AtkResNode* OathGaugeBarFill; 43 | [FieldOffset(0x30)] public AtkComponentTextNineGrid* OathValueDisplay; 44 | [FieldOffset(0x38)] public AtkComponentBase* StanceIcon; 45 | } 46 | 47 | [FieldOffset(0x260)] public OathGaugeData DataPrevious; 48 | [FieldOffset(0x278)] public OathGaugeData DataCurrent; 49 | [FieldOffset(0x290)] public OathGauge GaugeStandard; 50 | [FieldOffset(0x2E8)] public OathGaugeSimple GaugeSimple; 51 | } 52 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/Structs/AddonJobHudGNB.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using FFXIVClientStructs.Attributes; 3 | using FFXIVClientStructs.FFXIV.Component.GUI; 4 | using FFXIVClientStructs.Interop; 5 | using FFXIVClientStructs.Interop.Attributes; 6 | using static FFXIVClientStructs.FFXIV.Client.UI.AddonJobHud; 7 | // ReSharper disable All 8 | namespace FFXIVClientStructs.FFXIV.Client.UI; 9 | 10 | /// 11 | /// GNB - Powder Gauge 12 | /// 13 | [Addon("JobHudGNB0")] 14 | [StructLayout(LayoutKind.Explicit, Size = 0x358)] 15 | public unsafe partial struct AddonJobHudGNB0 { 16 | [FieldOffset(0x000)] public AddonJobHud JobHud; 17 | 18 | [StructLayout(LayoutKind.Explicit, Size = 0x10)] 19 | public partial struct PowderGaugeData { 20 | [FieldOffset(0x00)] public AddonJobHudGaugeData GaugeData; 21 | [FieldOffset(0x08)] public fixed byte Prerequisites[4]; 22 | [FieldOffset(0x0C)] public int Ammo; 23 | } 24 | 25 | [StructLayout(LayoutKind.Explicit, Size = 0x70)] 26 | public partial struct PowderGauge { 27 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 28 | [FieldOffset(0x10)] public AtkResNode* Container; 29 | 30 | [FixedSizeArray>(3)] 31 | [FieldOffset(0x18)] public fixed byte Ammo[3 * 0x08]; 32 | 33 | [FixedSizeArray>(3)] 34 | [FieldOffset(0x30)] public fixed byte AmmoNode[3 * 0x08]; 35 | 36 | [FieldOffset(0x58)] public AtkResNode* RecoilNode; 37 | [FieldOffset(0x60)] public AtkResNode* AmmoPlate; 38 | [FieldOffset(0x68)] public AtkResNode* StanceIcon; 39 | } 40 | 41 | [StructLayout(LayoutKind.Explicit, Size = 0x68)] 42 | public partial struct PowderGaugeSimple { 43 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 44 | 45 | [FixedSizeArray>(3)] 46 | [FieldOffset(0x10)] public fixed byte AmmoGem[3 * 0x08]; 47 | 48 | [FixedSizeArray>(3)] 49 | [FieldOffset(0x28)] public fixed byte AmmoGemGlow[3 * 0x08]; 50 | 51 | [FieldOffset(0x48)] public AtkComponentBase* StanceIcon; 52 | [FieldOffset(0x58)] public AtkResNode* TwoGems; 53 | } 54 | 55 | [FieldOffset(0x260)] public PowderGaugeData DataPrevious; 56 | [FieldOffset(0x270)] public PowderGaugeData DataCurrent; 57 | [FieldOffset(0x280)] public PowderGauge GaugeStandard; 58 | [FieldOffset(0x2F0)] public PowderGaugeSimple GaugeSimple; 59 | } 60 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Healer/SGE.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using static GaugeOMatic.GameData.JobData; 7 | using static GaugeOMatic.GameData.JobData.Job; 8 | using static GaugeOMatic.GameData.JobData.Role; 9 | using static GaugeOMatic.JobModules.Tweaks; 10 | using static GaugeOMatic.Widgets.Common.WidgetUI; 11 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 12 | 13 | namespace GaugeOMatic.JobModules; 14 | 15 | public class SGEModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 16 | : JobModule(trackerManager, trackerConfigList, "JobHudGFF0", "JobHudGFF1") 17 | { 18 | public override Job Job => SGE; 19 | public override Job Class => Job.None; 20 | public override Role Role => Healer; 21 | public override List AddonOptions => 22 | [ 23 | new("JobHudGFF0", "Eukrasia"), 24 | new("JobHudGFF1", "Addersgall Gauge"), 25 | new("_ParameterWidget", "Parameter Bar") 26 | ]; 27 | 28 | public override List JobGaugeMenu => 29 | [ 30 | new("Eukrasia", nameof(EukrasiaTracker)), 31 | new("Addersgall Gauge", nameof(AddersgallTracker)), 32 | new("Addersting Counter", nameof(AdderstingTracker)) 33 | ]; 34 | 35 | public override void Save() 36 | { 37 | Configuration.TrackerConfigs.SGE = SaveOrder; 38 | Configuration.Save(); 39 | } 40 | 41 | public override void TweakUI() 42 | { 43 | Heading("Eukrasia"); 44 | ToggleControls("Hide Eukrasia", ref TweakConfigs.SGEHide0); 45 | 46 | Heading("Addersgall Gauge"); 47 | ToggleControls("Hide Addersgall Gauge", ref TweakConfigs.SGEHide1); 48 | } 49 | 50 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 51 | { 52 | var gauge = (AddonJobHudGFF0*)gaugeAddon; 53 | VisibilityTweak(TweakConfigs.SGEHide0, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 54 | } 55 | 56 | public override unsafe void ApplyTweaks1(IntPtr gaugeAddon) 57 | { 58 | var gauge = (AddonJobHudGFF1*)gaugeAddon; 59 | VisibilityTweak(TweakConfigs.SGEHide1, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GetNodeById(25)); 60 | } 61 | } 62 | 63 | public partial class TweakConfigs 64 | { 65 | public bool SGEHide0; 66 | public bool SGEHide1; 67 | } 68 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Tank/DRK.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using static GaugeOMatic.GameData.JobData; 7 | using static GaugeOMatic.GameData.JobData.Job; 8 | using static GaugeOMatic.GameData.JobData.Role; 9 | using static GaugeOMatic.JobModules.Tweaks; 10 | using static GaugeOMatic.Widgets.Common.WidgetUI; 11 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 12 | 13 | namespace GaugeOMatic.JobModules; 14 | 15 | public class DRKModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 16 | : JobModule(trackerManager, trackerConfigList, "JobHudDRK0", "JobHudDRK1") 17 | { 18 | public override Job Job => DRK; 19 | public override Job Class => Job.None; 20 | public override Role Role => Tank; 21 | public override List AddonOptions => 22 | [ 23 | new("JobHudDRK0", "Blood Gauge"), 24 | new("JobHudDRK1", "Darkside Gauge"), 25 | new("_ParameterWidget", "Parameter Bar") 26 | ]; 27 | 28 | public override List JobGaugeMenu { get; } = 29 | [ 30 | new("Blood Gauge", nameof(BloodGaugeTracker)), 31 | new("Darkside Gauge", nameof(DarksideGaugeTracker)), 32 | new("Living Shadow", nameof(LivingShadowTracker)) 33 | ]; 34 | 35 | public override void Save() 36 | { 37 | Configuration.TrackerConfigs.DRK = SaveOrder; 38 | Configuration.Save(); 39 | } 40 | 41 | public override void TweakUI() 42 | { 43 | Heading("Blood Gauge"); 44 | ToggleControls("Hide Blood Gauge", ref TweakConfigs.DRKHide0); 45 | 46 | Heading("Darkside Gauge"); 47 | ToggleControls("Hide Darkside Gauge", ref TweakConfigs.DRKHide1); 48 | } 49 | 50 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 51 | { 52 | var gauge = (AddonJobHudDRK0*)gaugeAddon; 53 | VisibilityTweak(TweakConfigs.DRKHide0, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 54 | } 55 | 56 | public override unsafe void ApplyTweaks1(IntPtr gaugeAddon) 57 | { 58 | var gauge = (AddonJobHudDRK1*)gaugeAddon; 59 | VisibilityTweak(TweakConfigs.DRKHide1, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GetNodeById(20)); 60 | } 61 | } 62 | 63 | public partial class TweakConfigs 64 | { 65 | public bool DRKHide0; 66 | public bool DRKHide1; 67 | } 68 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Ranged/DNC.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using static GaugeOMatic.GameData.JobData; 7 | using static GaugeOMatic.GameData.JobData.Job; 8 | using static GaugeOMatic.GameData.JobData.Role; 9 | using static GaugeOMatic.JobModules.Tweaks; 10 | using static GaugeOMatic.Widgets.Common.WidgetUI; 11 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 12 | 13 | namespace GaugeOMatic.JobModules; 14 | 15 | public class DNCModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 16 | : JobModule(trackerManager, trackerConfigList, "JobHudDNC0", "JobHudDNC1") 17 | { 18 | public override Job Job => DNC; 19 | public override Job Class => Job.None; 20 | public override Role Role => Ranged; 21 | public override List AddonOptions => 22 | [ 23 | new("JobHudDNC0", "Step Gauge"), 24 | new("JobHudDNC1", "Fourfold Feathers"), 25 | new("_ParameterWidget", "Parameter Bar") 26 | ]; 27 | 28 | public override List JobGaugeMenu { get; } = 29 | [ 30 | new("Fourfold Feathers", nameof(FourfoldTracker)), 31 | new("Esprit Gauge", nameof(EspritGaugeTracker)), 32 | new("Dance Steps", nameof(DanceStepTracker)) 33 | ]; 34 | 35 | public override void Save() 36 | { 37 | Configuration.TrackerConfigs.DNC = SaveOrder; 38 | Configuration.Save(); 39 | } 40 | 41 | public override void TweakUI() 42 | { 43 | Heading("Step Gauge"); 44 | ToggleControls("Hide Step Gauge", ref TweakConfigs.DNCHide0); 45 | 46 | Heading("Fourfold Feathers"); 47 | ToggleControls("Hide Fourfold Feathers", ref TweakConfigs.DNCHide1); 48 | } 49 | 50 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 51 | { 52 | var gauge = (AddonJobHudDNC0*)gaugeAddon; 53 | VisibilityTweak(TweakConfigs.DNCHide0, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 54 | } 55 | 56 | public override unsafe void ApplyTweaks1(IntPtr gaugeAddon) 57 | { 58 | var gauge = (AddonJobHudDNC1*)gaugeAddon; 59 | VisibilityTweak(TweakConfigs.DNCHide1, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 60 | } 61 | } 62 | 63 | public partial class TweakConfigs 64 | { 65 | public bool DNCHide0; 66 | public bool DNCHide1; 67 | } 68 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Melee/RPR.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using static GaugeOMatic.GameData.JobData; 7 | using static GaugeOMatic.GameData.JobData.Job; 8 | using static GaugeOMatic.GameData.JobData.Role; 9 | using static GaugeOMatic.JobModules.Tweaks; 10 | using static GaugeOMatic.Widgets.Common.WidgetUI; 11 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 12 | 13 | namespace GaugeOMatic.JobModules; 14 | 15 | public class RPRModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 16 | : JobModule(trackerManager, trackerConfigList, "JobHudRRP0", "JobHudRRP1") 17 | { 18 | public override Job Job => RPR; 19 | public override Job Class => Job.None; 20 | public override Role Role => Melee; 21 | public override List AddonOptions => 22 | [ 23 | new("JobHudRRP0", "Soul Gauge"), 24 | new("JobHudRRP1", "Death Gauge"), 25 | new("_ParameterWidget", "Parameter Bar") 26 | ]; 27 | 28 | public override List JobGaugeMenu { get; } = 29 | [ 30 | new("Soul Gauge", nameof(SoulGaugeTracker)), 31 | new("Shroud Gauge", nameof(ShroudGaugeTracker)), 32 | new("Lemure Shroud", nameof(LemureShroudTracker)), 33 | new("Void Shroud", nameof(VoidShroudTracker)) 34 | ]; 35 | 36 | public override void Save() 37 | { 38 | Configuration.TrackerConfigs.RPR = SaveOrder; 39 | Configuration.Save(); 40 | } 41 | 42 | public override void TweakUI() 43 | { 44 | Heading("Soul Gauge"); 45 | ToggleControls("Hide Soul Gauge", ref TweakConfigs.RPRHide0); 46 | 47 | Heading("Death Gauge"); 48 | ToggleControls("Hide Death Gauge", ref TweakConfigs.RPRHide1); 49 | } 50 | 51 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 52 | { 53 | var gauge = (AddonJobHudRRP0*)gaugeAddon; 54 | VisibilityTweak(TweakConfigs.RPRHide0, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 55 | } 56 | 57 | public override unsafe void ApplyTweaks1(IntPtr gaugeAddon) 58 | { 59 | var gauge = (AddonJobHudRRP1*)gaugeAddon; 60 | VisibilityTweak(TweakConfigs.RPRHide1, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 61 | } 62 | } 63 | 64 | public partial class TweakConfigs 65 | { 66 | public bool RPRHide0; 67 | public bool RPRHide1; 68 | } 69 | -------------------------------------------------------------------------------- /GaugeOMatic/Windows/Tooltips/StatusTooltip.cs: -------------------------------------------------------------------------------- 1 | using ImGuiNET; 2 | using System.Linq; 3 | using System.Numerics; 4 | using static Dalamud.Interface.Utility.ImGuiHelpers; 5 | using static GaugeOMatic.GameData.Sheets; 6 | using static GaugeOMatic.Utility.ImGuiHelpy; 7 | 8 | namespace GaugeOMatic.GameData; 9 | 10 | public partial class StatusRef 11 | { 12 | public override void TooltipHeaderText() 13 | { 14 | MulticolorText((Plain, Name), (Disabled, $" [{ID}]")); 15 | ImGui.TextDisabled(AppliedTo == StatusActor.Target ? "On Enemy Target" : "On Self"); 16 | } 17 | 18 | public override void DrawTooltipIcon(Vector2 startPos) 19 | { 20 | var texture = GetIconTexture(); 21 | if (texture != null) 22 | { 23 | ImGui.Image(texture.ImGuiHandle, new Vector2(30 * GlobalScale, 40 * GlobalScale) * 1.2f); 24 | ImGui.SameLine(); 25 | } 26 | 27 | ImGui.SetCursorPos(new(startPos.X + (50 * GlobalScale), 28 | startPos.Y + (4 * GlobalScale))); 29 | } 30 | 31 | public override bool UseCounterAsState() => MaxStacks <= 1; 32 | 33 | public override void PrintStateDesc() => ImGui.Text("Shows if active"); 34 | public override void FooterContents() 35 | { 36 | var seeAlsoFiltered = SeeAlso?.Where(static s => ((StatusRef)s).HideFromDropdown == false).ToArray(); 37 | if (seeAlsoFiltered?.Any() == true) 38 | { 39 | ImGui.TextDisabled($"Also checks the following status effect{(seeAlsoFiltered.Length > 1 ? "s" : "")}"); 40 | foreach (var id in seeAlsoFiltered) 41 | { 42 | var statusRef = (StatusRef)id; 43 | var row = StatusSheet?.GetRow(id); 44 | var icon = statusRef.Icon ?? row?.Icon; 45 | var name = statusRef.Name is { Length: > 0 } ? statusRef.Name : row?.Name.ToString() ?? "UNKNOWN"; 46 | 47 | if (icon != null) 48 | { 49 | DrawGameIcon(icon.Value, 22f); 50 | ImGui.SameLine(0,3); 51 | ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 2); 52 | } 53 | 54 | MulticolorText((Plain, $" {name}"), (Disabled, $" [{id}]")); 55 | 56 | 57 | } 58 | 59 | } 60 | } 61 | 62 | public override void PrintCounterDesc() => ImGui.Text($"Shows stacks ({MaxStacks})"); 63 | public override void PrintBarTimerDesc() 64 | { 65 | ImGui.Text(MaxTime > 0 ? $"Shows time remaining ({MaxTime}s)" : "Fills if active"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Caster/SMN.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using static GaugeOMatic.GameData.JobData; 7 | using static GaugeOMatic.GameData.JobData.Job; 8 | using static GaugeOMatic.GameData.JobData.Role; 9 | using static GaugeOMatic.JobModules.Tweaks; 10 | using static GaugeOMatic.Widgets.Common.WidgetUI; 11 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 12 | 13 | namespace GaugeOMatic.JobModules; 14 | 15 | public class SMNModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 16 | : JobModule(trackerManager, trackerConfigList, "JobHudSMN0", "JobHudSMN1") 17 | { 18 | public sealed override Job Job => SMN; 19 | public override Job Class => ACN; 20 | public override Role Role => Caster; 21 | public override List AddonOptions => 22 | [ 23 | new("JobHudSMN0", "Aetherflow Gauge"), 24 | new("JobHudSMN1", "Trance Gauge"), 25 | new("_ParameterWidget", "Parameter Bar") 26 | ]; 27 | 28 | public override List JobGaugeMenu => 29 | [ 30 | new("Aetherflow Gauge", nameof(AetherflowSMNGaugeTracker)), 31 | new("Fire Attunement", nameof(RubyTracker)), 32 | new("Earth Attunement", nameof(TopazTracker)), 33 | new("Wind Attunement", nameof(EmeraldTracker)), 34 | new("Summon Phase", nameof(SummonTracker)) 35 | ]; 36 | 37 | public override void Save() 38 | { 39 | Configuration.TrackerConfigs.SMN = SaveOrder; 40 | Configuration.Save(); 41 | } 42 | 43 | public override void TweakUI() 44 | { 45 | Heading("Aetherflow Gauge"); 46 | ToggleControls("Hide Aetherflow Gauge", ref TweakConfigs.SMNHide0); 47 | 48 | Heading("Trance Gauge"); 49 | ToggleControls("Hide Trance Gauge", ref TweakConfigs.SMNHide1); 50 | } 51 | 52 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 53 | { 54 | var gauge = (AddonJobHudSMN0*)gaugeAddon; 55 | VisibilityTweak(TweakConfigs.SMNHide0, gauge->UseSimpleGauge, gauge->GetNodeById(2), gauge->GetNodeById(11)); 56 | } 57 | 58 | public override unsafe void ApplyTweaks1(IntPtr gaugeAddon) 59 | { 60 | var gauge = (AddonJobHudSMN1*)gaugeAddon; 61 | VisibilityTweak(TweakConfigs.SMNHide1, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GetNodeById(82)); 62 | } 63 | } 64 | 65 | public partial class TweakConfigs 66 | { 67 | public bool SMNHide0; 68 | public bool SMNHide1; 69 | } 70 | -------------------------------------------------------------------------------- /GaugeOMatic/Widgets/WidgetConfig.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.ComponentModel; 3 | using System.Linq; 4 | using System.Numerics; 5 | using static GaugeOMatic.Utility.Json; 6 | using static Newtonsoft.Json.JsonConvert; 7 | 8 | namespace GaugeOMatic.Widgets; 9 | 10 | public enum MilestoneType { None, Above, Below } 11 | 12 | // container class for any/all widget configs associated with a tracker. 13 | // has a field for each type of widget (my sloppy way of getting around polymorphic deserialization) 14 | // each widget contributes a part to this class in its own file. 15 | public partial class WidgetConfig 16 | { 17 | public string? WidgetType { get; set; } 18 | public static implicit operator string(WidgetConfig w) => SerializeObject(w, JsonSettings); 19 | public static implicit operator WidgetConfig?(string s) => DeserializeObject(s) ?? null; 20 | 21 | public WidgetConfig CleanUp(string? widgetType) // nullify any configs in here that don't belong to the currently-set widget type 22 | { 23 | foreach (var p in 24 | from p in typeof(WidgetConfig).GetProperties() 25 | where p.GetValue(this) != null 26 | let decType = p.PropertyType.DeclaringType?.Name 27 | where decType != null && decType != widgetType 28 | select p) 29 | p.SetValue(this, null); 30 | 31 | return this; 32 | } 33 | } 34 | 35 | public class WidgetTypeConfig // base class for the individual types of configs found inside WidgetConfig 36 | { 37 | public Vector2 Position; 38 | [JsonIgnore] public virtual Vector2 DefaultPosition { get; } = new(0); 39 | [DefaultValue(1f)] public float Scale = 1; 40 | 41 | public bool ShowIcon; 42 | public Vector2 IconPosition; 43 | [DefaultValue(1f)] public float IconScale = 1; 44 | public MilestoneType SoundType; 45 | [DefaultValue(0.5f)] public float SoundMilestone = 0.5f; 46 | [DefaultValue(78)] public uint SoundId = 78; 47 | 48 | public WidgetTypeConfig(WidgetTypeConfig? config) 49 | { 50 | if (config == null) 51 | { 52 | Position = DefaultPosition; 53 | return; 54 | } 55 | 56 | Position = config.Position; 57 | Scale = config.Scale; 58 | ShowIcon = config.ShowIcon; 59 | IconPosition = config.IconPosition; 60 | IconScale = config.IconScale == 0 ? 1: config.IconScale; 61 | 62 | SoundType = config.SoundType; 63 | SoundMilestone = config.SoundMilestone; 64 | SoundId = config.SoundId; 65 | } 66 | 67 | public WidgetTypeConfig() => Position = DefaultPosition; 68 | } 69 | 70 | -------------------------------------------------------------------------------- /GaugeOMatic/Widgets/Widget.Events.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using Dalamud.Game.ClientState.Conditions; 6 | using GaugeOMatic.CustomNodes.Animation; 7 | using GaugeOMatic.GameData; 8 | using GaugeOMatic.Utility; 9 | using static GaugeOMatic.GameData.FrameworkData; 10 | 11 | namespace GaugeOMatic.Widgets; 12 | 13 | public abstract partial class Widget 14 | { 15 | public virtual string? SharedEventGroup => null; 16 | 17 | [SuppressMessage("ReSharper", "UnusedMember.Global")] 18 | public struct SharedEventArgs 19 | { 20 | public Color.ColorRGB? ColorRGB; 21 | public Color.AddRGB? AddRGB; 22 | public int? Intval; 23 | public float? Floatval; 24 | } 25 | 26 | public Dictionary> SharedEvents = new(); 27 | 28 | public void InvokeSharedEvent(string group, string eventLabel, SharedEventArgs? args = null) 29 | { 30 | foreach (var widget in Tracker.JobModule.WidgetList.Where(widget => widget?.SharedEventGroup == group)) 31 | if (widget!.SharedEvents.TryGetValue(eventLabel, out var action)) 32 | action.Invoke(args); 33 | } 34 | 35 | public bool IsVisible { get; set; } = true; 36 | 37 | public void ApplyDisplayRules() 38 | { 39 | if (!ClientState.IsPvP && (Tracker.UsePreviewValue || (CheckLevel() && CheckFlags()))) 40 | Show(); 41 | else 42 | Hide(); 43 | return; 44 | 45 | bool CheckLevel() 46 | { 47 | if (!TrackerConfig.LimitLevelRange) return true; 48 | if (TrackerConfig is { LevelMin: 1, LevelMax: JobData.LevelCap }) return true; 49 | var level = LocalPlayer.Lvl; 50 | return level >= TrackerConfig.LevelMin && level <= TrackerConfig.LevelMax; 51 | } 52 | 53 | bool CheckFlags() => !TrackerConfig.HideOutsideCombatDuty || 54 | Condition.Any(ConditionFlag.InCombat, ConditionFlag.BoundByDuty, 55 | ConditionFlag.BoundByDuty56, ConditionFlag.BoundByDuty95, 56 | ConditionFlag.InDeepDungeon); 57 | } 58 | 59 | public void Hide() 60 | { 61 | if (!IsVisible) return; 62 | 63 | IsVisible = false; 64 | Animator += new Tween(WidgetRoot, WidgetRoot, new(100) { Alpha = 0 }); 65 | } 66 | 67 | public void Show() 68 | { 69 | if (IsVisible) return; 70 | 71 | IsVisible = true; 72 | Animator += new Tween(WidgetRoot, WidgetRoot, new(100) { Alpha = 255 }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /GaugeOMatic/Windows/Dropdowns/Dropdown.cs: -------------------------------------------------------------------------------- 1 | using GaugeOMatic.Utility; 2 | using ImGuiNET; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Numerics; 6 | using Dalamud.Interface.Utility.Raii; 7 | using static Dalamud.Interface.Utility.ImGuiHelpers; 8 | 9 | namespace GaugeOMatic.Windows.Dropdowns; 10 | 11 | public abstract class Dropdown 12 | { 13 | public abstract List Values { get; } 14 | public abstract List DisplayNames { get; } 15 | public string[] ComboList => DisplayNames.ToArray(); 16 | public T CurrentSelection => Values[Index]; 17 | 18 | public int Index; 19 | 20 | public bool Draw(string label, float size) 21 | { 22 | var index = Index; 23 | 24 | ImGui.SetNextItemWidth(size * GlobalScale); 25 | if (ImGui.Combo($"##{label}", ref index, ComboList, ComboList.Length) && index != Index) 26 | { 27 | Index = index; 28 | return true; 29 | } 30 | 31 | return false; 32 | } 33 | } 34 | 35 | public abstract class BranchingDropdown 36 | { 37 | public bool IsOpen { get; set; } 38 | public abstract string DropdownText(string fallback); 39 | 40 | /// An indexed collection, whose values can inform the behaviour of the DrawSubMenu() method. 41 | public abstract ICollection SubMenus { get; } 42 | public abstract void DrawSubMenu(int i); 43 | 44 | public void Draw(string label, float width) 45 | { 46 | var i = 0; 47 | 48 | Vector2 windowPos; 49 | Vector2 cursorPos; 50 | using (ImRaii.PushColor(ImGuiCol.Button,ImGuiHelpy.GetStyleColorVec4(ImGuiCol.ButtonHovered),IsOpen)) 51 | { 52 | windowPos = ImGui.GetWindowPos(); 53 | cursorPos = ImGui.GetCursorPos(); 54 | ImGui.SetNextItemWidth(width * GlobalScale); 55 | ImGui.Combo($"##{label}{GetHashCode()}FakeCombo", ref i, DropdownText(label)); 56 | } 57 | 58 | var popupPos = new Vector2(windowPos.X + cursorPos.X, windowPos.Y + cursorPos.Y + 22f); 59 | CreateMenuPopup($"##{label}{GetHashCode()}MenuPopup", width * GlobalScale, popupPos); 60 | 61 | if (ImGui.IsItemClicked()) 62 | { 63 | ImGui.OpenPopup($"##{label}{GetHashCode()}MenuPopup"); 64 | } 65 | } 66 | 67 | public void CreateMenuPopup(string label, float width, Vector2 popupPos) 68 | { 69 | using var p = ImRaii.Popup(label); 70 | IsOpen = p.Success; 71 | if (IsOpen) 72 | { 73 | ImGui.SetWindowPos(popupPos); 74 | ImGui.Button("", new(width - 16f, 0.01f)); 75 | for (var i = 0; i < SubMenus.Count; i++) DrawSubMenu(i); 76 | ImGui.Button("", new(width - 16f, 0.01f)); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /GaugeOMatic/CustomNodes/CustomNode.Bounds.Line.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Numerics; 5 | 6 | namespace CustomNodes; 7 | 8 | public partial class CustomNode 9 | { 10 | public struct Line(Vector2 a, Vector2 b) : IEquatable 11 | { 12 | public Vector2 A { get; set; } = a; 13 | public Vector2 B { get; set; } = b; 14 | public readonly bool Equals(Line l2) => (A == l2.A && B == l2.B) || (A == l2.B && B == l2.A); 15 | public override readonly bool Equals(object? o2) => o2 is Line l2 && ((A == l2.A && B == l2.B) || (A == l2.B && B == l2.A)); 16 | public override readonly int GetHashCode() => HashCode.Combine(A, B); 17 | public static bool operator ==(Line left, Line right) => left.Equals(right); 18 | public static bool operator !=(Line left, Line right) => !left.Equals(right); 19 | 20 | public readonly bool Intersects(Line l2) => Intersects(l2, out _); 21 | 22 | // ReSharper disable once OutParameterValueIsAlwaysDiscarded.Global 23 | public readonly bool Intersects(Line l2, out Vector2 intersection) 24 | { 25 | intersection = default; 26 | if (l2.Equals(this)) return false; 27 | 28 | var m1 = Slope(); 29 | var m2 = l2.Slope(); 30 | 31 | if (m1 == null && m2 == null) return false; 32 | 33 | float iX; 34 | float iY; 35 | 36 | float? b1 = m1 != null ? A.Y - (m1.Value * A.X) : null; 37 | float? b2 = m2 != null ? l2.A.Y - (m2.Value * l2.A.X) : null; 38 | 39 | if (m1 == null) // l1 is vertical 40 | { 41 | iX = A.X; 42 | iY = (m2!.Value * iX) + b2!.Value; 43 | } 44 | else 45 | { 46 | if (m2 == null) iX = l2.A.X; // l2 is vertical 47 | else 48 | { 49 | var mDiff = m1.Value - m2.Value; 50 | if (Math.Abs(mDiff) < 0.00000001f) return false; 51 | iX = (b2!.Value - b1!.Value) / mDiff; 52 | } 53 | iY = (m1.Value * iX) + b1!.Value; 54 | } 55 | 56 | var xList = new List { A.X, B.X, l2.A.X, l2.B.X }; 57 | var yList = new List { A.Y, B.Y, l2.A.Y, l2.B.Y }; 58 | if (iX < xList.Min() || iX > xList.Max()) return false; 59 | if (iY > yList.Max() || iY < yList.Min()) return false; 60 | 61 | intersection = new(iX, iY); 62 | 63 | return true; 64 | } 65 | 66 | public readonly float? Slope() 67 | { 68 | var dX = B.X - A.X; 69 | var dY = B.Y - A.Y; 70 | return Math.Abs(dX) > 0.00000001f ? dY / dX : null; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Ranged/MCH.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Numerics; 7 | using static GaugeOMatic.GameData.JobData; 8 | using static GaugeOMatic.GameData.JobData.Job; 9 | using static GaugeOMatic.GameData.JobData.Role; 10 | using static GaugeOMatic.JobModules.Tweaks; 11 | using static GaugeOMatic.Widgets.Common.WidgetUI; 12 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 13 | 14 | namespace GaugeOMatic.JobModules; 15 | 16 | public class MCHModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 17 | : JobModule(trackerManager, trackerConfigList, "JobHudMCH0") 18 | { 19 | public override Job Job => MCH; 20 | public override Job Class => Job.None; 21 | public override Role Role => Ranged; 22 | public override List AddonOptions => 23 | [ 24 | new("JobHudMCH0", "Heat Gauge"), 25 | new("_ParameterWidget", "Parameter Bar") 26 | ]; 27 | 28 | public override List JobGaugeMenu { get; } = 29 | [ 30 | new("Heat Gauge", nameof(HeatGaugeTracker)), 31 | new("Battery Gauge", nameof(BatteryGaugeTracker)), 32 | new("Automaton Timer", nameof(AutomatonTracker)) 33 | ]; 34 | 35 | public override void Save() 36 | { 37 | Configuration.TrackerConfigs.MCH = SaveOrder; 38 | Configuration.Save(); 39 | } 40 | 41 | public override void TweakUI() 42 | { 43 | Heading("Heat Gauge"); 44 | ToggleControls("Hide Heat Gauge", ref TweakConfigs.MCHHide0Heat); 45 | ToggleControls("Hide Battery Gauge", ref TweakConfigs.MCHHide0Battery); 46 | 47 | if (!TweakConfigs.MCHHide0Battery) 48 | { 49 | PositionControls("Move Battery Gauge", ref TweakConfigs.MCHBatteryPos); 50 | } 51 | } 52 | 53 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 54 | { 55 | var gauge = (AddonJobHudMCH0*)gaugeAddon; 56 | VisibilityTweak(TweakConfigs.MCHHide0Heat, gauge->UseSimpleGauge, gauge->GetNodeById(3), gauge->GetNodeById(34)); 57 | VisibilityTweak(TweakConfigs.MCHHide0Battery, gauge->UseSimpleGauge, gauge->GetNodeById(17), gauge->GetNodeById(39)); 58 | 59 | if (gauge != null && gauge->GaugeStandard.HeatContainer != null) 60 | { 61 | var batteryPos = TweakConfigs.MCHBatteryPos; 62 | gauge->GaugeStandard.BatteryContainer->SetPositionFloat(batteryPos.X, batteryPos.Y + 59); 63 | gauge->GaugeSimple.BatteryContainer->SetPositionFloat(batteryPos.X, batteryPos.Y + 72); 64 | } 65 | } 66 | } 67 | 68 | public partial class TweakConfigs 69 | { 70 | public bool MCHHide0Heat; 71 | public bool MCHHide0Battery; 72 | public Vector2 MCHBatteryPos; 73 | } 74 | -------------------------------------------------------------------------------- /GaugeOMatic/CustomNodes/Animation/Animator.cs: -------------------------------------------------------------------------------- 1 | using CustomNodes; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using static GaugeOMatic.CustomNodes.Animation.Tween.EaseType; 7 | 8 | // ReSharper disable UnusedMember.Global 9 | // ReSharper disable UnusedMethodReturnValue.Global 10 | 11 | namespace GaugeOMatic.CustomNodes.Animation; 12 | 13 | public class Animator : IDisposable, IEnumerable 14 | { 15 | public List Tweens = []; 16 | 17 | public void RunTweens() => Tweens = 18 | [ 19 | ..Tweens.Where(static t => !t.IsStale) 20 | .Select(static t => t.Update()) 21 | ]; 22 | 23 | public Animator Add(Tween t) 24 | { 25 | Tweens.Add(t); 26 | return this; 27 | } 28 | 29 | public Animator Add(IEnumerable t) 30 | { 31 | Tweens = Tweens.Concat(t).ToList(); 32 | return this; 33 | } 34 | 35 | public Animator Add(params Tween[] t) 36 | { 37 | Tweens = Tweens.Concat(t).ToList(); 38 | return this; 39 | } 40 | 41 | public static Animator operator +(Animator a, Tween t) => a.Add(t); 42 | public static Animator operator +(Animator a, IEnumerable t) => a.Add(t); 43 | 44 | public Animator Clear() 45 | { 46 | Tweens.Clear(); 47 | return this; 48 | } 49 | 50 | public Animator Remove(Tween t) 51 | { 52 | Tweens.Remove(t); 53 | return this; 54 | } 55 | 56 | public Animator Remove(CustomNode node) 57 | { 58 | Tweens = [.. Tweens.Where(t => t.Target != node)]; 59 | return this; 60 | } 61 | 62 | public Animator Remove(string label) 63 | { 64 | Tweens = [.. Tweens.Where(t => t.Label != label)]; 65 | return this; 66 | } 67 | 68 | public Animator Remove(string label, params CustomNode[] nodes) 69 | { 70 | Tweens = [.. Tweens.Where(t => t.Label != label || !nodes.Contains(t.Target))]; 71 | return this; 72 | } 73 | 74 | public static Animator operator -(Animator tm, Tween t) => tm.Remove(t); 75 | public static Animator operator -(Animator tm, string label) => tm.Remove(label); 76 | public static Animator operator -(Animator tm, CustomNode n) => tm.Remove(n); 77 | 78 | public Animator PlayNodeTimeline(CustomNode node, float time, bool repeat = false, bool sin = false, float start = 0, float end = 1) => Add(new Tween(node, new(0) { TimelineProg = start }, new(time) { TimelineProg = end }) { Ease = sin ? SinInOut : Linear, Repeat = repeat }); 79 | public Animator TweenTo(CustomNode customNode, KeyFrame keyFrame) => Add(new Tween(customNode, customNode, keyFrame)); 80 | 81 | public void Dispose() => Clear(); 82 | 83 | public IEnumerator GetEnumerator() => Tweens.GetEnumerator(); 84 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 85 | } 86 | -------------------------------------------------------------------------------- /GaugeOMatic/CustomNodes/CustomNode.Image.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Component.GUI; 2 | using System; 3 | using System.Numerics; 4 | using static GaugeOMatic.Utility.MemoryHelper; 5 | 6 | namespace CustomNodes; 7 | 8 | public unsafe partial class CustomNodeManager 9 | { 10 | public static AtkImageNode* CreateImageNode(CustomPartsList customPartsList, ushort partId) 11 | { 12 | try 13 | { 14 | var node = CleanAlloc(); 15 | node->Ctor(); 16 | 17 | node->PartsList = customPartsList; 18 | node->PartId = partId; 19 | 20 | node->AtkResNode.Type = NodeType.Image; 21 | node->AtkResNode.NodeFlags = NodeFlags.Visible | NodeFlags.AnchorLeft | NodeFlags.AnchorTop | NodeFlags.Enabled | NodeFlags.EmitsEvents; 22 | node->AtkResNode.Width = customPartsList.AtkUldPartsList->Parts[partId].Width; 23 | node->AtkResNode.Height = customPartsList.AtkUldPartsList->Parts[partId].Height; 24 | 25 | node->AtkResNode.NodeId = GetFreeId(); 26 | RegisteredNodes.Add(node->AtkResNode.NodeId, (AtkResNode*)node); 27 | 28 | return node; 29 | } 30 | catch (Exception ex) 31 | { 32 | Log.Error("Failed to create image node!\n" + ex.StackTrace); 33 | return CreateResNode()->GetAsAtkImageNode(); 34 | } 35 | } 36 | 37 | public static AtkImageNode* CreateIconNode(uint iconId) 38 | { 39 | var iconNode = CreateImageNode(new CustomPartsList(CustomPartsList.CreateAsset(), new Vector4(0, 0, 40, 40)), 0); 40 | iconNode->LoadIconTexture(iconId,0); 41 | return iconNode; 42 | } 43 | 44 | public static AtkClippingMaskNode* CreateClippingMaskNode(CustomPartsList customPartsList, ushort partId) 45 | { 46 | try 47 | { 48 | var node = CleanAlloc(); 49 | node->Ctor(); 50 | 51 | node->PartsList = customPartsList; 52 | node->PartId = partId; 53 | 54 | node->AtkResNode.Type = NodeType.ClippingMask; 55 | node->AtkResNode.NodeFlags = NodeFlags.Visible | NodeFlags.AnchorLeft | NodeFlags.AnchorTop | NodeFlags.Enabled | NodeFlags.EmitsEvents; 56 | node->AtkResNode.Width = customPartsList.AtkUldPartsList->Parts[partId].Width; 57 | node->AtkResNode.Height = customPartsList.AtkUldPartsList->Parts[partId].Height; 58 | 59 | node->AtkResNode.NodeId = GetFreeId(); 60 | RegisteredNodes.Add(node->AtkResNode.NodeId, (AtkResNode*)node); 61 | 62 | return node; 63 | } 64 | catch (Exception ex) 65 | { 66 | Log.Error("Failed to create clipping mask node!\n" + ex.StackTrace); 67 | var node = (AtkClippingMaskNode*)CreateResNode(); 68 | node->Type = NodeType.ClippingMask; 69 | return node; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gauge-O-Matic 2 | 3 | This plugin allows FFXIV players to track additional information on their job gauges, choosing from a variety of unique widget designs. All trackers & widgets can be toggled on/off at will, pinned to different parts of the HUD, and customized with unique options per design. 4 | 5 | ![Example designs for Ninja, Dragoon, and Paladin](GaugeOMatic/Assets/showCase.jpg) 6 | 7 | This plugin is a work in progress! There are still many more widget designs on the way, and presets for most jobs are still in a rough draft stage. If you have a request for an element that you'd like to see on your job's gauge, an idea for a widget/tweak, or if you've come up with a preset that you want to share, be sure to share your feedback in the repo's discussion section. 8 | 9 | # Adding gauge elements 10 | There are two ways to add elements to your job gauge: setting them up manually, or loading a preset. 11 | 12 | ## Adding elements manually 13 | 14 | 1. Click "Add" 15 | 2. Select a tracker to use. There are three categories: 16 | - **Status Effects** - Retrieves the time remaining or number of stacks. 17 | - **Actions** - Retrieves the cooldown time or the number of charges available. 18 | - **Job Gauge** - Retrieves unique data options per job gauge. 19 | 3. Select a Widget. Widgets fall into these categories: 20 | - **Counters** - Shows a count of stacks or charges 21 | - **Bars & Timers** - Shows a timer or resource value 22 | - **State Indicators** - Toggles between different visual states (usually on/off) 23 | - **Multi-Component** - These are groups of widgets designed to layer on top of each other, making one combined design. 24 | 4. Customize! 25 | 26 | Each widget design has its own set of options to customize the way it looks and behaves. You can also choose which HUD element to pin each widget to, and control the order that widgets layer on top of each other. 27 | 28 | ## Using Presets 29 | 30 | Upon opening the preset window, you'll be presented with a list of installed presets. Selecting any one of these will show you its contents. From there you can: 31 | - Add elements from the preset individually 32 | - Copy a design from the preset onto one of your existing trackers 33 | - Add all elements at once 34 | - Replace your job's current trackers with the contents of the preset. 35 | 36 | > [!NOTE] 37 | > If the preset contains trackers that are not applicable to the selected job, they'll be greyed out, but you can still use the widget designs. 38 | 39 | ## Saving Presets 40 | 41 | You can save the trackers for your current job as a preset, or import a preset from elsewhere via the clipboard. 42 | 43 | # Gauge Tweaks 44 | 45 | There are a few options in place for customizing the appearance of the existing job gauges, rather than adding new elements to them. These are fairly limited for the time being, and most jobs only have the option to hide their gauges (allowing you to replace it with a different design, if you wish). 46 | -------------------------------------------------------------------------------- /GaugeOMatic/GaugeOMatic.cs: -------------------------------------------------------------------------------- 1 | global using static GaugeOMatic.GaugeOMatic.Service; 2 | using Dalamud.Interface.Windowing; 3 | using Dalamud.Plugin; 4 | using GaugeOMatic.Config; 5 | using GaugeOMatic.Trackers; 6 | using GaugeOMatic.Windows; 7 | using static GaugeOMatic.GameData.ActionRef; 8 | using static GaugeOMatic.GameData.FrameworkData; 9 | using static GaugeOMatic.Widgets.Common.CommonParts; 10 | using static GaugeOMatic.Widgets.WidgetAttribute; 11 | 12 | namespace GaugeOMatic; 13 | 14 | public sealed partial class GaugeOMatic : IDalamudPlugin 15 | { 16 | // ReSharper disable once UnusedMember.Global 17 | public static string Name => "Gauge-O-Matic"; 18 | private const string CommandName = "/gomatic"; 19 | internal static string PluginDirPath = null!; 20 | 21 | internal static WindowSystem WindowSystem = new("Gauge-O-Matic"); 22 | internal static ConfigWindow ConfigWindow { get; set; } = null!; 23 | internal static PresetWindow PresetWindow { get; set; } = null!; 24 | internal Configuration Configuration { get; set; } 25 | private IDalamudPluginInterface PluginInterface { get; init; } 26 | private TrackerManager TrackerManager { get; init; } 27 | 28 | public GaugeOMatic(IDalamudPluginInterface pluginInterface) 29 | { 30 | PluginInterface = pluginInterface; 31 | PluginInterface.Create(); 32 | PluginDirPath = PluginInterface.AssemblyLocation.DirectoryName!; 33 | 34 | Configuration = PluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); 35 | Configuration.Initialize(PluginInterface); 36 | 37 | PluginInterface.UiBuilder.Draw += DrawWindows; 38 | PluginInterface.UiBuilder.OpenConfigUi += OpenConfigWindow; 39 | 40 | BuildWidgetList(); 41 | PopulateActions(); 42 | 43 | TrackerManager = new(Configuration); 44 | 45 | ConfigWindow = new(TrackerManager); 46 | PresetWindow = new(TrackerManager); 47 | 48 | WindowSystem.AddWindow(ConfigWindow); 49 | WindowSystem.AddWindow(PresetWindow); 50 | 51 | Framework.Update += UpdatePlayerData; 52 | 53 | CommandManager.AddHandler(CommandName, new(OnCommand) { HelpMessage = "Open Gauge-O-Matic Settings" }); 54 | } 55 | 56 | public void Dispose() 57 | { 58 | PluginInterface.UiBuilder.Draw -= DrawWindows; 59 | PluginInterface.UiBuilder.OpenConfigUi -= OpenConfigWindow; 60 | Framework.Update -= UpdatePlayerData; 61 | 62 | TrackerManager.Dispose(); 63 | 64 | WindowSystem.RemoveAllWindows(); 65 | ConfigWindow.Dispose(); 66 | 67 | CommandManager.RemoveHandler(CommandName); 68 | 69 | DisposeSharedParts(); 70 | } 71 | 72 | public static void OnCommand(string command, string args) => ConfigWindow.IsOpen = !ConfigWindow.IsOpen; 73 | 74 | public static void DrawWindows() => WindowSystem.Draw(); 75 | public static void OpenConfigWindow() => ConfigWindow.IsOpen = true; 76 | } 77 | -------------------------------------------------------------------------------- /GaugeOMatic/GaugeOMatic.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ItsBexy 7 | 8 | 0.8.1.9 9 | Gauge-O-Matic 10 | 11 | https://github.com/ItsBexy/GaugeOMatic 12 | AGPL-3.0-or-later 13 | false 14 | preview 15 | README.md 16 | net9.0-windows7.0 17 | https://github.com/ItsBexy/GaugeOMatic 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | True 33 | \ 34 | 35 | 36 | 37 | 38 | 39 | 40 | $(DalamudLibPath)InteropGenerator.Runtime.dll 41 | false 42 | 43 | 44 | 45 | 46 | 47 | Always 48 | 49 | 50 | Always 51 | 52 | 53 | Always 54 | 55 | 56 | Always 57 | 58 | 59 | Always 60 | 61 | 62 | Always 63 | 64 | 65 | Always 66 | 67 | 68 | Always 69 | 70 | 71 | Always 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /GaugeOMatic/Windows/Tooltips/ItemRefTooltip.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Interface.Textures.TextureWraps; 2 | using ImGuiNET; 3 | using System.Numerics; 4 | using Dalamud.Interface.Utility.Raii; 5 | using static Dalamud.Interface.Utility.ImGuiHelpers; 6 | using static GaugeOMatic.Trackers.Tracker; 7 | 8 | namespace GaugeOMatic.GameData; 9 | 10 | public abstract partial class ItemRef 11 | { 12 | public virtual void DrawTooltip() 13 | { 14 | using (ImRaii.PushColor(ImGuiCol.PopupBg, new Vector4(0.03f, 0.03f, 0.03f, 1))) 15 | { 16 | using (var tt = ImRaii.Tooltip()) 17 | { 18 | if (tt.Success) { 19 | var startPos = ImGui.GetCursorPos(); 20 | 21 | DrawTooltipIcon(startPos); 22 | using (var gr = ImRaii.Group()) 23 | { 24 | if (gr.Success) TooltipHeaderText(); 25 | } 26 | 27 | ImGui.SetCursorPosY(startPos.Y + (50 * GlobalScale)); 28 | 29 | WidgetBehaviorTable(); 30 | 31 | FooterContents(); 32 | } 33 | } 34 | } 35 | } 36 | 37 | public void WidgetBehaviorTable() 38 | { 39 | ImGui.TextDisabled("Widget Behavior"); 40 | 41 | using var table = ImRaii.Table("BehaviorTable", 2); 42 | if (table.Success) { 43 | ImGui.TableSetupColumn("Widget"); 44 | ImGui.TableSetupColumn("Value"); 45 | 46 | ImGui.TableNextRow(); 47 | ImGui.TableNextColumn(); 48 | ImGui.Text("Bar / Timer:"); 49 | 50 | ImGui.TableNextColumn(); 51 | PrintBarTimerDesc(); 52 | 53 | ImGui.TableNextRow(); 54 | ImGui.TableNextColumn(); 55 | ImGui.Text("Counter:"); 56 | 57 | ImGui.TableNextColumn(); 58 | if (UseCounterAsState()) 59 | PrintStateDesc(); 60 | else 61 | PrintCounterDesc(); 62 | 63 | ImGui.TableNextRow(); 64 | ImGui.TableNextColumn(); 65 | ImGui.Text("State Indicator:"); 66 | ImGui.TableNextColumn(); 67 | PrintStateDesc(); 68 | } 69 | } 70 | 71 | public IDalamudTextureWrap? GetIconTexture() 72 | { 73 | if (Icon == null) return null; 74 | try 75 | { 76 | return TextureProvider.GetFromGameIcon(new(Icon.Value)) 77 | .TryGetWrap(out var texture, out _) ? texture : null; 78 | } 79 | catch 80 | { 81 | return null; 82 | } 83 | } 84 | 85 | public virtual void TooltipHeaderText() { } 86 | public virtual void DrawTooltipIcon(Vector2 startPos) { } 87 | public virtual bool UseCounterAsState() => false; 88 | public virtual void PrintBarTimerDesc() { } 89 | public virtual void PrintCounterDesc() { } 90 | public virtual void PrintStateDesc() { } 91 | public virtual void FooterContents() { } 92 | public abstract TrackerData GetTrackerData(float? preview); 93 | } 94 | -------------------------------------------------------------------------------- /GaugeOMatic/CustomNodes/CustomNode.Properties.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | using static CustomNodes.CustomNode.CustomNodeFlags; 4 | using static GaugeOMatic.Utility.Color; 5 | 6 | // ReSharper disable UnusedMember.Global 7 | 8 | namespace CustomNodes; 9 | 10 | public unsafe partial class CustomNode 11 | { 12 | [Flags] 13 | public enum CustomNodeFlags 14 | { 15 | SetVisByAlpha = 0x01 // toggles visibility when alpha changes to/from zero 16 | } 17 | 18 | public const CustomNodeFlags DefaultFlags = SetVisByAlpha; 19 | 20 | public CustomNodeFlags Flags = DefaultFlags; 21 | 22 | public bool Visible 23 | { 24 | get => Node->IsVisible(); 25 | set => Node->ToggleVisibility(value); 26 | } 27 | 28 | public float X 29 | { 30 | get => Node->X; 31 | set => SetX(value); 32 | } 33 | 34 | public float Y 35 | { 36 | get => Node->Y; 37 | set => SetY(value); 38 | } 39 | 40 | public Vector2 Position 41 | { 42 | get => new(Node->X, Node->Y); 43 | set => SetPos(value); 44 | } 45 | 46 | public ushort Width 47 | { 48 | get => Node->Width; 49 | set => SetWidth(value); 50 | } 51 | 52 | public ushort Height 53 | { 54 | get => Node->Height; 55 | set => SetHeight(value); 56 | } 57 | 58 | public Vector2 Size 59 | { 60 | get => new(Node->Width, Node->Height); 61 | set => SetSize(value); 62 | } 63 | 64 | public float ScaleX 65 | { 66 | get => Node->ScaleX; 67 | set => SetScaleX(value); 68 | } 69 | 70 | public float ScaleY 71 | { 72 | get => Node->ScaleY; 73 | set => SetScaleY(value); 74 | } 75 | 76 | public Vector2 Scale 77 | { 78 | get => new(Node->ScaleX, Node->ScaleY); 79 | set => SetScale(value); 80 | } 81 | 82 | public float OriginX 83 | { 84 | get => Node->OriginX; 85 | set => SetOriginX(value); 86 | } 87 | 88 | public float OriginY 89 | { 90 | get => Node->OriginY; 91 | set => SetOriginY(value); 92 | } 93 | 94 | public Vector2 Origin 95 | { 96 | get => new(Node->OriginX, Node->OriginY); 97 | set => SetOrigin(value); 98 | } 99 | 100 | public float Rotation 101 | { 102 | get => Node->Rotation; 103 | set => SetRotation(value); 104 | } 105 | 106 | public ColorRGB Color 107 | { 108 | get => Node->Color; 109 | set => SetRGBA(value); 110 | } 111 | 112 | public byte Alpha 113 | { 114 | get => Node->Color.A; 115 | set => SetAlpha(value); 116 | } 117 | 118 | public AddRGB Add 119 | { 120 | get => new(Node->AddRed, Node->AddGreen, Node->AddBlue); 121 | set => SetAddRGB(value); 122 | } 123 | 124 | public ColorRGB Multiply 125 | { 126 | get => new(Node->MultiplyRed, Node->MultiplyGreen, Node->MultiplyBlue); 127 | set => SetMultiply(value); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Ranged/BRD.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Numerics; 7 | using GaugeOMatic.GameData; 8 | using static GaugeOMatic.GameData.JobData; 9 | using static GaugeOMatic.GameData.JobData.Job; 10 | using static GaugeOMatic.GameData.JobData.Role; 11 | using static GaugeOMatic.JobModules.Tweaks; 12 | using static GaugeOMatic.Widgets.Common.WidgetUI; 13 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 14 | 15 | namespace GaugeOMatic.JobModules; 16 | 17 | public class BRDModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 18 | : JobModule(trackerManager, trackerConfigList, "JobHudBRD0") 19 | { 20 | public override Job Job => BRD; 21 | public override Job Class => ARC; 22 | public override Role Role => Ranged; 23 | public override List AddonOptions => 24 | [ 25 | new("JobHudBRD0", "Song Gauge"), 26 | new("_ParameterWidget", "Parameter Bar") 27 | ]; 28 | 29 | public override List JobGaugeMenu { get; } = [ 30 | new("Soul Voice Gauge", nameof(SoulVoiceGaugeTracker)), 31 | new("Song Tracker (Any)", nameof(SongTracker)), 32 | new("Army's Paeon", nameof(SongTrackerArmy)), 33 | new("Mage's Ballad", nameof(SongTrackerMage)), 34 | new("Wanderer's Minuet", nameof(SongTrackerWanderer)) 35 | ]; 36 | 37 | public override void Save() 38 | { 39 | Configuration.TrackerConfigs.BRD = SaveOrder; 40 | Configuration.Save(); 41 | } 42 | 43 | public override void TweakUI() 44 | { 45 | Heading("Song Gauge"); 46 | ToggleControls("Hide Song Gauge", ref TweakConfigs.BRDHide0Song); 47 | 48 | if (!TweakConfigs.BRDHide0Song) 49 | { 50 | ToggleControls("Hide Soul Voice Gauge", ref TweakConfigs.BRDHide0SoulVoice); 51 | 52 | if (!TweakConfigs.BRDHide0SoulVoice) 53 | { 54 | PositionControls("Move Soul Voice Gauge", ref TweakConfigs.BRDSoulVoicePos); 55 | } 56 | } 57 | } 58 | 59 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 60 | { 61 | var gauge = (AddonJobHudBRD0*)gaugeAddon; 62 | VisibilityTweak(TweakConfigs.BRDHide0Song, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 63 | VisibilityTweak(TweakConfigs.BRDHide0SoulVoice, gauge->UseSimpleGauge, gauge->GaugeStandard.SoulVoiceContainer, gauge->GaugeSimple.SoulVoiceContainer); 64 | 65 | if (gauge != null && gauge->GaugeStandard.Container != null) 66 | { 67 | var soulVoicePos = TweakConfigs.BRDSoulVoicePos; 68 | gauge->GaugeStandard.SoulVoiceContainer->SetPositionFloat(soulVoicePos.X + 16, soulVoicePos.Y + 98); 69 | gauge->GaugeSimple.SoulVoiceContainer->SetPositionFloat(soulVoicePos.X + 10, soulVoicePos.Y + 39); 70 | } 71 | } 72 | 73 | public static float BloodletterFix() => FrameworkData.LocalPlayer.Lvl < 84 ? 30f : 45f; 74 | } 75 | 76 | public partial class TweakConfigs 77 | { 78 | public bool BRDHide0Song; 79 | public bool BRDHide0SoulVoice; 80 | public Vector2 BRDSoulVoicePos; 81 | } 82 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Caster/RDM.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using Dalamud.Interface; 7 | using Dalamud.Interface.Components; 8 | using static GaugeOMatic.GameData.JobData; 9 | using static GaugeOMatic.GameData.JobData.Job; 10 | using static GaugeOMatic.GameData.JobData.Role; 11 | using static GaugeOMatic.GameData.StatusRef; 12 | using static GaugeOMatic.GameData.StatusRef.StatusActor; 13 | using static GaugeOMatic.JobModules.Tweaks; 14 | using static GaugeOMatic.Widgets.Common.WidgetUI; 15 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 16 | 17 | namespace GaugeOMatic.JobModules; 18 | 19 | public class RDMModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 20 | : JobModule(trackerManager, trackerConfigList, "JobHudRDM0") 21 | { 22 | public override Job Job => RDM; 23 | public override Job Class => Job.None; 24 | public override Role Role => Caster; 25 | public override List AddonOptions => 26 | [ 27 | new("JobHudRDM0", "Balance Gauge"), 28 | new("_ParameterWidget", "Parameter Bar") 29 | ]; 30 | 31 | public override List JobGaugeMenu { get; } = 32 | [ 33 | new("Black Mana", nameof(BlackManaTracker)), 34 | new("White Mana", nameof(WhiteManaTracker)), 35 | new("Mana Stacks", nameof(ManaStackTracker)), 36 | new("Balance Crystal", nameof(BalanceCrystalTracker)) 37 | ]; 38 | 39 | public override void Save() 40 | { 41 | Configuration.TrackerConfigs.RDM = SaveOrder; 42 | Configuration.Save(); 43 | } 44 | 45 | public override void TweakUI() 46 | { 47 | Heading("Balance Gauge"); 48 | ToggleControls("Hide Balance Gauge", ref TweakConfigs.RDMHide0); 49 | ToggleControls("Magicked Swordplay Cue", ref TweakConfigs.RDM0SwordplayCue); 50 | ImGuiComponents.HelpMarker("Cues the gauge to become highlighted after pressing\nManification and gaining the Magicked Swordplay buff",FontAwesomeIcon.QuestionCircle); 51 | } 52 | 53 | public bool SwordplayStatePrev; 54 | public bool SwordplayStateCurrent; 55 | 56 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 57 | { 58 | var gauge = (AddonJobHudRDM0*)gaugeAddon; 59 | ApplySwordplayCueTweak(gauge); 60 | VisibilityTweak(TweakConfigs.RDMHide0, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 61 | } 62 | 63 | private unsafe void ApplySwordplayCueTweak(AddonJobHudRDM0* gauge) 64 | { 65 | SwordplayStatePrev = SwordplayStateCurrent; 66 | SwordplayStateCurrent = TweakConfigs.RDM0SwordplayCue && 67 | StatusData[3875].TryGetStatus(out var buff, Self) && 68 | buff?.Param == 3; 69 | 70 | if (SwordplayStateCurrent) 71 | { 72 | if (!SwordplayStatePrev) UIGlobals.PlaySoundEffect(78); 73 | if (gauge->DataCurrent.BlackMana < 50 || gauge->DataCurrent.WhiteMana < 50) JobUiData->SetValue(3, 0, true); 74 | } 75 | } 76 | } 77 | 78 | public partial class TweakConfigs 79 | { 80 | public bool RDM0SwordplayCue; 81 | public bool RDMHide0; 82 | } 83 | -------------------------------------------------------------------------------- /GaugeOMatic/Trackers/TrackerConfig.cs: -------------------------------------------------------------------------------- 1 | using GaugeOMatic.GameData; 2 | using GaugeOMatic.JobModules; 3 | using GaugeOMatic.Widgets; 4 | using Newtonsoft.Json; 5 | using System; 6 | using System.ComponentModel; 7 | using System.Linq; 8 | using static GaugeOMatic.GameData.JobData; 9 | using static GaugeOMatic.GameData.JobData.Role; 10 | using static GaugeOMatic.GameData.ParamRef; 11 | using static GaugeOMatic.Widgets.WidgetAttribute; 12 | using static Newtonsoft.Json.JsonConvert; 13 | 14 | namespace GaugeOMatic.Trackers; 15 | 16 | public enum RefType { None, Status, Action, JobGauge, Parameter } 17 | 18 | public class TrackerConfig 19 | { 20 | public string TrackerType { get; set; } = null!; 21 | public uint ItemId; 22 | public string AddonName { get; set; } = null!; 23 | public WidgetConfig WidgetConfig = null!; 24 | 25 | public bool Enabled { get; set; } 26 | 27 | public bool LimitLevelRange = false; 28 | 29 | [DefaultValue(1)] 30 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)] 31 | public byte LevelMin { get; set; } = 1; 32 | 33 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)] 34 | [DefaultValue(LevelCap)] 35 | public byte LevelMax { get; set; } = LevelCap; 36 | 37 | public bool HideOutsideCombatDuty; 38 | 39 | [JsonIgnore] public int Index { get; set; } 40 | [JsonIgnore] public bool Preview { get; set; } 41 | [JsonIgnore] public float PreviewValue { get; set; } = 1f; 42 | 43 | [JsonIgnore] 44 | public string? WidgetType 45 | { 46 | get => WidgetConfig.WidgetType; 47 | set => WidgetConfig.WidgetType = value; 48 | } 49 | 50 | [JsonIgnore] public TrackerDisplayAttribute? DisplayAttr; 51 | public TrackerDisplayAttribute GetDisplayAttr() => 52 | DisplayAttr ??= TrackerType switch 53 | { 54 | nameof(StatusTracker) => new((StatusRef)ItemId), 55 | nameof(ActionTracker) => new((ActionRef)ItemId), 56 | nameof(ParameterTracker) => Attrs[(ParamTypes)ItemId], 57 | _ => Type.GetType($"{typeof(Tracker).Namespace}.{TrackerType}")? 58 | .GetCustomAttributes(typeof(TrackerDisplayAttribute), true) 59 | .FirstOrDefault() as TrackerDisplayAttribute ?? new() 60 | }; 61 | 62 | public TrackerConfig? Clone() => DeserializeObject(SerializeObject(this)); 63 | 64 | public bool JobRoleMatch(JobModule module) 65 | { 66 | var attr = GetDisplayAttr(); 67 | return (attr.Role != None && attr.Role.HasFlag(module.Role)) || attr.Job == module.Job || (attr.Job == module.Class && module.Class != Job.None); 68 | } 69 | 70 | public bool JobMatch(JobModule module) 71 | { 72 | var attr = GetDisplayAttr(); 73 | return attr.Job == module.Job || (module.Class != Job.None && attr.Job == module.Class); 74 | } 75 | 76 | public TrackerConfig CleanUp() 77 | { 78 | WidgetConfig = WidgetConfig.CleanUp(WidgetType); 79 | return this; 80 | } 81 | 82 | public void DrawTooltip() => GetDisplayAttr().DrawTooltip(TrackerType, ItemId); 83 | 84 | [JsonIgnore] public string WidgetDisplayName => WidgetList.TryGetValue(WidgetType ?? string.Empty, out var result) ? result.DisplayName : ""; 85 | } 86 | -------------------------------------------------------------------------------- /GaugeOMatic/Trackers/Presets/Preset.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using static Dalamud.Utility.Util; 4 | using static GaugeOMatic.Utility.Json; 5 | using static Newtonsoft.Json.JsonConvert; 6 | using static System.Convert; 7 | 8 | namespace GaugeOMatic.Trackers.Presets; 9 | 10 | public struct Preset : IEquatable 11 | { 12 | public string Name; 13 | public TrackerConfig[] Trackers; 14 | 15 | [JsonIgnore] public bool BuiltIn = false; 16 | 17 | public Preset(string? importStr, bool zipped = false) 18 | { 19 | if (zipped) importStr = Unzip(importStr); 20 | if (importStr == null) 21 | { 22 | Name = "New Preset"; 23 | Trackers = []; 24 | } 25 | else 26 | { 27 | try 28 | { 29 | var import = DeserializeObject(importStr, JsonSettings); 30 | Name = import.Name.Length == 0 ? "New Preset" : import.Name; 31 | Trackers = import.Trackers; 32 | 33 | } 34 | catch (Exception ex) 35 | { 36 | Log.Error($"Error deserializing Preset!\n{ex}"); 37 | Name = "New Preset"; 38 | Trackers = []; 39 | } 40 | } 41 | } 42 | 43 | public Preset(TrackerConfig[] trackers, string name = "New Preset") 44 | { 45 | Name = name; 46 | Trackers = trackers; 47 | } 48 | 49 | public Preset(string name, bool builtIn, TrackerConfig[] trackers) 50 | { 51 | Name = name; 52 | BuiltIn = builtIn; 53 | Trackers = trackers; 54 | } 55 | 56 | public readonly Preset Clone() => new(ExportStr(), true); 57 | 58 | public readonly string ExportStr() => Zip(SerializeObject(CleanUp(), JsonSettings)); 59 | 60 | private readonly Preset CleanUp() 61 | { 62 | for (var i = 0; i < Trackers.Length; i++) Trackers[i] = Trackers[i].CleanUp(); 63 | return this; 64 | } 65 | 66 | public static implicit operator string(Preset p) => p.ExportStr(); 67 | 68 | public static implicit operator Preset(string s) => new(s, true); 69 | public static implicit operator TrackerConfig[](Preset p) => p.Trackers; 70 | 71 | public static string Zip(string s) => ToBase64String(CompressString(s)); 72 | public static string? Unzip(string? s) 73 | { 74 | try 75 | { 76 | return s == null ? null : DecompressString(FromBase64String(s)); 77 | } 78 | catch (Exception ex) 79 | { 80 | Log.Error($"Error decompressing preset string:\n{s}\n{ex.StackTrace}"); 81 | return null; 82 | } 83 | } 84 | 85 | public readonly Preset Disable() 86 | { 87 | foreach (var t in Trackers) t.Enabled = false; 88 | return this; 89 | } 90 | 91 | public readonly bool Equals(Preset other) => ExportStr() == other.ExportStr(); 92 | 93 | public override readonly bool Equals(object? obj) => obj is Preset other && Equals(other); 94 | 95 | public override readonly int GetHashCode() => 0; 96 | 97 | public static bool operator ==(Preset left, Preset right) => left.Equals(right); 98 | 99 | public static bool operator !=(Preset left, Preset right) => !left.Equals(right); 100 | } 101 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/Structs/AddonJobHudDRG.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using FFXIVClientStructs.Attributes; 3 | using FFXIVClientStructs.FFXIV.Component.GUI; 4 | using static FFXIVClientStructs.FFXIV.Client.UI.AddonJobHud; 5 | // ReSharper disable All 6 | namespace FFXIVClientStructs.FFXIV.Client.UI; 7 | 8 | /// 9 | /// DRG - Dragon Gauge 10 | /// 11 | [Addon("JobHudDRG0")] 12 | [StructLayout(LayoutKind.Explicit, Size = 0x398)] 13 | public unsafe partial struct AddonJobHudDRG0 { 14 | [FieldOffset(0x000)] public AddonJobHud JobHud; 15 | 16 | [StructLayout(LayoutKind.Explicit, Size = 0x28)] 17 | public partial struct DragonGaugeData { 18 | [FieldOffset(0x00)] public AddonJobHudGaugeData GaugeData; 19 | [FieldOffset(0x08)] public fixed byte Prerequisites[3]; 20 | [FieldOffset(0x0C)] public int LotDStatus; // set to 2 while LotD is active (may get set to 1 at a lower level range?) 21 | [FieldOffset(0x10)] public int LotDTimer; 22 | [FieldOffset(0x14)] public int LotDMax; 23 | [FieldOffset(0x18)] public int EyeCount; 24 | [FieldOffset(0x1C)] public int FirstMindsFocusCount; 25 | } 26 | 27 | [StructLayout(LayoutKind.Explicit, Size = 0x58)] 28 | public partial struct DragonGauge { 29 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 30 | [FieldOffset(0x10)] public AtkResNode* Container; 31 | [FieldOffset(0x18)] public AtkResNode* Container2; 32 | [FieldOffset(0x20)] public int LotDStatus; 33 | [FieldOffset(0x28)] public AtkTextNode* LotDTimerText; 34 | [FieldOffset(0x30)] public AtkComponentGaugeBar* LotDTimerGaugeBar; 35 | [FieldOffset(0x38)] public AtkResNode* EyeNode; 36 | [FieldOffset(0x40)] public AtkResNode* DragonSilhouette; 37 | [FieldOffset(0x48)] public AtkResNode* FirstMindsFocusContainer; 38 | [FieldOffset(0x50)] public AtkResNode* FirstMindsFocusContainer2; 39 | } 40 | 41 | [StructLayout(LayoutKind.Explicit, Size = 0x90)] 42 | public partial struct DragonGaugeSimple { 43 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 44 | [FieldOffset(0x10)] public AtkResNode* Container; 45 | [FieldOffset(0x18)] public AtkComponentTextNineGrid* LotDTimerDisplay; 46 | [FieldOffset(0x20)] public AtkComponentGaugeBar* LotDTimerGaugeBar; 47 | [FieldOffset(0x28)] public AtkResNode* GemContainer; 48 | [FieldOffset(0x30)] public AtkResNode* EyeContainer; 49 | [FieldOffset(0x38)] public bool LotDReady; 50 | [FieldOffset(0x40)] public AtkComponentBase* Eye1; 51 | [FieldOffset(0x48)] public AtkResNode* EyeGlow1; 52 | [FieldOffset(0x50)] public AtkComponentBase* Eye2; 53 | [FieldOffset(0x58)] public AtkResNode* EyeGlow2; 54 | [FieldOffset(0x60)] public AtkResNode* FirstMindsFocusContainer; 55 | [FieldOffset(0x68)] public bool FirstMindsFocusReady; 56 | [FieldOffset(0x70)] public AtkComponentBase* FirstMindsFocus1; 57 | [FieldOffset(0x78)] public AtkResNode* FirstMindsFocusGlow1; 58 | [FieldOffset(0x80)] public AtkComponentBase* FirstMindsFocus2; 59 | [FieldOffset(0x88)] public AtkResNode* FirstMindsFocusGlow2; 60 | } 61 | 62 | [FieldOffset(0x260)] public DragonGaugeData DataPrevious; 63 | [FieldOffset(0x288)] public DragonGaugeData DataCurrent; 64 | [FieldOffset(0x2B0)] public DragonGauge GaugeStandard; 65 | [FieldOffset(0x308)] public DragonGaugeSimple GaugeSimple; 66 | } 67 | -------------------------------------------------------------------------------- /GaugeOMatic/CustomNodes/Animation/Tween.cs: -------------------------------------------------------------------------------- 1 | using CustomNodes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Linq; 6 | using static GaugeOMatic.CustomNodes.Animation.KeyFrame; 7 | using static GaugeOMatic.CustomNodes.Animation.Tween.EaseType; 8 | using static GaugeOMatic.Utility.MiscMath; 9 | using static System.DateTime; 10 | using static System.TimeSpan; 11 | 12 | namespace GaugeOMatic.CustomNodes.Animation; 13 | 14 | [SuppressMessage("ReSharper", "UnassignedField.Global")] 15 | public unsafe class Tween 16 | { 17 | public enum EaseType { Linear, SinInOut, Overshoot } 18 | 19 | public static readonly Dictionary> EaseFuncs = new() 20 | { 21 | { Linear, static p => p }, 22 | { SinInOut, static p => (float)((-0.5f * Math.Cos(p * Math.PI)) + 0.5f) }, 23 | { Overshoot, static p => PolyCalc(p, 0, 0.76686507936507d, 2.96130952380954d, -2.72817460317462d) } 24 | }; 25 | 26 | public CustomNode Target; 27 | public DateTime StartTime; 28 | public List KeyFrames { get; set; } 29 | public float Length; 30 | public bool IsStale; 31 | public int Delay; 32 | 33 | public bool Repeat { get; set; } 34 | public string Label { get; set; } = string.Empty; 35 | public EaseType Ease = Linear; 36 | public Action? Complete { get; set; } 37 | public Action? PerCycle { get; set; } 38 | 39 | public Tween(CustomNode target, params KeyFrame[] keyFrames) 40 | { 41 | StartTime = Now; 42 | Target = target; 43 | IsStale = false; 44 | 45 | KeyFrames = [.. keyFrames.OrderBy(static k => k.Time)]; 46 | Length = 0; 47 | foreach (var k in KeyFrames) Length = Math.Max(Length, k.Time); 48 | } 49 | 50 | public float CalculateProg(double timePassed, out KeyFrame start, out KeyFrame end) 51 | { 52 | for (var i = 0; i < KeyFrames.Count - 1; i++) 53 | { 54 | start = KeyFrames[i]; 55 | end = KeyFrames[i + 1]; 56 | if (timePassed < start.Time || timePassed > end.Time) continue; 57 | 58 | return Math.Abs(end.Time - start.Time) > 0.01f ? (float)Math.Clamp((timePassed - start.Time) / (end.Time - start.Time), 0f, 1f) : 1; 59 | } 60 | 61 | start = KeyFrames[^1]; 62 | end = KeyFrames[^1]; 63 | return 1f; 64 | } 65 | 66 | public Tween Update(float? progOverride = null) 67 | { 68 | if ((progOverride == null && IsStale) || Target.Node == null) 69 | { 70 | IsStale = true; 71 | return this; 72 | } 73 | 74 | var timePassed = progOverride == null ? (Now - (StartTime + FromMilliseconds(Delay))).TotalMilliseconds : progOverride.Value * Length; 75 | 76 | if (timePassed < 0) return this; 77 | 78 | if (timePassed > Length) 79 | { 80 | if (Repeat) 81 | { 82 | PerCycle?.Invoke(); 83 | StartTime = StartTime.AddMilliseconds(Length); 84 | timePassed %= Length; 85 | } 86 | else 87 | { 88 | IsStale = true; 89 | Complete?.Invoke(); 90 | } 91 | } 92 | 93 | 94 | var easeFunc = EaseFuncs.TryGetValue(Ease, out var e) ? e : static p => p; 95 | var subProg = CalculateProg(timePassed, out var start, out var end); 96 | 97 | Interpolate(start, end, easeFunc(subProg)).ApplyToNode(Target); 98 | 99 | return this; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /GaugeOMatic/Trackers/TrackerTypes.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.GameData; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using static GaugeOMatic.GameData.JobData; 6 | using static GaugeOMatic.Trackers.RefType; 7 | 8 | namespace GaugeOMatic.Trackers; 9 | 10 | public abstract partial class Tracker 11 | { 12 | [SuppressMessage("ReSharper", "UnusedMember.Global")] 13 | public virtual string TermCount => "Count"; 14 | public virtual string TermGauge => "Timer"; 15 | public virtual string[] StateNames => ["Inactive", "Active"]; 16 | 17 | public virtual TrackerDisplayAttribute DisplayAttr => (TrackerDisplayAttribute?)GetType().GetCustomAttributes(typeof(TrackerDisplayAttribute), true).FirstOrDefault() ?? new(); 18 | 19 | public abstract RefType RefType { get; } 20 | public abstract TrackerData GetCurrentData(float? preview = null); 21 | } 22 | 23 | public sealed class StatusTracker : Tracker 24 | { 25 | public StatusRef StatusRef => StatusRefCopy ??= ((StatusRef)ItemRef!.ID).Clone(); 26 | public StatusRef? StatusRefCopy { get; private set; } // make a local copy for the tracker so we can modify it 27 | 28 | public override RefType RefType => Status; 29 | public override TrackerDisplayAttribute DisplayAttr => StatusRef; 30 | 31 | public override string TermCount => "Stacks"; 32 | public override string TermGauge => "Timer"; 33 | public override string[] StateNames { get; } = ["Inactive", "Active"]; 34 | 35 | public override TrackerData GetCurrentData(float? preview = null) => StatusRef.GetTrackerData(preview); 36 | } 37 | 38 | public sealed class ActionTracker : Tracker 39 | { 40 | public ActionRef ActionRef => ActionRefCopy ??= ItemRef!.ID; 41 | public ActionRef? ActionRefCopy { get; private set; } 42 | 43 | public override RefType RefType => Action; 44 | public override TrackerDisplayAttribute DisplayAttr => ActionRef; 45 | 46 | public override string TermCount => "Charges"; 47 | public override string TermGauge => "Timer"; 48 | public override string[] StateNames { get; } = ["Unavailable", "Available"]; 49 | 50 | public override TrackerData GetCurrentData(float? preview = null) => ActionRef.GetTrackerData(preview); 51 | } 52 | 53 | public abstract unsafe class JobGaugeTracker : Tracker where T : unmanaged 54 | { 55 | public override RefType RefType => JobGauge; 56 | 57 | public override string TermCount => "Count"; 58 | public override string TermGauge => "Gauge"; 59 | 60 | public override string[] StateNames { get; } = ["Inactive", "Active"]; 61 | 62 | public abstract string GaugeAddonName { get; } 63 | 64 | public AddonJobHud* GaugeAddon => (AddonJobHud*)GameGui.GetAddonByName(GaugeAddonName); 65 | public T* GaugeData => (T*)GaugeAddon->DataCurrentPointer; 66 | } 67 | 68 | public class ParameterTracker : Tracker 69 | { 70 | public override RefType RefType => Parameter; 71 | public override TrackerDisplayAttribute DisplayAttr => ParamRef.Attrs[((ParamRef)ItemRef!).ParamType]; 72 | public override TrackerData GetCurrentData(float? preview = null) => ItemRef!.GetTrackerData(preview); 73 | } 74 | 75 | public class EmptyTracker : Tracker 76 | { 77 | public override RefType RefType => None; 78 | public override TrackerDisplayAttribute DisplayAttr => new("[ Track... ]", Job.None, 60071); 79 | 80 | public override string TermCount => "Count"; 81 | public override string TermGauge => "Timer"; 82 | public override string[] StateNames { get; } = ["Inactive", "Active"]; 83 | 84 | public override TrackerData GetCurrentData(float? preview = null) => new(0, 1, 0, 1, 0, 1, 0); 85 | } 86 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/Structs/AddonJobHudMCH.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using FFXIVClientStructs.Attributes; 3 | using FFXIVClientStructs.FFXIV.Component.GUI; 4 | using static FFXIVClientStructs.FFXIV.Client.UI.AddonJobHud; 5 | // ReSharper disable All 6 | namespace FFXIVClientStructs.FFXIV.Client.UI; 7 | 8 | /// 9 | /// MCH - Heat Gauge 10 | /// 11 | [Addon("JobHudMCH0")] 12 | [StructLayout(LayoutKind.Explicit, Size = 0x3B8)] 13 | public unsafe partial struct AddonJobHudMCH0 { 14 | [FieldOffset(0x000)] public AddonJobHud JobHud; 15 | 16 | [StructLayout(LayoutKind.Explicit, Size = 0x38)] 17 | public partial struct HeatGaugeData { 18 | [FieldOffset(0x00)] public AddonJobHudGaugeData GaugeData; 19 | [FieldOffset(0x08)] public fixed byte Prerequisites[2]; 20 | [FieldOffset(0x0C)] public int HeatValue; 21 | [FieldOffset(0x10)] public int HeatMax; 22 | [FieldOffset(0x14)] public int HeatMid; 23 | [FieldOffset(0x18)] public bool OverheatActive; 24 | [FieldOffset(0x1C)] public int OverheatTimeLeft; 25 | [FieldOffset(0x24)] public int BatteryValue; 26 | [FieldOffset(0x28)] public int BatteryMax; 27 | [FieldOffset(0x2C)] public int BatteryMid; 28 | [FieldOffset(0x30)] public bool SummonActive; 29 | [FieldOffset(0x34)] public int SummonTimeLeft; 30 | } 31 | 32 | [StructLayout(LayoutKind.Explicit, Size = 0x78)] 33 | public partial struct HeatGauge { 34 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 35 | [FieldOffset(0x10)] public AtkResNode* HeatContainer; 36 | [FieldOffset(0x18)] public AtkComponentGaugeBar* HeatGaugeBar; 37 | [FieldOffset(0x20)] public AtkComponentTextNineGrid* HeatValueDisplay; 38 | [FieldOffset(0x28)] public AtkResNode* OverheatTimer; 39 | [FieldOffset(0x30)] public AtkTextNode* OverheatTimerText; 40 | [FieldOffset(0x38)] public int OverheatState; 41 | [FieldOffset(0x40)] public AtkResNode* BatteryContainer; 42 | [FieldOffset(0x48)] public AtkComponentGaugeBar* BatteryGaugeBar; 43 | [FieldOffset(0x50)] public AtkComponentTextNineGrid* BatteryValueDisplay; 44 | [FieldOffset(0x58)] public AtkResNode* BatteryTimer; 45 | [FieldOffset(0x60)] public AtkResNode* BatteryTimerTextContainer; 46 | [FieldOffset(0x68)] public AtkTextNode* BatteryTimerText; 47 | [FieldOffset(0x70)] public int SummonState; 48 | } 49 | 50 | [StructLayout(LayoutKind.Explicit, Size = 0x70)] 51 | public partial struct HeatGaugeSimple { 52 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 53 | [FieldOffset(0x10)] public AtkResNode* HeatContainer; 54 | [FieldOffset(0x18)] public AtkComponentGaugeBar* HeatGaugeBar; 55 | [FieldOffset(0x20)] public AtkComponentTextNineGrid* HeatValueDisplay; 56 | [FieldOffset(0x28)] public AtkComponentTextNineGrid* OverheatTimerDisplay; 57 | [FieldOffset(0x30)] public int OverheatState; 58 | [FieldOffset(0x38)] public AtkResNode* BatteryContainer; 59 | [FieldOffset(0x40)] public AtkComponentGaugeBar* BatteryGaugeBar; 60 | [FieldOffset(0x48)] public AtkComponentTextNineGrid* BatteryValueDisplay; 61 | [FieldOffset(0x50)] public AtkResNode* BarFillContainer; 62 | [FieldOffset(0x58)] public AtkResNode* BatteryContainer2; // duplicate of 0x38 63 | [FieldOffset(0x60)] public AtkComponentTextNineGrid* SummonTimerDisplay; 64 | } 65 | 66 | [FieldOffset(0x260)] public HeatGaugeData DataPrevious; 67 | [FieldOffset(0x298)] public HeatGaugeData DataCurrent; 68 | [FieldOffset(0x2D0)] public HeatGauge GaugeStandard; 69 | [FieldOffset(0x348)] public HeatGaugeSimple GaugeSimple; 70 | } 71 | -------------------------------------------------------------------------------- /GaugeOMatic/Widgets/Widget.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Component.GUI; 2 | using GaugeOMatic.Trackers; 3 | using System; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Linq; 6 | using static CustomNodes.CustomNodeManager; 7 | using static GaugeOMatic.Widgets.WidgetAttribute; 8 | using static GaugeOMatic.Widgets.Common.WidgetUI; 9 | using static GaugeOMatic.Widgets.Common.WidgetUI.WidgetUiTab; 10 | using static System.Activator; 11 | 12 | // ReSharper disable VirtualMemberCallInConstructor 13 | 14 | namespace GaugeOMatic.Widgets; 15 | 16 | public abstract unsafe partial class Widget : IDisposable 17 | { 18 | [SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")] 19 | protected Widget(Tracker tracker) 20 | { 21 | Tracker = tracker; 22 | InitConfigs(); 23 | 24 | if (!GetAttributes.AddonPermitted(Tracker.AddonName)) Tracker.AddonName = Tracker.JobModule.AddonOptions.First(a => GetAttributes.AddonPermitted(a.Name)).Name; 25 | 26 | Addon = (AtkUnitBase*)GameGui.GetAddonByName(Tracker.AddonName); 27 | 28 | WidgetContainer = BuildContainer(); 29 | WidgetIconContainer = BuildWidgetIcon(tracker); 30 | WidgetContainer.Append(WidgetIconContainer); 31 | WidgetRoot = new(CreateResNode(), WidgetContainer); 32 | 33 | WidgetIconContainer.AttachTo(WidgetContainer); 34 | WidgetRoot.AssembleNodeTree(); 35 | Attach(); 36 | 37 | ApplyConfigs(); 38 | } 39 | 40 | public static Widget? Create(Tracker tracker) 41 | { 42 | if (string.IsNullOrEmpty(tracker.WidgetType)) return null; 43 | 44 | var type = Type.GetType($"{typeof(Widget).Namespace}.{tracker.WidgetType}"); 45 | 46 | return type == null ? null : 47 | string.IsNullOrEmpty(tracker.AddonName) ? null : 48 | (AtkUnitBase*)GameGui.GetAddonByName(tracker.AddonName) == null ? null : 49 | (Widget?)CreateInstance(type, tracker); 50 | } 51 | 52 | public WidgetAttribute GetAttributes => WidgetList.TryGetValue(GetType().Name, out var widgetAttr) ? widgetAttr : new(); 53 | public Tracker Tracker { get; set; } 54 | public TrackerConfig TrackerConfig => Tracker.TrackerConfig; 55 | 56 | public void Dispose() 57 | { 58 | Animator.Dispose(); 59 | Detach(); 60 | if (WidgetRoot.Node != null) WidgetRoot.Dispose(); 61 | foreach (var partsList in PartsLists) partsList.Dispose(); 62 | } 63 | 64 | public WidgetUiTab UiTab { get; set; } = Layout; 65 | 66 | public abstract WidgetTypeConfig Config { get; } 67 | 68 | public abstract void Update(); 69 | public abstract void InitConfigs(); 70 | public abstract void ResetConfigs(); 71 | 72 | public virtual void ApplyConfigs() 73 | { 74 | WidgetContainer.SetPos(Config.Position) 75 | .SetScale(Config.Scale); 76 | 77 | WidgetIconContainer.SetVis(Config.ShowIcon) 78 | .SetPos(Config.IconPosition) 79 | .SetScale(Config.IconScale); 80 | } 81 | 82 | public virtual void DrawUI() 83 | { 84 | switch (UiTab) 85 | { 86 | case Layout: 87 | PositionControls("Position", ref Config.Position); 88 | ScaleControls("Scale", ref Config.Scale); 89 | break; 90 | case Icon: 91 | ToggleControls("Show Icon", ref Config.ShowIcon); 92 | if (Config.ShowIcon) 93 | { 94 | PositionControls("Position##iconPos", ref Config.IconPosition); 95 | ScaleControls("Scale##iconScale", ref Config.IconScale); 96 | } 97 | break; 98 | default: 99 | break; 100 | } 101 | } 102 | } 103 | 104 | -------------------------------------------------------------------------------- /GaugeOMatic/Windows/Dropdowns/WidgetDropdown.cs: -------------------------------------------------------------------------------- 1 | using GaugeOMatic.Trackers; 2 | using GaugeOMatic.Widgets; 3 | using ImGuiNET; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Numerics; 7 | using Dalamud.Interface.Utility.Raii; 8 | using static GaugeOMatic.Widgets.WidgetAttribute; 9 | using static GaugeOMatic.Widgets.WidgetTags; 10 | using static GaugeOMatic.Widgets.Common.WidgetUI; 11 | using static GaugeOMatic.Widgets.Common.WidgetUI.UpdateFlags; 12 | 13 | namespace GaugeOMatic.Windows.Dropdowns; 14 | 15 | public class WidgetDropdown : BranchingDropdown 16 | { 17 | public Tracker Tracker; 18 | public int Hash => Tracker.GetHashCode(); 19 | public Dictionary AvailableWidgets = new(); 20 | 21 | public override List<(string label, WidgetTags tag)> SubMenus { get; } 22 | 23 | public WidgetDropdown(Tracker tracker) 24 | { 25 | Tracker = tracker; 26 | AvailableWidgets.Clear(); 27 | 28 | foreach (var (widgetType, widgetInfo) in WidgetList) 29 | { 30 | var tags = widgetInfo.WidgetTags; 31 | 32 | if (tags.HasFlag(Exclude)) continue; 33 | if (tags.HasFlag(HasAddonRestrictions) && !Tracker.JobModule.AddonOptions.Any(a => widgetInfo.WhiteList.Contains(a.Name) || widgetInfo.BlackList.Contains(a.Name) == false)) continue; 34 | 35 | AvailableWidgets.Add(widgetType, widgetInfo); 36 | } 37 | 38 | SubMenus = 39 | [ 40 | ("Counters", Counter), 41 | ("Bars & Timers", GaugeBar), 42 | ("State Indicators", State), 43 | ("Multi-Component", MultiComponent) 44 | ]; 45 | } 46 | 47 | public override void DrawSubMenu(int i) 48 | { 49 | var (label, tag) = SubMenus[i]; 50 | 51 | if (tag == MultiComponent) MultiCompSubMenu(label); 52 | else 53 | { 54 | if (ImGui.BeginMenu($"{label}##{Hash}{label}Menu")) 55 | { 56 | var widgets = AvailableWidgets.Where(w => w.Value.WidgetTags.HasFlag(tag)) 57 | .OrderBy(static w => w.Value.DisplayName); 58 | 59 | foreach (var w in widgets.Where(static w => ImGui.MenuItem(w.Value.DisplayName))) 60 | { 61 | Tracker.WidgetType = w.Key; 62 | UpdateFlag |= Reset | Save; 63 | } 64 | 65 | ImGui.EndMenu(); 66 | } 67 | } 68 | } 69 | 70 | public void MultiCompSubMenu(string label) 71 | { 72 | if (ImGui.BeginMenu($"{label}##{Hash}{label}Menu")) 73 | { 74 | foreach (var (key, name) in MultiCompDict) 75 | { 76 | using (ImRaii.PushColor(ImGuiCol.Border, new Vector4(0))) 77 | { 78 | if (ImGui.BeginMenu($"{name}##{Hash}{label}{key}Menu")) 79 | { 80 | foreach (var w in AvailableWidgets 81 | .Where(w => w.Value.WidgetTags.HasFlag(MultiComponent) && 82 | w.Value.MultiCompData?.Key == key) 83 | .OrderBy(static w => w.Value.MultiCompData?.Index) 84 | .Where(static w => ImGui.MenuItem(w.Value.DisplayName))) 85 | { 86 | Tracker.WidgetType = w.Key; 87 | UpdateFlag |= Reset | Save; 88 | } 89 | 90 | ImGui.EndMenu(); 91 | } 92 | } 93 | } 94 | 95 | ImGui.EndMenu(); 96 | } 97 | } 98 | 99 | public override string DropdownText(string fallback) => WidgetList.TryGetValue(Tracker.WidgetType ?? "", out var attr) ? attr.DisplayName : fallback; 100 | } 101 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/Structs/AddonJobHudAST.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using FFXIVClientStructs.Attributes; 3 | using FFXIVClientStructs.FFXIV.Component.GUI; 4 | using FFXIVClientStructs.Interop.Attributes; 5 | using static FFXIVClientStructs.FFXIV.Client.UI.AddonJobHud; 6 | // ReSharper disable All 7 | namespace FFXIVClientStructs.FFXIV.Client.UI; 8 | 9 | /// 10 | /// AST - Arcana Gauge 11 | /// 12 | [Addon("JobHudAST0")] 13 | [StructLayout(LayoutKind.Explicit, Size = 0x468)] 14 | public unsafe partial struct AddonJobHudAST0 { 15 | [FieldOffset(0x000)] public AddonJobHud JobHud; 16 | 17 | [StructLayout(LayoutKind.Explicit, Size = 0x40)] 18 | public partial struct ArcanaGaugeData { 19 | [FieldOffset(0x00)] public AddonJobHudGaugeData GaugeData; 20 | [FieldOffset(0x08)] public fixed byte Prerequisites[3]; 21 | [FieldOffset(0x0C)] public int DrawnCard; 22 | [FieldOffset(0x10)] public int DrawnMinorArcanum; 23 | [FieldOffset(0x14)] public int DrawnRoleBuff; 24 | [FieldOffset(0x18)] public int DrawnAstrosign; 25 | [FieldOffset(0x1C)] public int Astrosign1; 26 | [FieldOffset(0x20)] public int Astrosign2; 27 | [FieldOffset(0x24)] public int Astrosign3; 28 | [FieldOffset(0x30)] public byte* CardName; 29 | [FieldOffset(0x38)] public byte* MinorArcanaName; 30 | } 31 | 32 | [StructLayout(LayoutKind.Explicit, Size = 0xC8)] 33 | public partial struct ArcanaGauge { 34 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 35 | [FieldOffset(0x10)] public AtkResNode* Container; 36 | [FieldOffset(0x18)] public AtkComponentBase* CardContainer; 37 | [FieldOffset(0x20)] public AtkTextNode* CardName; 38 | [FieldOffset(0x28)] public AtkResNode* CardAstrosign; 39 | [FieldOffset(0x30)] public AtkResNode* CardSymbol; 40 | [FieldOffset(0x38)] public AtkResNode* CardBorder; 41 | [FieldOffset(0x40)] public AtkResNode* GaugePlate; 42 | [FieldOffset(0x48)] public AtkResNode* MinorArcanaContainer; 43 | [FieldOffset(0x50)] public AtkTextNode* MinorArcanaName; 44 | [FieldOffset(0x58)] public AtkResNode* MinorArcanaSymbol; 45 | [FieldOffset(0x60)] public AtkResNode* AstrosignContainer; 46 | 47 | [FixedSizeArray(3)] 48 | [FieldOffset(0x78)] public fixed byte Astrosigns[3 * Astrosign.Size]; 49 | } 50 | 51 | [StructLayout(LayoutKind.Explicit, Size = 0xC0)] 52 | public partial struct ArcanaGaugeSimple { 53 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 54 | [FieldOffset(0x10)] public AtkComponentBase* CardContainer; 55 | [FieldOffset(0x18)] public AtkComponentTextNineGrid* CardName; 56 | [FieldOffset(0x20)] public AtkResNode* CardAstrosign; 57 | [FieldOffset(0x28)] public AtkResNode* CardSymbol; 58 | [FieldOffset(0x30)] public AtkResNode* CardBorder; 59 | [FieldOffset(0x38)] public AtkResNode* Container; 60 | [FieldOffset(0x40)] public AtkComponentBase* MinorArcanaContainer; 61 | [FieldOffset(0x48)] public AtkComponentTextNineGrid* MinorArcanaName; // seemingly unused 62 | [FieldOffset(0x50)] public AtkResNode* MinorArcanaSymbol; 63 | [FieldOffset(0x58)] public AtkResNode* AstrosignContainer; 64 | 65 | [FixedSizeArray(3)] 66 | [FieldOffset(0x70)] public fixed byte Astrosigns[3 * Astrosign.Size]; 67 | } 68 | 69 | [StructLayout(LayoutKind.Explicit, Size = Size)] 70 | public partial struct Astrosign { 71 | public const int Size = 0x18; 72 | [FieldOffset(0x00)] public AtkComponentBase* Container; 73 | [FieldOffset(0x08)] public AtkResNode* Symbol; 74 | [FieldOffset(0x10)] public int Type; 75 | } 76 | 77 | [FieldOffset(0x260)] public ArcanaGaugeData DataPrevious; 78 | [FieldOffset(0x2A0)] public ArcanaGaugeData DataCurrent; 79 | [FieldOffset(0x2E0)] public ArcanaGauge GaugeStandard; 80 | [FieldOffset(0x3A8)] public ArcanaGaugeSimple GaugeSimple; 81 | } 82 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Melee/MNK.cs: -------------------------------------------------------------------------------- 1 | using CustomNodes; 2 | using FFXIVClientStructs.FFXIV.Client.UI; 3 | using GaugeOMatic.Trackers; 4 | using GaugeOMatic.Windows.Dropdowns; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Numerics; 8 | using Dalamud.Interface; 9 | using Dalamud.Interface.Components; 10 | using static GaugeOMatic.GameData.JobData; 11 | using static GaugeOMatic.GameData.JobData.Job; 12 | using static GaugeOMatic.GameData.JobData.Role; 13 | using static GaugeOMatic.JobModules.Tweaks; 14 | using static GaugeOMatic.Widgets.Common.WidgetUI; 15 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 16 | 17 | namespace GaugeOMatic.JobModules; 18 | 19 | public class MNKModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 20 | : JobModule(trackerManager, trackerConfigList, "JobHudMNK0", "JobHudMNK1") 21 | { 22 | public override Job Job => MNK; 23 | public override Job Class => PGL; 24 | public override Role Role => Melee; 25 | public override List AddonOptions => 26 | [ 27 | new("JobHudMNK0", "Beast Chakra Gauge"), 28 | new("JobHudMNK1", "Chakra Gauge"), 29 | new("_ParameterWidget", "Parameter Bar") 30 | ]; 31 | 32 | public override List JobGaugeMenu { get; } = [new("Chakra Gauge", nameof(ChakraGaugeTracker))]; 33 | 34 | public override void Save() 35 | { 36 | Configuration.TrackerConfigs.MNK = SaveOrder; 37 | Configuration.Save(); 38 | } 39 | 40 | public override void TweakUI() 41 | { 42 | Heading("Beast Chakra Gauge"); 43 | ToggleControls("Hide Beast Chakra Gauge", ref TweakConfigs.MNKHide0); 44 | if (!TweakConfigs.MNKHide0) 45 | { 46 | ToggleControls("Reverse Fury Stack Order", ref TweakConfigs.MNK0Reverse); 47 | ImGuiComponents.HelpMarker("Reverses the order of the Beast Chakras. Useful if you\nprefer to arrange your combo buttons from right to left.",FontAwesomeIcon.QuestionCircle); 48 | 49 | PositionControls("Reposition Fury Stacks", ref TweakConfigs.MNK0BeastPos); 50 | } 51 | 52 | Heading("Chakra Gauge"); 53 | ToggleControls("Hide Chakra Gauge", ref TweakConfigs.MNKHide1); 54 | } 55 | 56 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 57 | { 58 | var gauge = (AddonJobHudMNK0*)gaugeAddon; 59 | VisibilityTweak(TweakConfigs.MNKHide0, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 60 | 61 | ReorderTweak(gaugeAddon); 62 | BeastBarPosTweak(gaugeAddon); 63 | } 64 | 65 | private void BeastBarPosTweak(AddonIndex gaugeIndex) 66 | { 67 | gaugeIndex[3u].SetPos(TweakConfigs.MNK0BeastPos); 68 | } 69 | 70 | private void ReorderTweak(AddonIndex gaugeIndex) 71 | { 72 | if (TweakConfigs.MNK0Reverse) 73 | { 74 | gaugeIndex[4u].SetX(106); 75 | gaugeIndex[7u].SetX(66); 76 | gaugeIndex[11u].SetX(10); 77 | 78 | gaugeIndex[40u].SetX(139); 79 | gaugeIndex[43u].SetX(90); 80 | gaugeIndex[47u].SetX(20); 81 | } 82 | else 83 | { 84 | gaugeIndex[4u].SetX(10); 85 | gaugeIndex[7u].SetX(58); 86 | gaugeIndex[11u].SetX(106); 87 | 88 | gaugeIndex[40u].SetX(20); 89 | gaugeIndex[43u].SetX(70); 90 | gaugeIndex[47u].SetX(120); 91 | } 92 | } 93 | 94 | public override unsafe void ApplyTweaks1(IntPtr gaugeAddon) 95 | { 96 | var gauge = (AddonJobHudMNK1*)gaugeAddon; 97 | VisibilityTweak(TweakConfigs.MNKHide1, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 98 | } 99 | } 100 | 101 | public partial class TweakConfigs 102 | { 103 | public bool MNKHide0; 104 | public bool MNK0Reverse; 105 | public Vector2 MNK0BeastPos; 106 | 107 | public bool MNKHide1; 108 | } 109 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/Structs/AddonJobHudNIN.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using FFXIVClientStructs.Attributes; 3 | using FFXIVClientStructs.FFXIV.Component.GUI; 4 | using FFXIVClientStructs.Interop; 5 | using FFXIVClientStructs.Interop.Attributes; 6 | using static FFXIVClientStructs.FFXIV.Client.UI.AddonJobHud; 7 | // ReSharper disable All 8 | namespace FFXIVClientStructs.FFXIV.Client.UI; 9 | 10 | /// 11 | /// NIN - Ninki Gauge 12 | /// 13 | [Addon("JobHudNIN0")] 14 | [StructLayout(LayoutKind.Explicit, Size = 0x308)] 15 | public unsafe partial struct AddonJobHudNIN0 { 16 | [FieldOffset(0x000)] public AddonJobHud JobHud; 17 | 18 | [StructLayout(LayoutKind.Explicit, Size = 0x18)] 19 | public partial struct NinkiGaugeData { 20 | [FieldOffset(0x00)] public AddonJobHudGaugeData GaugeData; 21 | [FieldOffset(0x08)] public fixed byte Prerequisites[1]; 22 | [FieldOffset(0x0C)] public int NinkiValue; 23 | [FieldOffset(0x10)] public int Max; 24 | [FieldOffset(0x14)] public int Mid; 25 | } 26 | 27 | [StructLayout(LayoutKind.Explicit, Size = 0x40)] 28 | public partial struct NinkiGauge { 29 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 30 | [FieldOffset(0x10)] public AtkResNode* Container; 31 | [FieldOffset(0x18)] public AtkResNode* Scroll; 32 | [FieldOffset(0x20)] public AtkTextNode* ValueText; 33 | [FieldOffset(0x28)] public AtkComponentGaugeBar* GaugeBar0; // bar for 0-50 34 | [FieldOffset(0x30)] public AtkComponentGaugeBar* GaugeBar1; // bar for 50-100 35 | [FieldOffset(0x38)] public bool CanSpend; 36 | } 37 | 38 | [StructLayout(LayoutKind.Explicit, Size = 0x38)] 39 | public partial struct NinkiGaugeSimple { 40 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 41 | [FieldOffset(0x10)] public AtkResNode* Container; 42 | [FieldOffset(0x18)] public AtkResNode* BarTextNode; 43 | [FieldOffset(0x20)] public AtkComponentTextNineGrid* ValueDisplay; 44 | [FieldOffset(0x28)] public AtkComponentGaugeBar* GaugeBar; 45 | } 46 | 47 | [FieldOffset(0x260)] public NinkiGaugeData DataPrevious; 48 | [FieldOffset(0x278)] public NinkiGaugeData DataCurrent; 49 | [FieldOffset(0x290)] public NinkiGauge GaugeStandard; 50 | [FieldOffset(0x2D0)] public NinkiGaugeSimple GaugeSimple; 51 | } 52 | 53 | /// 54 | /// NIN - Huton Gauge 55 | /// 56 | [Addon("JobHudNIN1")] 57 | [StructLayout(LayoutKind.Explicit, Size = 0x320)] 58 | public unsafe partial struct AddonJobHudNIN1 { 59 | [FieldOffset(0x000)] public AddonJobHud JobHud; 60 | 61 | [StructLayout(LayoutKind.Explicit, Size = 0x18)] 62 | public partial struct HutonGaugeData { 63 | [FieldOffset(0x00)] public AddonJobHudGaugeData GaugeData; 64 | [FieldOffset(0x08)] public fixed byte Prerequisites[2]; 65 | [FieldOffset(0x0C)] public int TimeLeft; // in ms 66 | [FieldOffset(0x10)] public int MaxTime; // in ms 67 | [FieldOffset(0x14)] public int ManualCasts; 68 | } 69 | 70 | [StructLayout(LayoutKind.Explicit, Size = 0x68)] 71 | public partial struct HutonGauge { 72 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 73 | [FieldOffset(0x10)] public AtkResNode* Container; 74 | [FieldOffset(0x18)] public AtkResNode* Pinwheel; 75 | [FieldOffset(0x20)] public AtkTextNode* ValueText; 76 | 77 | [FixedSizeArray>(6)] 78 | [FieldOffset(0x28)] public fixed byte Blade[6 * 0x8]; 79 | 80 | [FieldOffset(0x5C)] public int BladesFallen; 81 | [FieldOffset(0x58)] public int TimePerBlade; // in seconds 82 | [FieldOffset(0x60)] public bool IsActive; 83 | } 84 | 85 | [StructLayout(LayoutKind.Explicit, Size = 0x28)] 86 | public partial struct HutonGaugeSimple { 87 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 88 | [FieldOffset(0x10)] public AtkResNode* Container; 89 | [FieldOffset(0x18)] public AtkComponentTextNineGrid* ValueDisplay; 90 | [FieldOffset(0x20)] public AtkComponentGaugeBar* GaugeBar; 91 | } 92 | 93 | [FieldOffset(0x260)] public HutonGaugeData DataPrevious; 94 | [FieldOffset(0x278)] public HutonGaugeData DataCurrent; 95 | [FieldOffset(0x290)] public HutonGauge GaugeStandard; 96 | [FieldOffset(0x2F8)] public HutonGaugeSimple GaugeSimple; 97 | } 98 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/Job.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static GaugeOMatic.GameData.JobData.Job; 3 | using static GaugeOMatic.GameData.JobData.Role; 4 | 5 | // ReSharper disable UnusedMember.Global 6 | 7 | namespace GaugeOMatic.GameData; 8 | 9 | public class JobData 10 | { 11 | public enum Job 12 | { 13 | None = 0, 14 | MNK = 20, DRG = 22, NIN = 30, SAM = 34, RPR = 39, VPR = 41, 15 | BRD = 23, MCH = 31, DNC = 38, 16 | BLM = 25, SMN = 27, RDM = 35, PCT = 42, 17 | PLD = 19, WAR = 21, DRK = 32, GNB = 37, 18 | WHM = 24, SCH = 28, AST = 33, SGE = 40, 19 | 20 | PGL = 2, LNC = 4, ROG = 29, ARC = 5, THM = 7, ACN = 26, GLA = 1, MRD = 3, CNJ = 6, 21 | 22 | BLU = 36, BST = 43, 23 | 24 | CRP = 8, BSM = 9, ARM = 10, GSM = 11, LTW = 12, WVR = 13, ALC = 14, CUL = 15, 25 | MIN = 16, BTN = 17, FSH = 18 26 | } 27 | 28 | [Flags] 29 | public enum Role 30 | { 31 | None = 0x00, 32 | Tank = 0x01, 33 | Healer = 0x02, 34 | Melee = 0x04, 35 | Ranged = 0x08, 36 | Caster = 0x10, 37 | Limited = 0x20, 38 | Crafter = 0x40, 39 | Gatherer = 0x80, 40 | Combat = 0x3F, 41 | All = 0xFF 42 | } 43 | 44 | public static Job GetJobByCategory(uint cat) => 45 | cat switch 46 | { 47 | 2 => GLA, 48 | 3 => PGL, 49 | 4 => MRD, 50 | 5 => LNC, 51 | 6 => ARC, 52 | 7 => CNJ, 53 | 8 => THM, 54 | 20 or 38 => PLD, 55 | 21 or 41 => MNK, 56 | 22 or 44 => WAR, 57 | 23 or 47 => DRG, 58 | 24 or 50 => BRD, 59 | 25 or 53 => WHM, 60 | 26 or 55 => BLM, 61 | 27 => ACN, 62 | 28 or 69 => SMN, 63 | 29 => SCH, 64 | 91 => ROG, 65 | 92 or 93 => NIN, 66 | 96 => MCH, 67 | 98 => DRK, 68 | 99 => AST, 69 | 111 => SAM, 70 | 112 => RDM, 71 | 129 => BLU, 72 | 149 => GNB, 73 | 150 => DNC, 74 | 180 => RPR, 75 | 181 => SGE, 76 | 196 => VPR, 77 | 197 => PCT, 78 | _ => Job.None 79 | }; 80 | 81 | public static Role GetRoleByCategory(uint cat) => 82 | cat switch 83 | { 84 | 113 => Tank, 85 | 114 => Melee, 86 | 115 => Ranged, 87 | 116 => Caster, 88 | 117 => Healer, 89 | 118 => Melee | Ranged, 90 | 120 => Healer | Caster, 91 | 161 => Melee | Tank | Ranged, 92 | _ => Role.None 93 | }; 94 | 95 | public const byte LevelCap = 100; 96 | 97 | internal static Job Current => LastKnown = FrameworkData.LocalPlayer.Job; 98 | internal static Job LastKnown; 99 | internal static bool JobChanged => LastKnown != Current; 100 | 101 | public static uint GetJobIcon(Job job) => 102 | job switch 103 | { 104 | MNK => 62402, 105 | DRG => 62404, 106 | SAM => 62414, 107 | RPR => 62419, 108 | VPR => 62421, 109 | BRD => 62405, 110 | MCH => 62411, 111 | DNC => 62418, 112 | BLM => 62407, 113 | SMN => 62408, 114 | RDM => 62415, 115 | PCT => 62422, 116 | PLD => 62401, 117 | WAR => 62403, 118 | DRK => 62412, 119 | GNB => 62417, 120 | WHM => 62406, 121 | SCH => 62409, 122 | AST => 62413, 123 | SGE => 62420, 124 | PGL => 62302, 125 | LNC => 62304, 126 | ROG => 62309, 127 | NIN => 62410, 128 | ARC => 62305, 129 | THM => 62307, 130 | ACN => 62308, 131 | GLA => 62301, 132 | MRD => 62303, 133 | CNJ => 62306, 134 | BLU => 62416, 135 | CRP => 62310, 136 | BSM => 62311, 137 | ARM => 62312, 138 | GSM => 62313, 139 | LTW => 62314, 140 | WVR => 62315, 141 | ALC => 62316, 142 | CUL => 62317, 143 | MIN => 62318, 144 | BTN => 62319, 145 | FSH => 62320, 146 | _ => 60071 147 | }; 148 | } 149 | -------------------------------------------------------------------------------- /GaugeOMatic/Trackers/TrackerManager.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Game.ClientState.Conditions; 2 | using GaugeOMatic.Config; 3 | using GaugeOMatic.JobModules; 4 | using System; 5 | using System.Collections.Generic; 6 | using GaugeOMatic.GameData; 7 | using static GaugeOMatic.Trackers.Presets.PluginPresets; 8 | 9 | namespace GaugeOMatic.Trackers; 10 | 11 | public class TrackerManager : IDisposable 12 | { 13 | public Configuration Configuration; 14 | public TrackerConfigs TrackerConfigs => Configuration.TrackerConfigs; 15 | 16 | public List JobModules; 17 | 18 | public TrackerManager(Configuration configuration) 19 | { 20 | Configuration = configuration; 21 | 22 | JobModules = 23 | [ 24 | new PLDModule(this, TrackerConfigs.PLD), 25 | new WARModule(this, TrackerConfigs.WAR), 26 | new DRKModule(this, TrackerConfigs.DRK), 27 | new GNBModule(this, TrackerConfigs.GNB), 28 | 29 | new WHMModule(this, TrackerConfigs.WHM), 30 | new SCHModule(this, TrackerConfigs.SCH), 31 | new ASTModule(this, TrackerConfigs.AST), 32 | new SGEModule(this, TrackerConfigs.SGE), 33 | 34 | new MNKModule(this, TrackerConfigs.MNK), 35 | new DRGModule(this, TrackerConfigs.DRG), 36 | new NINModule(this, TrackerConfigs.NIN), 37 | new SAMModule(this, TrackerConfigs.SAM), 38 | new RPRModule(this, TrackerConfigs.RPR), 39 | new VPRModule(this, TrackerConfigs.VPR), 40 | 41 | new BRDModule(this, TrackerConfigs.BRD), 42 | new MCHModule(this, TrackerConfigs.MCH), 43 | new DNCModule(this, TrackerConfigs.DNC), 44 | 45 | new BLMModule(this, TrackerConfigs.BLM), 46 | new SMNModule(this, TrackerConfigs.SMN), 47 | new RDMModule(this, TrackerConfigs.RDM), 48 | new PCTModule(this, TrackerConfigs.PCT), 49 | 50 | new BLUModule(this, TrackerConfigs.BLU) 51 | ]; 52 | 53 | Condition.ConditionChange += ApplyDisplayRules; 54 | } 55 | 56 | private void ApplyDisplayRules(ConditionFlag flag, bool value) 57 | { 58 | if (ClientState.LocalPlayer == null) return; 59 | if (JobData.Current == 0) return; 60 | 61 | JobModules.Find(static jm => jm.Job == JobData.LastKnown || jm.Class == JobData.LastKnown)?.ApplyDisplayRules(); 62 | } 63 | 64 | public void Dispose() 65 | { 66 | Condition.ConditionChange -= ApplyDisplayRules; 67 | foreach (var module in JobModules) module.Dispose(); 68 | } 69 | } 70 | 71 | public class TrackerConfigs 72 | { 73 | public TrackerConfig[] PLD { get; set; } = PLDDefault.Clone().Disable(); 74 | public TrackerConfig[] WAR { get; set; } = WARDefault.Clone().Disable(); 75 | public TrackerConfig[] DRK { get; set; } = DRKDefault.Clone().Disable(); 76 | public TrackerConfig[] GNB { get; set; } = GNBDefault.Clone().Disable(); 77 | 78 | public TrackerConfig[] WHM { get; set; } = WHMDefault.Clone().Disable(); 79 | public TrackerConfig[] SCH { get; set; } = SCHDefault.Clone().Disable(); 80 | public TrackerConfig[] AST { get; set; } = ASTDefault.Clone().Disable(); 81 | public TrackerConfig[] SGE { get; set; } = SGEDefault.Clone().Disable(); 82 | 83 | public TrackerConfig[] MNK { get; set; } = MNKDefault.Clone().Disable(); 84 | public TrackerConfig[] DRG { get; set; } = DRGDefault.Clone().Disable(); 85 | public TrackerConfig[] NIN { get; set; } = NINDefault.Clone().Disable(); 86 | public TrackerConfig[] SAM { get; set; } = SAMDefault.Clone().Disable(); 87 | public TrackerConfig[] RPR { get; set; } = RPRDefault.Clone().Disable(); 88 | public TrackerConfig[] VPR { get; set; } = VPRDefault.Clone().Disable(); 89 | 90 | public TrackerConfig[] BRD { get; set; } = BRDDefault.Clone().Disable(); 91 | public TrackerConfig[] MCH { get; set; } = MCHDefault.Clone().Disable(); 92 | public TrackerConfig[] DNC { get; set; } = DNCDefault.Clone().Disable(); 93 | 94 | public TrackerConfig[] BLM { get; set; } = BLMDefault.Clone().Disable(); 95 | public TrackerConfig[] SMN { get; set; } = SMNDefault.Clone().Disable(); 96 | public TrackerConfig[] RDM { get; set; } = RDMDefault.Clone().Disable(); 97 | public TrackerConfig[] PCT { get; set; } = PCTDefault.Clone().Disable(); 98 | 99 | public TrackerConfig[] BLU { get; set; } = []; 100 | } 101 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Melee/SAM.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Numerics; 7 | using static GaugeOMatic.GameData.JobData; 8 | using static GaugeOMatic.GameData.JobData.Job; 9 | using static GaugeOMatic.GameData.JobData.Role; 10 | using static GaugeOMatic.JobModules.Tweaks; 11 | using static GaugeOMatic.Widgets.Common.WidgetUI; 12 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 13 | 14 | namespace GaugeOMatic.JobModules; 15 | 16 | public class SAMModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 17 | : JobModule(trackerManager, trackerConfigList, "JobHudSAM0", "JobHudSAM1") 18 | { 19 | public override Job Job => SAM; 20 | public override Job Class => Job.None; 21 | public override Role Role => Melee; 22 | public override List AddonOptions => 23 | [ 24 | new("JobHudSAM0", "Kenki Gauge"), 25 | new("JobHudSAM1", "Sen Gauge"), 26 | new("_ParameterWidget", "Parameter Bar") 27 | ]; 28 | 29 | public override List JobGaugeMenu { get; } = 30 | [ 31 | new("Kenki Gauge", nameof(KenkiGaugeTracker)), 32 | new("Meditation Gauge", nameof(MeditationGaugeTracker)), 33 | new("Sen Gauge - Seal Count", nameof(SenSealTracker)), 34 | new("Sen Gauge - Setsu Seal", nameof(SenGaugeSetsuTracker)), 35 | new("Sen Gauge - Getsu Seal", nameof(SenGaugeGetsuTracker)), 36 | new("Sen Gauge - Ka Seal", nameof(SenGaugeKaTracker)) 37 | ]; 38 | 39 | public override void Save() 40 | { 41 | Configuration.TrackerConfigs.SAM = SaveOrder; 42 | Configuration.Save(); 43 | } 44 | 45 | public override void TweakUI() 46 | { 47 | Heading("Kenki Gauge"); 48 | ToggleControls("Hide Kenki Bar", ref TweakConfigs.SAMHide0Kenki); 49 | 50 | ToggleControls("Hide Meditation Stacks", ref TweakConfigs.SAMHide0Meditation); 51 | 52 | Heading("Sen Gauge"); 53 | ToggleControls("Hide Sen Gauge", ref TweakConfigs.SAMHide1); 54 | 55 | Heading("Reposition Seals"); 56 | PositionControls("Setsu", ref TweakConfigs.SAMSealPosSetsu); 57 | PositionControls("Getsu", ref TweakConfigs.SAMSealPosGetsu); 58 | PositionControls("Ka", ref TweakConfigs.SAMSealPosKa); 59 | } 60 | 61 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 62 | { 63 | var gauge = (AddonJobHudSAM0*)gaugeAddon; 64 | 65 | VisibilityTweak(TweakConfigs.SAMHide0Kenki, gauge->UseSimpleGauge, gauge->GaugeStandard.KenkiContainer, gauge->GaugeSimple.KenkiContainer); 66 | VisibilityTweak(TweakConfigs.SAMHide0Meditation, gauge->UseSimpleGauge, gauge->GaugeStandard.MeditationContainer, gauge->GaugeSimple.MeditationContainer); 67 | } 68 | 69 | public override unsafe void ApplyTweaks1(IntPtr gaugeAddon) 70 | { 71 | var gauge = (AddonJobHudSAM1*)gaugeAddon; 72 | var simple1 = gauge->UseSimpleGauge; 73 | VisibilityTweak(TweakConfigs.SAMHide1, simple1, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 74 | 75 | if (gauge != null && gauge->GaugeStandard.Container != null) 76 | { 77 | var setsuPos = TweakConfigs.SAMSealPosSetsu; 78 | var getsuPos = TweakConfigs.SAMSealPosGetsu; 79 | var kaPos = TweakConfigs.SAMSealPosKa; 80 | if (!simple1) 81 | { 82 | gauge->GaugeStandard.SetsuNode->SetPositionFloat(setsuPos.X + 44, setsuPos.Y + 4); 83 | gauge->GaugeStandard.GetsuNode->SetPositionFloat(getsuPos.X, getsuPos.Y + 76); 84 | gauge->GaugeStandard.KaNode->SetPositionFloat(kaPos.X + 90, kaPos.Y + 73); 85 | } 86 | else 87 | { 88 | gauge->GaugeSimple.SetsuNode->SetPositionFloat(setsuPos.X + 0, setsuPos.Y); 89 | gauge->GaugeSimple.GetsuNode->SetPositionFloat(getsuPos.X + 19, getsuPos.Y); 90 | gauge->GaugeSimple.KaNode->SetPositionFloat(kaPos.X + 38, kaPos.Y); 91 | } 92 | } 93 | } 94 | } 95 | 96 | public partial class TweakConfigs 97 | { 98 | public bool SAMHide0Kenki; 99 | public bool SAMHide0Meditation; 100 | public bool SAMHide1; 101 | public Vector2 SAMSealPosSetsu; 102 | public Vector2 SAMSealPosGetsu; 103 | public Vector2 SAMSealPosKa; 104 | } 105 | -------------------------------------------------------------------------------- /GaugeOMatic/Widgets/Common/LabelTextNode.cs: -------------------------------------------------------------------------------- 1 | using CustomNodes; 2 | using Dalamud.Interface; 3 | using FFXIVClientStructs.FFXIV.Component.GUI; 4 | using Newtonsoft.Json; 5 | using System.Collections.Generic; 6 | using System.Numerics; 7 | using static CustomNodes.CustomNode.CustomNodeFlags; 8 | using static CustomNodes.CustomNodeManager; 9 | using static Dalamud.Interface.FontAwesomeIcon; 10 | using static FFXIVClientStructs.FFXIV.Component.GUI.AlignmentType; 11 | using static FFXIVClientStructs.FFXIV.Component.GUI.FontType; 12 | using static GaugeOMatic.Utility.Color; 13 | using static GaugeOMatic.Widgets.Common.WidgetUI; 14 | using static Newtonsoft.Json.DefaultValueHandling; 15 | 16 | namespace GaugeOMatic.Widgets.Common; 17 | 18 | public class LabelTextNode : CustomNode 19 | { 20 | public LabelTextProps Props { get; set; } 21 | public string Fallback { get; set; } 22 | public static unsafe implicit operator AtkResNode*(LabelTextNode l) => l.Node; 23 | 24 | public unsafe LabelTextNode(string label, string fallback) 25 | { 26 | Fallback = fallback; 27 | Node = (AtkResNode*)CreateTextNode(label.Length > 0 ? label : fallback, 20, 52); 28 | SetText(label); 29 | SetTextColor(0xffffffff, 0x000000ff); 30 | RemoveFlags(SetVisByAlpha); 31 | 32 | Children = []; 33 | } 34 | 35 | public LabelTextNode SetLabelText(string text) 36 | { 37 | SetText(text.Length > 0 ? text : Fallback); 38 | return this; 39 | } 40 | 41 | public LabelTextNode ApplyProps(LabelTextProps props, Vector2? posAdjust = null) 42 | { 43 | Props = props; 44 | this.SetLabelText(Props.Text) 45 | .SetTextColor(Props.Color, Props.EdgeColor) 46 | .SetTextFont(Props.Font) 47 | .SetTextAlign(Props.Align) 48 | .SetTextSize(Props.FontSize) 49 | .SetPos(Props.Position + (posAdjust ?? new(0))) 50 | .SetVis(Props.Enabled); 51 | return this; 52 | } 53 | } 54 | 55 | public struct LabelTextProps 56 | { 57 | internal static List AlignList = [Left, Center, Right]; 58 | internal static List AlignIcons = [AlignLeft, AlignCenter, AlignRight]; 59 | internal static List FontList = [Axis, MiedingerMed, TrumpGothic, Jupiter]; 60 | internal static List FontNames = ["Axis", "Miedinger Med", "Trump Gothic", "Jupiter"]; 61 | 62 | public string Text; 63 | [JsonProperty(DefaultValueHandling = Include)] public bool Enabled = false; 64 | [JsonProperty(DefaultValueHandling = Include)] public Vector2 Position; 65 | public ColorRGB Color = new(255, 255, 255); 66 | public ColorRGB EdgeColor = new(0, 0, 0); 67 | [JsonProperty(DefaultValueHandling = Include)] public FontType Font = Miedinger; 68 | [JsonProperty(DefaultValueHandling = Include)] public byte FontSize = 18; 69 | [JsonProperty(DefaultValueHandling = Include)] public AlignmentType Align = Center; 70 | 71 | public LabelTextProps(string text, bool enabled, Vector2 position, ColorRGB color, ColorRGB edgeColor, FontType font, byte fontSize, AlignmentType align) 72 | { 73 | Text = text; 74 | Enabled = enabled; 75 | Position = position; 76 | Color = color; 77 | EdgeColor = edgeColor; 78 | Font = font; 79 | FontSize = fontSize; 80 | Align = align; 81 | } 82 | 83 | public static void LabelTextControls(string label, ref LabelTextProps configVal, string hintText) 84 | { 85 | var labelTextProps = configVal; 86 | 87 | ToggleControls(label, ref labelTextProps.Enabled); 88 | if (labelTextProps.Enabled) 89 | { 90 | var text = labelTextProps.Text; 91 | 92 | if (StringControls("Override Text", ref text, hintText)) labelTextProps.Text = text; 93 | 94 | PositionControls($"Position##{label}Pos", ref labelTextProps.Position); 95 | ColorPickerRGBA($"Color##{label}color", ref labelTextProps.Color); 96 | ColorPickerRGBA($"Edge Color##{label}edgeColor", ref labelTextProps.EdgeColor); 97 | 98 | ComboControls($"Font##{label}font", ref labelTextProps.Font, FontList, FontNames); 99 | 100 | RadioIcons($"Alignment##{label}align", ref labelTextProps.Align, AlignList, AlignIcons); 101 | IntControls($"Font Size##{label}fontSize", ref labelTextProps.FontSize, 1, 100, 1); 102 | } 103 | 104 | if (UpdateFlag.HasFlag(UpdateFlags.Save)) configVal = labelTextProps; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/Structs/AddonJobHudDRK.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using FFXIVClientStructs.Attributes; 3 | using FFXIVClientStructs.FFXIV.Component.GUI; 4 | using static FFXIVClientStructs.FFXIV.Client.UI.AddonJobHud; 5 | // ReSharper disable All 6 | namespace FFXIVClientStructs.FFXIV.Client.UI; 7 | 8 | /// 9 | /// DRK - Blood Gauge 10 | /// 11 | [Addon("JobHudDRK0")] 12 | [StructLayout(LayoutKind.Explicit, Size = 0x318)] 13 | public unsafe partial struct AddonJobHudDRK0 { 14 | [FieldOffset(0x000)] public AddonJobHud JobHud; 15 | 16 | [StructLayout(LayoutKind.Explicit, Size = 0x18)] 17 | public partial struct BloodGaugeData { 18 | [FieldOffset(0x00)] public AddonJobHudGaugeData GaugeData; 19 | [FieldOffset(0x08)] public fixed byte Prerequisites[2]; 20 | [FieldOffset(0x0A)] public byte TankStance; 21 | [FieldOffset(0x0C)] public int BloodValue; 22 | [FieldOffset(0x10)] public int BloodMax; 23 | [FieldOffset(0x14)] public int BloodMid; 24 | } 25 | 26 | [StructLayout(LayoutKind.Explicit, Size = 0x48)] 27 | public partial struct BloodGauge { 28 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 29 | [FieldOffset(0x10)] public AtkResNode* Container; 30 | [FieldOffset(0x18)] public AtkResNode* StanceGemContainer; 31 | [FieldOffset(0x20)] public AtkResNode* SwordGlow; 32 | [FieldOffset(0x28)] public AtkResNode* StanceGem; 33 | [FieldOffset(0x30)] public AtkTextNode* BloodValueText; 34 | [FieldOffset(0x38)] public AtkComponentGaugeBar* BloodGaugeBar; 35 | } 36 | 37 | [StructLayout(LayoutKind.Explicit, Size = 0x40)] 38 | public partial struct BloodGaugeSimple { 39 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 40 | [FieldOffset(0x10)] public AtkResNode* Container; 41 | [FieldOffset(0x18)] public AtkResNode* BarContainer; 42 | [FieldOffset(0x20)] public AtkComponentBase* StanceIcon; 43 | [FieldOffset(0x28)] public AtkComponentTextNineGrid* BloodValueDisplay; 44 | [FieldOffset(0x38)] public AtkComponentGaugeBar* BloodGaugeBar; 45 | } 46 | 47 | [FieldOffset(0x260)] public BloodGaugeData DataPrevious; 48 | [FieldOffset(0x278)] public BloodGaugeData DataCurrent; 49 | [FieldOffset(0x290)] public BloodGauge GaugeStandard; 50 | [FieldOffset(0x2D8)] public BloodGaugeSimple GaugeSimple; 51 | } 52 | 53 | /// 54 | /// DRK - Darkside Gauge 55 | /// 56 | [Addon("JobHudDRK1")] 57 | [StructLayout(LayoutKind.Explicit, Size = 0x338)] 58 | public unsafe partial struct AddonJobHudDRK1 { 59 | [FieldOffset(0x000)] public AddonJobHud JobHud; 60 | 61 | [StructLayout(LayoutKind.Explicit, Size = 0x18)] 62 | public partial struct DarksideGaugeData { 63 | [FieldOffset(0x00)] public AddonJobHudGaugeData GaugeData; 64 | [FieldOffset(0x08)] public fixed byte Prerequisites[3]; 65 | [FieldOffset(0x0C)] public int DarksideTimeLeft; 66 | [FieldOffset(0x10)] public int DarksideTimeMax; 67 | [FieldOffset(0x14)] public int LivingShadowTimeLeft; 68 | } 69 | 70 | [StructLayout(LayoutKind.Explicit, Size = 0x60)] 71 | public partial struct DarksideGauge { 72 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 73 | [FieldOffset(0x10)] public AtkResNode* Container; 74 | [FieldOffset(0x18)] public AtkResNode* DarkArtsContainer; 75 | [FieldOffset(0x20)] public AtkResNode* DarkArts; 76 | [FieldOffset(0x30)] public AtkResNode* DarksideHelm; 77 | [FieldOffset(0x38)] public AtkTextNode* DarksideTimerText; 78 | [FieldOffset(0x48)] public AtkResNode* LivingShadowHelm; 79 | [FieldOffset(0x50)] public AtkTextNode* LivingShadowTimerText; 80 | } 81 | 82 | [StructLayout(LayoutKind.Explicit, Size = 0x48)] 83 | public partial struct DarksideGaugeSimple { 84 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 85 | [FieldOffset(0x10)] public AtkComponentBase* DarkArtsGem ; 86 | [FieldOffset(0x20)] public AtkComponentGaugeBar* DarksideGaugeBar; 87 | [FieldOffset(0x28)] public AtkComponentTextNineGrid* DarksideValueDisplay; 88 | [FieldOffset(0x30)] public AtkResNode* LivingShadow; 89 | [FieldOffset(0x38)] public AtkResNode* LivingShadowTimerDisplay; 90 | } 91 | 92 | [FieldOffset(0x260)] public DarksideGaugeData DataPrevious; 93 | [FieldOffset(0x278)] public DarksideGaugeData DataCurrent; 94 | [FieldOffset(0x290)] public DarksideGauge GaugeStandard; 95 | [FieldOffset(0x2F0)] public DarksideGaugeSimple GaugeSimple; 96 | } 97 | -------------------------------------------------------------------------------- /GaugeOMatic/CustomNodes/CustomNode.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Component.GUI; 2 | using System; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using static CustomNodes.CustomNodeManager; 7 | 8 | namespace CustomNodes; 9 | 10 | [SuppressMessage("ReSharper", "UnusedMember.Global")] 11 | public unsafe partial class CustomNode(AtkResNode* node, params CustomNode[] children) : IDisposable 12 | { 13 | public AtkResNode* Node = node; 14 | public CustomNode[] Children { get; set; } = children; 15 | 16 | public byte* TextBuffer; 17 | public int TextBufferLen; 18 | 19 | public CustomNode(AtkImageNode* node) : this((AtkResNode*)node) { } 20 | public CustomNode(AtkNineGridNode* node) : this((AtkResNode*)node) { } 21 | public CustomNode(AtkTextNode* node) : this((AtkResNode*)node) { } 22 | public CustomNode(AtkClippingMaskNode* node) : this((AtkResNode*)node) { } 23 | public CustomNode(AtkComponentNode* node) : this((AtkResNode*)node) { } 24 | 25 | public static implicit operator CustomNode(AtkResNode* a) => new(a); 26 | public static implicit operator CustomNode(AtkImageNode* a) => new(a); 27 | public static implicit operator CustomNode(AtkTextNode* a) => new(a); 28 | public static implicit operator CustomNode(AtkNineGridNode* a) => new(a); 29 | public static implicit operator CustomNode(AtkClippingMaskNode* a) => new(a); 30 | public static implicit operator CustomNode(AtkComponentNode* a) => new(a); 31 | 32 | public static implicit operator AtkResNode*(CustomNode c) => c.Node; 33 | 34 | public CustomNode this[int i] 35 | { 36 | get 37 | { 38 | if (i < Children.Length) return Children[i]; 39 | 40 | try 41 | { 42 | AtkComponentNode* comp; 43 | AtkUldManager uld; 44 | 45 | return Node == null ? Warning("Node is null and has no children") : 46 | (comp = Node->GetAsAtkComponentNode()) == null || (uld = comp->Component->UldManager).NodeListSize < i ? 47 | Warning($"No Child node found at index {i}") : 48 | uld.NodeList[i]; 49 | } 50 | catch (Exception) 51 | { 52 | return Warning($"Error retrieving child node at index {i}"); 53 | } 54 | } 55 | } 56 | 57 | public CustomNode this[uint id] 58 | { 59 | get 60 | { 61 | try 62 | { 63 | AtkComponentNode* comp; 64 | if (Node == null) return Warning("Node is null and has no children"); 65 | if ((comp = Node->GetAsAtkComponentNode()) == null) return Warning($"No Child node found with ID {id}"); 66 | 67 | return comp->Component->UldManager.SearchNodeById(id); 68 | } 69 | catch (Exception) 70 | { 71 | return Warning($"Error retrieving child node with ID {id}"); 72 | } 73 | } 74 | } 75 | 76 | public void Append(CustomNode newChild) => Children = Children.Append(newChild).ToArray(); 77 | 78 | public CustomNode(AtkResNode* node) : this(node, []) { } 79 | 80 | public CustomNode() : this(null, []) { } 81 | 82 | public int AssembleNodeTree() 83 | { 84 | if (Children.Length == 0) return Node->ChildCount; 85 | 86 | var count = Children.Length; 87 | for (var i = 0; i < Children.Length; i++) 88 | { 89 | Children[i].Node->ParentNode = Node; 90 | count += Children[i].AssembleNodeTree(); 91 | if (i < Children.Length - 1) LinkSiblings(Children[i].Node, Children[i + 1].Node); 92 | } 93 | 94 | Node->ChildNode = Children[0].Node; 95 | Node->ChildCount = (ushort)count; 96 | return count; 97 | } 98 | 99 | public void Dispose() 100 | { 101 | foreach (var child in Children) child.Dispose(); 102 | 103 | if (Node != null) 104 | { 105 | if (TextBuffer != null) 106 | { 107 | NativeMemory.Free(TextBuffer); 108 | TextBufferLen = 0; 109 | } 110 | Node->Destroy(true); 111 | Node = null; 112 | } 113 | } 114 | 115 | public void Detach() => DetachNode(Node); 116 | public void AttachTo(AtkUnitBase* parentAddon) => AttachNode(parentAddon, Node); 117 | public void AttachTo(AtkResNode* parentNode) => AttachNode(parentNode, Node); 118 | } 119 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Caster/PCT.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.UI; 2 | using GaugeOMatic.Trackers; 3 | using GaugeOMatic.Windows.Dropdowns; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Numerics; 7 | using GaugeOMatic.GameData; 8 | using static GaugeOMatic.GameData.JobData; 9 | using static GaugeOMatic.GameData.JobData.Job; 10 | using static GaugeOMatic.GameData.JobData.Role; 11 | using static GaugeOMatic.JobModules.Tweaks; 12 | using static GaugeOMatic.Widgets.Common.WidgetUI; 13 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 14 | 15 | namespace GaugeOMatic.JobModules; 16 | 17 | public class PCTModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 18 | : JobModule(trackerManager, trackerConfigList, "JobHudRPM0", "JobHudRPM1") 19 | { 20 | public override Job Job => PCT; 21 | public override Job Class => Job.None; 22 | public override Role Role => Caster; 23 | public override List AddonOptions => 24 | [ 25 | new("JobHudRPM0", "Canvases"), 26 | new("JobHudRPM1", "Palette Gauge"), 27 | new("_ParameterWidget", "Parameter Bar") 28 | ]; 29 | 30 | public override List JobGaugeMenu { get; } = 31 | [ 32 | new("Creature Motif Deadline", nameof(CreatureMotifDeadline)), 33 | new("Weapon Motif Deadline", nameof(WeaponMotifDeadline)), 34 | new("Landscape Motif Deadline", nameof(LandscapeMotifDeadline)), 35 | new("Palette Gauge", nameof(PaletteGaugeTracker)) 36 | ]; 37 | 38 | public override void Save() 39 | { 40 | Configuration.TrackerConfigs.PCT = SaveOrder; 41 | Configuration.Save(); 42 | } 43 | 44 | public override void TweakUI() 45 | { 46 | Heading("Palette Gauge"); 47 | ToggleControls("Hide Palette Gauge", ref TweakConfigs.PCTHide1); 48 | 49 | Heading("Canvases"); 50 | ToggleControls("Hide Canvases", ref TweakConfigs.PCTHide0); 51 | 52 | if (!TweakConfigs.PCTHide0) 53 | { 54 | ToggleControls("Hide Easels", ref TweakConfigs.PCTHide0Easels); 55 | 56 | Heading("Reposition Canvases"); 57 | PositionControls("Creature", ref TweakConfigs.PCT0CanvasPosCreature); 58 | PositionControls("Weapon", ref TweakConfigs.PCT0CanvasPosWeapon); 59 | PositionControls("Landscape", ref TweakConfigs.PCT0CanvasPosLandscape); 60 | } 61 | } 62 | 63 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 64 | { 65 | var gauge = (AddonJobHudRPM0*)gaugeAddon; 66 | VisibilityTweak(TweakConfigs.PCTHide0, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 67 | 68 | if (gauge != null && gauge->GaugeStandard.Container != null) 69 | { 70 | var hideEasels = TweakConfigs.PCTHide0Easels; 71 | 72 | gauge->GetNodeById(3)->SetPositionFloat(TweakConfigs.PCT0CanvasPosCreature.X, TweakConfigs.PCT0CanvasPosCreature.Y); 73 | gauge->GetNodeById(19)->SetPositionFloat(TweakConfigs.PCT0CanvasPosWeapon.X + 102, TweakConfigs.PCT0CanvasPosWeapon.Y); 74 | gauge->GetNodeById(23)->SetPositionFloat(TweakConfigs.PCT0CanvasPosLandscape.X + 204, TweakConfigs.PCT0CanvasPosLandscape.Y); 75 | 76 | gauge->GetNodeById(28)->SetPositionFloat(TweakConfigs.PCT0CanvasPosCreature.X, TweakConfigs.PCT0CanvasPosCreature.Y); 77 | gauge->GetNodeById(38)->SetPositionFloat(TweakConfigs.PCT0CanvasPosWeapon.X + 78, TweakConfigs.PCT0CanvasPosWeapon.Y); 78 | gauge->GetNodeById(41)->SetPositionFloat(TweakConfigs.PCT0CanvasPosLandscape.X + 156, TweakConfigs.PCT0CanvasPosLandscape.Y); 79 | 80 | gauge->GetNodeById(10)->ToggleVisibility(!hideEasels); 81 | gauge->GetNodeById(20)->ToggleVisibility(!hideEasels); 82 | gauge->GetNodeById(24)->ToggleVisibility(!hideEasels); 83 | } 84 | } 85 | 86 | public override unsafe void ApplyTweaks1(IntPtr gaugeAddon) 87 | { 88 | var gauge = (AddonJobHudRPM1*)GameGui.GetAddonByName("JobHudRPM1"); 89 | VisibilityTweak(TweakConfigs.PCTHide1, gauge->UseSimpleGauge, gauge->GetNodeById(2), gauge->GetNodeById(28)); 90 | } 91 | 92 | public static float LivingMuseFix() => FrameworkData.LocalPlayer.Lvl < 96 ? 80f : 120f; 93 | } 94 | 95 | public partial class TweakConfigs 96 | { 97 | public bool PCTHide0; 98 | public bool PCTHide0Easels; 99 | public bool PCTHide1; 100 | public Vector2 PCT0CanvasPosCreature; 101 | public Vector2 PCT0CanvasPosWeapon; 102 | public Vector2 PCT0CanvasPosLandscape; 103 | } 104 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/Structs/AddonJobHudMNK.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using FFXIVClientStructs.Attributes; 3 | using FFXIVClientStructs.FFXIV.Component.GUI; 4 | using FFXIVClientStructs.Interop; 5 | using FFXIVClientStructs.Interop.Attributes; 6 | using static FFXIVClientStructs.FFXIV.Client.UI.AddonJobHud; 7 | // ReSharper disable All 8 | namespace FFXIVClientStructs.FFXIV.Client.UI; 9 | 10 | /// 11 | /// MNK - Master's Gauge 12 | /// 13 | [Addon("JobHudMNK0")] 14 | [StructLayout(LayoutKind.Explicit, Size = 0x420)] 15 | public unsafe partial struct AddonJobHudMNK0 { 16 | [FieldOffset(0x000)] public AddonJobHud JobHud; 17 | 18 | [StructLayout(LayoutKind.Explicit, Size = 0x28)] 19 | public partial struct MastersGaugeData { 20 | [FieldOffset(0x00)] public AddonJobHudGaugeData GaugeData; 21 | [FieldOffset(0x08)] public fixed byte Prerequisites[2]; 22 | [FieldOffset(0x0C)] public int BeastChakra1; 23 | [FieldOffset(0x10)] public int BeastChakra2; 24 | [FieldOffset(0x14)] public int BeastChakra3; 25 | [FieldOffset(0x18)] public bool LunarNadi; 26 | [FieldOffset(0x19)] public bool SolarNadi; 27 | [FieldOffset(0x1C)] public int BlitzTimeRemaining; 28 | [FieldOffset(0x20)] public int BlitzType; 29 | } 30 | 31 | [StructLayout(LayoutKind.Explicit, Size = 0xB0)] 32 | public partial struct MastersGauge { 33 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 34 | [FieldOffset(0x10)] public AtkResNode* NadiContainer; 35 | [FieldOffset(0x18)] public AtkComponentBase* BeastChakra1; 36 | [FieldOffset(0x20)] public AtkResNode* BeastChakraIcon1; 37 | [FieldOffset(0x28)] public AtkComponentBase* BeastChakra2; 38 | [FieldOffset(0x30)] public AtkResNode* BeastChakraIcon2; 39 | [FieldOffset(0x38)] public AtkComponentBase* BeastChakra3; 40 | [FieldOffset(0x40)] public AtkResNode* BeastChakraIcon3; 41 | [FieldOffset(0x60)] public AtkComponentBase* LunarNadi; 42 | [FieldOffset(0x68)] public AtkComponentBase* SolarNadi; 43 | [FieldOffset(0x70)] public AtkResNode* NadiIcons; 44 | [FieldOffset(0x80)] public AtkResNode* BlitzTimer; 45 | [FieldOffset(0x88)] public AtkTextNode* BlitzTimerText; 46 | [FieldOffset(0x90)] public bool BlitzActive; 47 | [FieldOffset(0x94)] public int BlitzType; 48 | [FieldOffset(0xA0)] public AtkResNode* Nadi; 49 | [FieldOffset(0xA8)] public AtkResNode* BeastChakraSlots; 50 | } 51 | 52 | [StructLayout(LayoutKind.Explicit, Size = 0xC0)] 53 | public partial struct MastersGaugeSimple { 54 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 55 | } 56 | 57 | [FieldOffset(0x260)] public MastersGaugeData DataPrevious; 58 | [FieldOffset(0x288)] public MastersGaugeData DataCurrent; 59 | [FieldOffset(0x2B0)] public MastersGauge GaugeStandard; 60 | [FieldOffset(0x360)] public MastersGaugeSimple GaugeSimple; 61 | } 62 | 63 | /// 64 | /// MNK - Chakra Gauge 65 | /// 66 | [Addon("JobHudMNK1")] 67 | [StructLayout(LayoutKind.Explicit, Size = 0x308)] 68 | public unsafe partial struct AddonJobHudMNK1 { 69 | [FieldOffset(0x000)] public AddonJobHud JobHud; 70 | 71 | [StructLayout(LayoutKind.Explicit, Size = 0x10)] 72 | public partial struct ChakraGaugeData { 73 | [FieldOffset(0x00)] public AddonJobHudGaugeData GaugeData; 74 | [FieldOffset(0x08)] public fixed byte Prerequisites[1]; 75 | [FieldOffset(0x0C)] public int ChakraCount; 76 | } 77 | 78 | [StructLayout(LayoutKind.Explicit, Size = 0x40)] 79 | public partial struct ChakraGauge { 80 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 81 | [FieldOffset(0x10)] public AtkResNode* Container; 82 | 83 | [FixedSizeArray>(5)] 84 | [FieldOffset(0x18)] public fixed byte Chakra[5 * 0x8]; 85 | } 86 | 87 | [StructLayout(LayoutKind.Explicit, Size = 0x48)] 88 | public partial struct ChakraGaugeSimple { 89 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 90 | [FieldOffset(0x10)] public AtkResNode* Container; 91 | 92 | [FixedSizeArray>(5)] 93 | [FieldOffset(0x18)] public fixed byte Chakra[5 * 0x8]; 94 | 95 | [FieldOffset(0x40)] public bool ChakraCapped; 96 | } 97 | 98 | [FieldOffset(0x260)] public ChakraGaugeData DataPrevious; 99 | [FieldOffset(0x270)] public ChakraGaugeData DataCurrent; 100 | [FieldOffset(0x280)] public ChakraGauge GaugeStandard; 101 | [FieldOffset(0x2C0)] public ChakraGaugeSimple GaugeSimple; 102 | } 103 | -------------------------------------------------------------------------------- /GaugeOMatic/JobModules/Healer/SCH.cs: -------------------------------------------------------------------------------- 1 | using CustomNodes; 2 | using FFXIVClientStructs.FFXIV.Client.UI; 3 | using GaugeOMatic.Trackers; 4 | using GaugeOMatic.Windows.Dropdowns; 5 | using System; 6 | using System.Collections.Generic; 7 | using static GaugeOMatic.GameData.JobData; 8 | using static GaugeOMatic.GameData.JobData.Job; 9 | using static GaugeOMatic.GameData.JobData.Role; 10 | using static GaugeOMatic.GameData.StatusRef; 11 | using static GaugeOMatic.GameData.StatusRef.StatusActor; 12 | using static GaugeOMatic.JobModules.Tweaks; 13 | using static GaugeOMatic.Widgets.Common.WidgetUI; 14 | using static GaugeOMatic.Windows.Dropdowns.TrackerDropdown; 15 | 16 | namespace GaugeOMatic.JobModules; 17 | 18 | public class SCHModule(TrackerManager trackerManager, TrackerConfig[] trackerConfigList) 19 | : JobModule(trackerManager, trackerConfigList, "JobHudACN0", "JobHudSCH0") 20 | { 21 | public override Job Job => SCH; 22 | public override Job Class => ACN; 23 | public override Role Role => Healer; 24 | public override List AddonOptions => 25 | [ 26 | new("JobHudACN0", "Aetherflow Gauge"), 27 | new("JobHudSCH0", "Faerie Gauge"), 28 | new("_ParameterWidget", "Parameter Bar") 29 | ]; 30 | 31 | public override List JobGaugeMenu => 32 | [ 33 | new("Aetherflow Gauge", nameof(AetherflowSCHGaugeTracker)), 34 | new("Fae Aether", nameof(FaerieGaugeTracker)), 35 | new("Seraph Timer", nameof(SeraphTracker)) 36 | ]; 37 | 38 | public override void Save() 39 | { 40 | Configuration.TrackerConfigs.SCH = SaveOrder; 41 | Configuration.Save(); 42 | } 43 | 44 | public override void TweakUI() 45 | { 46 | Heading("Aetherflow Gauge"); 47 | ToggleControls("Hide Aetherflow Gauge", ref TweakConfigs.SCHHide0); 48 | 49 | Heading("Faerie Gauge"); 50 | ToggleControls("Hide Faerie Gauge", ref TweakConfigs.SCHHide1); 51 | 52 | if (!TweakConfigs.SCHHide1) 53 | RadioControls("While Faerieless: ", ref TweakConfigs.SCH1FaerieLess, 54 | new() { 0, 1, 2 }, 55 | ["Show Gauge Value", "Hide Gauge Value", "Show Dissipation Timer"]); 56 | } 57 | 58 | public override unsafe void ApplyTweaks0(IntPtr gaugeAddon) 59 | { 60 | var gauge = (AddonJobHudACN0*)gaugeAddon; 61 | var gaugeIndex = (AddonIndex)gaugeAddon; 62 | VisibilityTweak(TweakConfigs.SCHHide0, gauge->UseSimpleGauge, gaugeIndex[2u], gaugeIndex[7u]); 63 | } 64 | 65 | public override unsafe void ApplyTweaks1(IntPtr gaugeAddon) 66 | { 67 | var gauge = (AddonJobHudSCH0*)gaugeAddon; 68 | VisibilityTweak(TweakConfigs.SCHHide1, gauge->UseSimpleGauge, gauge->GaugeStandard.Container, gauge->GaugeSimple.Container); 69 | 70 | FaerielessTweak(gauge); 71 | } 72 | 73 | private unsafe void FaerielessTweak(AddonJobHudSCH0* gauge) 74 | { 75 | if (gauge == null || gauge->GaugeStandard.FaeGaugeTextContainer == null) return; 76 | var summoned = gauge->DataCurrent.FaerieSummoned; 77 | bool show; 78 | int dissTimer; 79 | 80 | switch (TweakConfigs.SCH1FaerieLess) 81 | { 82 | case 1: 83 | show = summoned; 84 | dissTimer = 0; 85 | break; 86 | case 2: 87 | show = StatusData[791].TryGetStatus(out var buff, Self) || summoned; 88 | dissTimer = (int)Math.Abs(buff?.RemainingTime ?? 0); 89 | break; 90 | default: 91 | show = true; 92 | dissTimer = 0; 93 | break; 94 | } 95 | 96 | ((CustomNode)gauge->GaugeStandard.FaeGaugeTextContainer).SetVis(show); 97 | ((CustomNode)gauge->GaugeSimple.FaeValueDisplay->OwnerNode).SetVis(show); 98 | 99 | var textStandard = (CustomNode)gauge->GaugeStandard.FaeGaugeText; 100 | var textSimple = (CustomNode)gauge->GaugeSimple.FaeValueDisplay->AtkTextNode; 101 | if (dissTimer > 0) 102 | { 103 | textStandard.SetText(dissTimer.ToString()).SetTextColor(0xD7D7D7FF, 0x316381FF); 104 | textSimple.SetText(dissTimer.ToString()).SetTextColor(0xD7D7D7FF, 0x316381FF); 105 | } 106 | else 107 | { 108 | textStandard.SetTextColor(0xffffffff, 0x288246ff); 109 | textSimple.SetTextColor(0xffffffff, 0x9d835bff); 110 | } 111 | } 112 | } 113 | 114 | public partial class TweakConfigs 115 | { 116 | public bool SCHHide0; 117 | public uint SCH1FaerieLess; 118 | public bool SCHHide1; 119 | public bool SCHDissHideText; 120 | } 121 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/Structs/AddonJobHudSGE.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using FFXIVClientStructs.Attributes; 3 | using FFXIVClientStructs.FFXIV.Component.GUI; 4 | using FFXIVClientStructs.Interop; 5 | using FFXIVClientStructs.Interop.Attributes; 6 | using static FFXIVClientStructs.FFXIV.Client.UI.AddonJobHud; 7 | // ReSharper disable All 8 | namespace FFXIVClientStructs.FFXIV.Client.UI; 9 | 10 | /// 11 | /// SGE - Eukrasia Gauge 12 | /// 13 | [Addon("JobHudGFF0")] 14 | [StructLayout(LayoutKind.Explicit, Size = 0x2C0)] 15 | public unsafe partial struct AddonJobHudGFF0 { 16 | [FieldOffset(0x000)] public AddonJobHud JobHud; 17 | 18 | [StructLayout(LayoutKind.Explicit, Size = 0x10)] 19 | public partial struct EukrasiaGaugeData { 20 | [FieldOffset(0x00)] public AddonJobHudGaugeData GaugeData; 21 | [FieldOffset(0x08)] public fixed byte Prerequisites[1]; 22 | [FieldOffset(0x09)] public bool EukrasiaActive; 23 | } 24 | 25 | [StructLayout(LayoutKind.Explicit, Size = 0x20)] 26 | public partial struct EukrasiaGauge { 27 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 28 | [FieldOffset(0x10)] public AtkResNode* Container; 29 | [FieldOffset(0x18)] public bool EukrasiaActive; 30 | } 31 | 32 | [StructLayout(LayoutKind.Explicit, Size = 0x20)] 33 | public partial struct EukrasiaGaugeSimple { 34 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 35 | [FieldOffset(0x10)] public AtkResNode* Container; 36 | [FieldOffset(0x18)] public bool EukrasiaActive; 37 | } 38 | 39 | [FieldOffset(0x260)] public EukrasiaGaugeData DataPrevious; 40 | [FieldOffset(0x270)] public EukrasiaGaugeData DataCurrent; 41 | [FieldOffset(0x280)] public EukrasiaGauge GaugeStandard; 42 | [FieldOffset(0x2A0)] public EukrasiaGaugeSimple GaugeSimple; 43 | } 44 | 45 | /// 46 | /// SGE - Addersgall Gauge 47 | /// 48 | [Addon("JobHudGFF1")] 49 | [StructLayout(LayoutKind.Explicit, Size = 0x370)] 50 | public unsafe partial struct AddonJobHudGFF1 { 51 | [FieldOffset(0x000)] public AddonJobHud JobHud; 52 | 53 | [StructLayout(LayoutKind.Explicit, Size = 0x20)] 54 | public partial struct AddersgallGaugeData { 55 | [FieldOffset(0x00)] public AddonJobHudGaugeData GaugeData; 56 | [FieldOffset(0x08)] public fixed byte Prerequisites[2]; 57 | [FieldOffset(0x0C)] public int Addersgall; 58 | [FieldOffset(0x10)] public int Addersting; 59 | [FieldOffset(0x14)] public int AddersgallTimer; 60 | [FieldOffset(0x18)] public int AddersgallTimerMax; 61 | [FieldOffset(0x1C)] public bool InCombat; 62 | } 63 | 64 | [StructLayout(LayoutKind.Explicit, Size = 0x70)] 65 | public partial struct AddersgallGauge { 66 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 67 | [FieldOffset(0x10)] public AtkResNode* AdderstingContainer; 68 | 69 | [FixedSizeArray>(3)] 70 | [FieldOffset(0x18)] public fixed byte AddersgallGem[3 * 0x08]; 71 | 72 | [FixedSizeArray>(3)] 73 | [FieldOffset(0x30)] public fixed byte AdderstingGem[3 * 0x08]; 74 | 75 | [FieldOffset(0x48)] public AtkResNode* TimerBar; 76 | [FieldOffset(0x50)] public AtkResNode* TimerFill; 77 | [FieldOffset(0x58)] public AtkResNode* Container; 78 | [FieldOffset(0x60)] public int Addersgall; 79 | [FieldOffset(0x64)] public int Addersting; 80 | [FieldOffset(0x68)] public int TimelineFrameId; 81 | } 82 | 83 | [StructLayout(LayoutKind.Explicit, Size = 0x60)] 84 | public partial struct AddersgallGaugeSimple { 85 | [FieldOffset(0x00)] public AddonJobHudGauge Gauge; 86 | [FieldOffset(0x10)] public AtkResNode* AdderstingContainer; 87 | 88 | [FixedSizeArray>(3)] 89 | [FieldOffset(0x18)] public fixed byte AddersgallGem[3 * 0x08]; 90 | 91 | [FixedSizeArray>(3)] 92 | [FieldOffset(0x30)] public fixed byte AdderstingGem[3 * 0x08]; 93 | 94 | [FieldOffset(0x48)] public AtkComponentGaugeBar* TimerGaugeBar; 95 | [FieldOffset(0x50)] public int Addersgall; 96 | [FieldOffset(0x54)] public int Addersting; 97 | [FieldOffset(0x58)] public int AddersgallTimelineFrameId; 98 | [FieldOffset(0x5C)] public int AdderstingTimelineFrameId; 99 | } 100 | 101 | [FieldOffset(0x260)] public AddersgallGaugeData DataPrevious; 102 | [FieldOffset(0x280)] public AddersgallGaugeData DataCurrent; 103 | [FieldOffset(0x2A0)] public AddersgallGauge GaugeStandard; 104 | [FieldOffset(0x310)] public AddersgallGaugeSimple GaugeSimple; 105 | } 106 | -------------------------------------------------------------------------------- /GaugeOMatic/Windows/Tooltips/ActionTooltip.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Interface.Textures.TextureWraps; 2 | using ImGuiNET; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Numerics; 7 | using static Dalamud.Interface.Utility.ImGuiHelpers; 8 | using static GaugeOMatic.GameData.ActionFlags; 9 | using static GaugeOMatic.GameData.ActionRef.BarType; 10 | using static GaugeOMatic.GaugeOMatic; 11 | using static GaugeOMatic.Utility.ImGuiHelpy; 12 | 13 | namespace GaugeOMatic.GameData; 14 | 15 | public partial class ActionRef 16 | { 17 | public Dictionary FlagNames = new() 18 | { 19 | { LongCooldown, "Cooldown" }, 20 | { HasCharges, "Charges" }, 21 | { ComboBonus, "Combo" }, 22 | { Unassignable, "Unassignable" }, 23 | { RequiresStatus, "Status-Based" }, 24 | { CanGetAnts, "Conditional" }, 25 | { RoleAction, "Role" } 26 | }; 27 | 28 | public static IDalamudTextureWrap? FrameTex => TextureProvider.GetFromFile(Path.Combine(PluginDirPath, @"TextureAssets\iconFrame.png")) 29 | .GetWrapOrDefault(); 30 | 31 | public override void TooltipHeaderText() 32 | { 33 | MulticolorText((Plain, NameChain), (Disabled, $" [{ID}]")); 34 | ImGui.TextDisabled(string.Join(", ", FlagNames.Where(f => Flags.HasFlag(f.Key)).Select(static f => f.Value))); 35 | } 36 | 37 | public override void DrawTooltipIcon(Vector2 startPos) 38 | { 39 | var texture = GetAdjustedAction().GetIconTexture(); 40 | 41 | var frameTex = FrameTex; 42 | if (texture != null && frameTex != null) 43 | { 44 | ImGui.Image(texture.ImGuiHandle, new(40 * GlobalScale)); 45 | ImGui.SetCursorPos(startPos - (new Vector2(4, 3) * GlobalScale)); 46 | 47 | ImGui.Image(frameTex.ImGuiHandle, new(48 * GlobalScale)); 48 | ImGui.SameLine(); 49 | } 50 | 51 | ImGui.SetCursorPos(new(startPos.X + (50 * GlobalScale), 52 | startPos.Y + (2 * GlobalScale))); 53 | } 54 | 55 | public override bool UseCounterAsState() => !HasFlag(HasCharges); 56 | 57 | public override void PrintBarTimerDesc() 58 | { 59 | var barType = ((LongCooldown & Flags) != 0) switch 60 | { 61 | false when (Flags & RequiresStatus) != 0 && ReadyStatus != null => StatusTimer, 62 | false when (Flags & ComboBonus) != 0 => ComboTimer, 63 | _ => Cooldown 64 | }; 65 | 66 | if (barType == StatusTimer) 67 | { 68 | ImGui.TextColored(Plain, "Shows"); 69 | ImGui.SameLine(0,3); 70 | if (ReadyStatus?.Icon != null) 71 | { 72 | DrawGameIcon(ReadyStatus.Icon.Value, ImGui.GetFontSize() / GlobalScale); 73 | ImGui.SameLine(0,3); 74 | } 75 | ImGui.TextColored(Yellow, ReadyStatus?.Name ?? "?"); 76 | ImGui.SameLine(0,3); 77 | ImGui.TextColored(Plain, "timer"); 78 | } 79 | else if (barType == ComboTimer) ImGui.Text("Shows combo time remaining for this action"); 80 | else ImGui.Text($"Shows recast time remaining ({LastKnownCooldown}s)"); 81 | } 82 | 83 | public override void PrintCounterDesc() => ImGui.Text($"Shows Charges ({GetMaxCharges()})"); 84 | 85 | public override void PrintStateDesc() => ImGui.Text("Shows if ready"); 86 | 87 | public override void FooterContents() 88 | { 89 | var readyStatus = ReadyStatus?.Name ?? "?"; 90 | ImGui.TextDisabled("Ready Conditions"); 91 | 92 | if (HasFlag(TransformedButton)) MulticolorText((Plain, "•"), (Orange, GetBaseAction().Name), (Plain, "has changed to this action")); 93 | if (HasFlag(RequiresStatus) && readyStatus.Length > 1) 94 | { 95 | 96 | ImGui.TextColored(Plain, "•"); 97 | ImGui.SameLine(0,3); 98 | if (ReadyStatus?.Icon != null) 99 | { 100 | DrawGameIcon(ReadyStatus.Icon.Value, ImGui.GetFontSize() / GlobalScale); 101 | ImGui.SameLine(0,3); 102 | } 103 | MulticolorText((Yellow, readyStatus), (Plain, "is active")); 104 | } 105 | 106 | if (HasFlag(ComboBonus)) ImGui.Text("• This action is the next step in an active combo"); 107 | else if (HasFlag(CanGetAnts)) ImGui.Text("• This action is highlighted"); 108 | 109 | if (HasFlag(HasCharges)) ImGui.Text("• At least one charge is available"); 110 | else if (HasFlag(LongCooldown)) ImGui.Text("• This action is off cooldown"); 111 | 112 | if (HasFlag(CostsMP)) ImGui.Text($"• Enough MP is available (Current Cost: {GetActionCost()})"); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /GaugeOMatic/Trackers/Tracker.cs: -------------------------------------------------------------------------------- 1 | using GaugeOMatic.Config; 2 | using GaugeOMatic.GameData; 3 | using GaugeOMatic.JobModules; 4 | using GaugeOMatic.Widgets; 5 | using GaugeOMatic.Windows; 6 | using GaugeOMatic.Windows.Dropdowns; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using static GaugeOMatic.GaugeOMatic; 11 | using static System.Activator; 12 | 13 | namespace GaugeOMatic.Trackers; 14 | 15 | public abstract partial class Tracker : IDisposable 16 | { 17 | public TrackerConfig TrackerConfig { get; set; } = null!; 18 | 19 | public Widget? Widget { get; set; } 20 | public bool Available; 21 | 22 | public bool UsePreviewValue => TrackerConfig.Preview && (GaugeOMatic.ConfigWindow.IsOpen || Window?.IsOpen == true); 23 | 24 | public JobModule JobModule = null!; 25 | public WidgetDropdown WidgetMenuTable = null!; 26 | public WidgetDropdown WidgetMenuWindow = null!; 27 | public AddonDropdown AddonDropdown = null!; 28 | public TrackerDropdown TrackerDropdown = null!; 29 | public ItemRef? ItemRef; 30 | 31 | public string AddonName 32 | { 33 | get => TrackerConfig.AddonName; 34 | set => TrackerConfig.AddonName = value; 35 | } 36 | 37 | public string? WidgetType 38 | { 39 | get => TrackerConfig.WidgetType; 40 | set => TrackerConfig.WidgetType = value; 41 | } 42 | 43 | public WidgetConfig WidgetConfig 44 | { 45 | get => TrackerConfig.WidgetConfig; 46 | set => TrackerConfig.WidgetConfig = value; 47 | } 48 | 49 | public TrackerWindow? Window { get; set; } 50 | 51 | public void Dispose() 52 | { 53 | if (WindowSystem.Windows.Contains(Window)) WindowSystem.RemoveWindow(Window!); 54 | Window?.Dispose(); 55 | 56 | DisposeWidget(); 57 | } 58 | 59 | public void BuildWidget(Configuration configuration, List addonOptions) 60 | { 61 | if (TrackerConfig.Enabled == false) return; 62 | 63 | UpdateValues(); 64 | Widget = Widget.Create(this); 65 | 66 | if (Widget != null) 67 | { 68 | if (Window == null) CreateWindow(Widget, configuration); 69 | else 70 | { 71 | if (!WindowSystem.Windows.Contains(Window)) WindowSystem.AddWindow(Window); 72 | Window.Widget = Widget; 73 | } 74 | 75 | Available = true; 76 | } 77 | AddonDropdown.Prepare(addonOptions); 78 | } 79 | 80 | public void CreateWindow(Widget widget, Configuration configuration) 81 | { 82 | Window = new(this, widget, configuration, $"{DisplayAttr.Name}##{GetHashCode()}"); 83 | Window.Size = new(300, 600); 84 | WindowSystem.AddWindow(Window); 85 | } 86 | 87 | public void DisposeWidget() 88 | { 89 | Widget?.Dispose(); 90 | Available = false; 91 | Widget = null; 92 | } 93 | 94 | public void UpdateValues() 95 | { 96 | PreviousData = CurrentData; 97 | CurrentData = GetCurrentData(UsePreviewValue ? TrackerConfig.PreviewValue : null); 98 | } 99 | 100 | public void UpdateTracker() 101 | { 102 | UpdateValues(); 103 | Widget?.Update(); 104 | Widget?.UpdateIcon(); 105 | } 106 | 107 | public static Tracker? Create(JobModule jobModule, TrackerConfig trackerConfig) 108 | { 109 | var qualifiedTypeStr = $"{typeof(Tracker).Namespace}.{trackerConfig.TrackerType}"; 110 | var tracker = (Tracker?)CreateInstance(Type.GetType(qualifiedTypeStr) ?? typeof(EmptyTracker)); 111 | 112 | if (tracker == null) return null; 113 | 114 | tracker.JobModule = jobModule; 115 | 116 | if (trackerConfig.ItemId != 0) 117 | { 118 | tracker.ItemRef = trackerConfig.TrackerType switch 119 | { 120 | nameof(ActionTracker) => (ActionRef)trackerConfig.ItemId, 121 | nameof(StatusTracker) => (StatusRef)trackerConfig.ItemId, 122 | nameof(ParameterTracker) => (ParamRef)trackerConfig.ItemId, 123 | _ => null 124 | }; 125 | } 126 | 127 | tracker.TrackerConfig = trackerConfig; 128 | 129 | tracker.AddonDropdown = new(tracker); 130 | tracker.WidgetMenuTable = new(tracker); 131 | tracker.WidgetMenuWindow = new(tracker); 132 | tracker.TrackerDropdown = new(tracker); 133 | 134 | return tracker; 135 | } 136 | 137 | public void WriteWidgetConfig() 138 | { 139 | Widget?.ApplyConfigs(); 140 | 141 | typeof(WidgetConfig).GetProperties() 142 | .FirstOrDefault(prop => prop.PropertyType.Name == Widget?.Config.GetType().Name)? 143 | .SetValue(WidgetConfig, Widget?.Config); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /GaugeOMatic/GameData/ActionRef.DataGetters.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.Game; 2 | using System; 3 | using static FFXIVClientStructs.FFXIV.Client.Game.ActionManager; 4 | using static GaugeOMatic.GameData.ActionFlags; 5 | using static GaugeOMatic.GameData.ActionRef.BarType; 6 | using static GaugeOMatic.GameData.Overrides; 7 | using static GaugeOMatic.GameData.StatusRef.StatusActor; 8 | using static GaugeOMatic.Trackers.Tracker; 9 | 10 | namespace GaugeOMatic.GameData; 11 | 12 | public partial class ActionRef 13 | { 14 | public static unsafe ActionManager* ActionManager => Instance(); 15 | 16 | public int GetMaxCharges() => FFXIVClientStructs.FFXIV.Client.Game.ActionManager.GetMaxCharges(GetAdjustedId(), 0); 17 | public int GetActionCost() => FFXIVClientStructs.FFXIV.Client.Game.ActionManager.GetActionCost(ActionType.Action, ID, default, default, default, default); 18 | 19 | // ReSharper disable once UnusedMember.Global 20 | public int GetCurrentCharges() 21 | { 22 | var maxCharges = GetMaxCharges(); 23 | var elapsed = GetCooldownElapsed(); 24 | if (elapsed == 0) return maxCharges; 25 | 26 | var cooldownTotal = GetCooldownTotal(); 27 | 28 | return (int)Math.Floor(elapsed / cooldownTotal * maxCharges); 29 | } 30 | 31 | public unsafe float GetCooldownTotal() 32 | { 33 | var cooldownLength = CooldownOverrides.TryGetValue(ID, out var cd) ? 34 | cd.Invoke() : 35 | ActionManager->GetRecastTime(ActionType.Action, GetAdjustedId()); 36 | if (cooldownLength > 0) LastKnownCooldown = cooldownLength; 37 | return LastKnownCooldown; 38 | } 39 | 40 | public unsafe float GetCooldownElapsed() => ActionManager->GetRecastTimeElapsed(ActionType.Action, GetAdjustedId()); 41 | 42 | // ReSharper disable once UnusedMember.Global 43 | public float GetCooldownRemaining() 44 | { 45 | var elapsed = GetCooldownElapsed(); 46 | return elapsed == 0 ? 0 : GetCooldownTotal() - elapsed; 47 | } 48 | 49 | public unsafe bool HasAnts(bool adjusted = false) => ActionManager->IsActionHighlighted(ActionType.Action, adjusted ? GetAdjustedId() : ID); 50 | 51 | public BarType GetBarType() => HasFlag(RequiresStatus, exclude: LongCooldown) && ReadyStatus != null ? StatusTimer : 52 | HasFlag(ComboBonus, exclude: LongCooldown) ? ComboTimer : 53 | Cooldown; 54 | 55 | public enum BarType 56 | { 57 | Cooldown = 0, 58 | StatusTimer = 1, 59 | ComboTimer = 2 60 | } 61 | 62 | public override unsafe TrackerData GetTrackerData(float? preview) 63 | { 64 | var barType = GetBarType(); 65 | 66 | var cooldownTotal = GetCooldownTotal(); 67 | var elapsed = GetCooldownElapsed(); 68 | var cooldownRemaining = elapsed == 0 || elapsed >= cooldownTotal ? 0 : cooldownTotal - elapsed; 69 | 70 | int count; 71 | var maxCount = GetMaxCharges(); 72 | 73 | int state; 74 | 75 | var maxGauge = barType switch 76 | { 77 | StatusTimer => ReadyStatus!.MaxTime, 78 | ComboTimer => 30, 79 | _ => cooldownTotal 80 | }; 81 | 82 | var gaugeValue = preview == null ? barType switch 83 | { 84 | StatusTimer => Math.Abs(ReadyStatus!.TryGetStatus(out var status, Self) ? status?.RemainingTime ?? 0 : 0), 85 | ComboTimer => HasAnts(true) ? ActionManager->Combo.Timer : 0, 86 | _ => cooldownRemaining 87 | } : (preview.Value * maxGauge)!; 88 | 89 | if (preview != null) 90 | { 91 | state = (int)Math.Round(preview.Value); 92 | count = (int)(preview.Value * maxCount)!; 93 | } 94 | else 95 | { 96 | var charges = elapsed == 0 ? maxCount : (int)Math.Floor(elapsed / cooldownTotal * maxCount); 97 | 98 | var transformCheck = !HasFlag(TransformedButton) || GetBaseAction().GetAdjustedId() == ID; 99 | var chargeCheck = !HasFlag(HasCharges) || charges != 0; 100 | var cooldownCheck = !HasFlag(LongCooldown, exclude: HasCharges) || !(cooldownRemaining > 0); 101 | var statusCheck = !HasFlag(RequiresStatus) || (ReadyStatus?.TryGetStatus(Self) ?? false); 102 | var antCheck = !HasFlag(CanGetAnts | ComboBonus) || HasAnts(); 103 | var mpCheck = !HasFlag(CostsMP) || GetActionCost() < FrameworkData.LocalPlayer.CurMp; 104 | 105 | state = transformCheck && cooldownCheck && chargeCheck && statusCheck && antCheck && mpCheck ? 1 : 0; 106 | count = HasFlag(HasCharges) ? charges : state; 107 | } 108 | 109 | return new(count, maxCount, gaugeValue, maxGauge, state, 1, preview); 110 | } 111 | } 112 | 113 | --------------------------------------------------------------------------------