Initial release: HSUI v1.0.0.0 - HUD replacement with configurable hotbars
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,485 @@
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Textures;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using HSUI.Config;
|
||||
using HSUI.Config.Profiles;
|
||||
using HSUI.Helpers;
|
||||
using HSUI.Interface;
|
||||
using HSUI.Interface.GeneralElements;
|
||||
using HSUI.Interface.Nameplates;
|
||||
using HSUI.Interface.Party;
|
||||
using HSUI.Interface.PartyCooldowns;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using KamiToolKit;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace HSUI
|
||||
{
|
||||
public class Plugin : IDalamudPlugin
|
||||
{
|
||||
public static IBuddyList BuddyList { get; private set; } = null!;
|
||||
public static IClientState ClientState { get; private set; } = null!;
|
||||
public static ICommandManager CommandManager { get; private set; } = null!;
|
||||
public static ICondition Condition { get; private set; } = null!;
|
||||
public static IDalamudPluginInterface PluginInterface { get; private set; } = null!;
|
||||
public static IDataManager DataManager { get; private set; } = null!;
|
||||
public static IFramework Framework { get; private set; } = null!;
|
||||
public static IGameGui GameGui { get; private set; } = null!;
|
||||
public static IJobGauges JobGauges { get; private set; } = null!;
|
||||
public static IObjectTable ObjectTable { get; private set; } = null!;
|
||||
public static ISigScanner SigScanner { get; private set; } = null!;
|
||||
public static IGameInteropProvider GameInteropProvider { get; private set; } = null!;
|
||||
public static ITargetManager TargetManager { get; private set; } = null!;
|
||||
public static IUiBuilder UiBuilder { get; private set; } = null!;
|
||||
public static IPartyList PartyList { get; private set; } = null!;
|
||||
public static IPluginLog Logger { get; private set; } = null!;
|
||||
public static ITextureProvider TextureProvider { get; private set; } = null!;
|
||||
public static IAddonLifecycle AddonLifecycle { get; private set; } = null!;
|
||||
public static IChatGui Chat { get; private set; } = null!;
|
||||
public static ISeStringEvaluator SeStringEvaluator { get; private set; } = null!;
|
||||
|
||||
public static ISharedImmediateTexture? BannerTexture;
|
||||
|
||||
public static string AssemblyLocation { get; private set; } = "";
|
||||
public string Name => "HSUI";
|
||||
|
||||
public static string Version { get; private set; } = "";
|
||||
|
||||
private HudManager _hudManager = null!;
|
||||
|
||||
public delegate void JobChangedEventHandler(uint jobId);
|
||||
public static event JobChangedEventHandler? JobChangedEvent;
|
||||
private uint _jobId = 0;
|
||||
|
||||
public static double LoadTime = -1;
|
||||
|
||||
public Plugin(
|
||||
IBuddyList buddyList,
|
||||
IClientState clientState,
|
||||
ICommandManager commandManager,
|
||||
ICondition condition,
|
||||
IDalamudPluginInterface pluginInterface,
|
||||
IDataManager dataManager,
|
||||
IFramework framework,
|
||||
IGameGui gameGui,
|
||||
IJobGauges jobGauges,
|
||||
IObjectTable objectTable,
|
||||
IPartyList partyList,
|
||||
ISigScanner sigScanner,
|
||||
IGameInteropProvider gameInteropProvider,
|
||||
ITargetManager targetManager,
|
||||
IPluginLog logger,
|
||||
ITextureProvider textureProvider,
|
||||
IAddonLifecycle addonLifecycle,
|
||||
IChatGui chat,
|
||||
ISeStringEvaluator seStringEvaluator)
|
||||
{
|
||||
BuddyList = buddyList;
|
||||
ClientState = clientState;
|
||||
CommandManager = commandManager;
|
||||
Condition = condition;
|
||||
PluginInterface = pluginInterface;
|
||||
DataManager = dataManager;
|
||||
Framework = framework;
|
||||
GameGui = gameGui;
|
||||
JobGauges = jobGauges;
|
||||
ObjectTable = objectTable;
|
||||
PartyList = partyList;
|
||||
SigScanner = sigScanner;
|
||||
GameInteropProvider = gameInteropProvider;
|
||||
TargetManager = targetManager;
|
||||
UiBuilder = PluginInterface.UiBuilder;
|
||||
Logger = logger;
|
||||
TextureProvider = textureProvider;
|
||||
AddonLifecycle = addonLifecycle;
|
||||
Chat = chat;
|
||||
SeStringEvaluator = seStringEvaluator;
|
||||
|
||||
if (pluginInterface.AssemblyLocation.DirectoryName != null)
|
||||
{
|
||||
AssemblyLocation = pluginInterface.AssemblyLocation.DirectoryName + "\\";
|
||||
}
|
||||
else
|
||||
{
|
||||
AssemblyLocation = Assembly.GetExecutingAssembly().Location;
|
||||
}
|
||||
|
||||
Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "1.0.0.0";
|
||||
|
||||
KamiToolKitLibrary.Initialize(pluginInterface);
|
||||
|
||||
FontsManager.Initialize(AssemblyLocation);
|
||||
BarTexturesManager.Initialize(AssemblyLocation);
|
||||
LoadBanner();
|
||||
|
||||
ConfigurationManager.Initialize();
|
||||
ProfilesManager.Initialize();
|
||||
ConfigurationManager.Instance.LoadOrInitializeFiles();
|
||||
|
||||
FontsManager.Instance.LoadConfig();
|
||||
BarTexturesManager.Instance.LoadConfig();
|
||||
|
||||
ClipRectsHelper.Initialize();
|
||||
GlobalColors.Initialize();
|
||||
InputsHelper.Initialize();
|
||||
NameplatesManager.Initialize();
|
||||
PartyManager.Initialize();
|
||||
PartyCooldownsManager.Initialize();
|
||||
PullTimerHelper.Initialize();
|
||||
ActionBarsManager.Initialize();
|
||||
TextTagsHelper.Initialize();
|
||||
TooltipsHelper.Initialize();
|
||||
PetRenamerHelper.Initialize();
|
||||
HonorificHelper.Initialize();
|
||||
WotsitHelper.Initialize();
|
||||
WhosTalkingHelper.Initialize();
|
||||
|
||||
_hudManager = new HudManager();
|
||||
|
||||
UiBuilder.Draw += Draw;
|
||||
UiBuilder.OpenConfigUi += OpenConfigUi;
|
||||
|
||||
FontsManager.Instance.BuildFonts();
|
||||
|
||||
CommandManager.AddHandler(
|
||||
"/hsui",
|
||||
new CommandInfo(PluginCommand)
|
||||
{
|
||||
HelpMessage = "Opens the HSUI configuration window.\n"
|
||||
+ "/hsui toggle → Toggles HUD visibility.\n"
|
||||
+ "/hsui show → Shows HUD.\n"
|
||||
+ "/hsui hide → Hides HUD.\n"
|
||||
+ "/hsui toggledefaulthud → Toggles the game's Job Gauges visibility.\n"
|
||||
+ "/hsui forcejob <JOB> → Forces HSUI to show the HUD for the given Job short name.\n"
|
||||
+ "/hsui profile <PROFILE> → Switch to the given profile.\n"
|
||||
+ "/hsui mouse <on/off> → Toggles special input handling for extra mouse buttons when hovering HSUI elements.\n"
|
||||
+ "/hsui debug dragdrop → Toggles debug logging for action bar drag & drop (all hotbars). Logs SwapSlots before/after, release-outside, item payload resolution.\n"
|
||||
+ "/hsui debug tooltips → Toggles debug logging for tooltips.\n"
|
||||
+ "/hsui debug hud → Dumps HudLayout addon names and hashes to the log (for HUD hiding).\n"
|
||||
+ "/hsui debug hotbarslots → Dumps all hotbar slot CommandType/CommandId to the log (for SwapSlots diagnosis).\n"
|
||||
+ "/hsui debug macro <bar> <slot> → Dumps HotbarSlot + RaptureMacroModule.Macro memory for the slot (macro persistence).",
|
||||
ShowInHelp = true
|
||||
}
|
||||
);
|
||||
|
||||
CommandManager.AddHandler(
|
||||
"/hui",
|
||||
new CommandInfo(PluginCommand)
|
||||
{
|
||||
HelpMessage = "Opens the HSUI configuration window.\n"
|
||||
+ "/hui toggle → Toggles HUD visibility.\n"
|
||||
+ "/hui show → Shows HUD.\n"
|
||||
+ "/hui hide → Hides HUD.\n"
|
||||
+ "/hui toggledefaulthud → Toggles the game's Job Gauges visibility.\n"
|
||||
+ "/hui forcejob <JOB> → Forces HSUI to show the HUD for the given Job short name.\n"
|
||||
+ "/hui profile <PROFILE> → Switch to the given profile.\n"
|
||||
+ "/hui mouse <on/off> → Toggles special input handling for extra mouse buttons when hovering HSUI elements.",
|
||||
ShowInHelp = true
|
||||
}
|
||||
);
|
||||
|
||||
WotsitHelper.Instance?.Update();
|
||||
|
||||
if (ConfigurationManager.Instance?.IsChangelogWindowOpened == false)
|
||||
{
|
||||
LoadTime = ImGui.GetTime();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Logger.Info("Starting HSUI Dispose v" + Version);
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void LoadBanner()
|
||||
{
|
||||
string bannerImage = Path.Combine(Path.GetDirectoryName(AssemblyLocation.TrimEnd('\\')) ?? "", "Media", "Images", "banner_short_x150.png");
|
||||
|
||||
if (File.Exists(bannerImage))
|
||||
{
|
||||
try
|
||||
{
|
||||
BannerTexture = TextureProvider.GetFromFile(bannerImage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Image failed to load. {bannerImage}\n\n{ex}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug($"Image doesn't exist. {bannerImage}");
|
||||
}
|
||||
}
|
||||
|
||||
private void PluginCommand(string command, string arguments)
|
||||
{
|
||||
var configManager = ConfigurationManager.Instance;
|
||||
|
||||
if (configManager.IsConfigWindowOpened && !configManager.LockHUD)
|
||||
{
|
||||
configManager.LockHUD = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool printHUDStatus = false;
|
||||
|
||||
switch (arguments)
|
||||
{
|
||||
case "toggle":
|
||||
ConfigurationManager.Instance.ShowHUD = !ConfigurationManager.Instance.ShowHUD;
|
||||
printHUDStatus = true;
|
||||
break;
|
||||
|
||||
case "toggledefaulthud":
|
||||
HUDOptionsConfig config = ConfigurationManager.Instance.GetConfigObject<HUDOptionsConfig>();
|
||||
config.HideDefaultJobGauges = !config.HideDefaultJobGauges;
|
||||
string defaultJobGaugeStr = config.HideDefaultJobGauges ? "hidden" : "visible";
|
||||
Chat.Print($"Default Job Gauges are {defaultJobGaugeStr}.");
|
||||
break;
|
||||
|
||||
case "show":
|
||||
ConfigurationManager.Instance.ShowHUD = true;
|
||||
printHUDStatus = true;
|
||||
break;
|
||||
|
||||
case "hide":
|
||||
ConfigurationManager.Instance.ShowHUD = false;
|
||||
printHUDStatus = true;
|
||||
break;
|
||||
|
||||
case { } argument when argument.StartsWith("mouse"):
|
||||
string[] mouseArgs = argument.Split(" ");
|
||||
if (mouseArgs.Length > 1)
|
||||
{
|
||||
if (mouseArgs[1] == "on")
|
||||
InputsHelper.Instance?.ToggleProxy(true);
|
||||
else if (mouseArgs[1] == "off")
|
||||
InputsHelper.Instance?.ToggleProxy(false);
|
||||
}
|
||||
string mouseStr = InputsHelper.Instance?.IsProxyEnabled == true ? "enabled" : "disabled";
|
||||
Chat.Print($"HSUI special mouse handling is currently {mouseStr}.");
|
||||
break;
|
||||
|
||||
case { } argument when argument.StartsWith("forcejob"):
|
||||
string[] args = argument.Split(" ");
|
||||
if (args.Length > 1)
|
||||
{
|
||||
if (args[1].Equals("off", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ForcedJob.Enabled = false;
|
||||
return;
|
||||
}
|
||||
var job = typeof(JobIDs).GetField(args[1].ToUpperInvariant());
|
||||
if (job != null)
|
||||
{
|
||||
ForcedJob.Enabled = true;
|
||||
ForcedJob.ForcedJobId = (uint)(job.GetValue(null) ?? JobIDs.ACN);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case { } argument when argument.StartsWith("profile"):
|
||||
string[] profile = argument.Split(" ", 2);
|
||||
if (profile.Length > 1)
|
||||
{
|
||||
ProfilesManager.Instance?.CheckUpdateSwitchCurrentProfile(profile[1]);
|
||||
}
|
||||
break;
|
||||
|
||||
case "debug dragdrop":
|
||||
case "debug drag":
|
||||
var hotbarConfigs = configManager.GetObjects<HSUI.Interface.GeneralElements.HotbarBarConfig>();
|
||||
bool newState = hotbarConfigs.Count == 0 || !hotbarConfigs.Exists(c => c.DebugDragDrop);
|
||||
foreach (var bar in hotbarConfigs)
|
||||
bar.DebugDragDrop = newState;
|
||||
configManager.SaveConfigurations();
|
||||
Chat.Print($"HSUI drag-drop debug logging is {(newState ? "ON" : "OFF")} for all hotbars.");
|
||||
break;
|
||||
|
||||
case "debug tooltips":
|
||||
var tooltipsConfig = configManager.GetConfigObject<HSUI.Helpers.TooltipsConfig>();
|
||||
tooltipsConfig.DebugTooltips = !tooltipsConfig.DebugTooltips;
|
||||
configManager.SaveConfigurations();
|
||||
Chat.Print($"HSUI tooltip debug logging is {(tooltipsConfig.DebugTooltips ? "ON" : "OFF")}.");
|
||||
break;
|
||||
|
||||
case "debug hud":
|
||||
HSUI.Helpers.HudLayoutHashHelper.DumpHudLayoutAddonsToLog();
|
||||
Chat.Print("HSUI: HudLayout addon names and hashes dumped to the log (Dalamud log window or dev plugin).");
|
||||
break;
|
||||
|
||||
case "debug hotbarslots":
|
||||
HSUI.Helpers.ActionBarsManager.Instance?.DumpSlotStateToLog();
|
||||
Chat.Print("HSUI: Hotbar slot state dumped to the log.");
|
||||
break;
|
||||
|
||||
case "debug macromenu":
|
||||
HSUI.Helpers.ActionBarsManager.Instance?.DumpMacroMenuToLog();
|
||||
Chat.Print("HSUI: Macro menu state dumped to the log (open Macro menu first for best data).");
|
||||
break;
|
||||
|
||||
case { } arg when arg.StartsWith("debug macro"):
|
||||
var macroParts = arg.Split(" ", StringSplitOptions.RemoveEmptyEntries);
|
||||
if (macroParts.Length >= 4 && int.TryParse(macroParts[2], out int macroBar) && int.TryParse(macroParts[3], out int macroSlot))
|
||||
{
|
||||
HSUI.Helpers.ActionBarsManager.Instance?.DumpMacroSlotMemoryToLog(macroBar, macroSlot);
|
||||
Chat.Print($"HSUI: Macro slot memory for bar {macroBar} slot {macroSlot} dumped to the log.");
|
||||
}
|
||||
else
|
||||
Chat.Print("Usage: /hsui debug macro <bar> <slot> (e.g. /hsui debug macro 1 2)");
|
||||
break;
|
||||
|
||||
default:
|
||||
configManager.ToggleConfigWindow();
|
||||
break;
|
||||
}
|
||||
|
||||
if (printHUDStatus)
|
||||
{
|
||||
string hudStr = ConfigurationManager.Instance.ShowHUD ? "visible" : "hidden";
|
||||
Chat.Print($"HSUI HUD is {hudStr}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateJob()
|
||||
{
|
||||
var player = ObjectTable.LocalPlayer;
|
||||
if (player is null) return;
|
||||
|
||||
uint newJobId = player.ClassJob.RowId;
|
||||
if (ForcedJob.Enabled)
|
||||
newJobId = ForcedJob.ForcedJobId;
|
||||
|
||||
if (_jobId != newJobId)
|
||||
{
|
||||
_jobId = newJobId;
|
||||
JobChangedEvent?.Invoke(_jobId);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool _drawInProgress;
|
||||
|
||||
private void Draw()
|
||||
{
|
||||
if (_drawInProgress) return;
|
||||
_drawInProgress = true;
|
||||
try
|
||||
{
|
||||
UpdateJob();
|
||||
|
||||
UiBuilder.OverrideGameCursor = false;
|
||||
|
||||
ConfigurationManager.Instance.Draw();
|
||||
try { NameplatesManager.Instance?.Update(); }
|
||||
catch (Exception ex) { Logger.Warning($"NameplatesManager.Update: {ex.Message}"); }
|
||||
try { PartyManager.Instance?.Update(); }
|
||||
catch (Exception ex) { Logger.Warning($"PartyManager.Update: {ex.Message}"); }
|
||||
|
||||
try
|
||||
{
|
||||
using (FontsManager.Instance.PushDefaultFont())
|
||||
{
|
||||
_hudManager?.Draw(_jobId);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("Something went wrong!:\n" + e.StackTrace);
|
||||
}
|
||||
|
||||
InputsHelper.Instance.OnFrameEnd();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_drawInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenConfigUi()
|
||||
{
|
||||
ConfigurationManager.Instance.ToggleConfigWindow();
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposing) return;
|
||||
|
||||
// Stop UI callbacks first so no Draw/OpenConfigUi runs during teardown
|
||||
try
|
||||
{
|
||||
Logger.Info("\tRemoving commands...");
|
||||
CommandManager.RemoveHandler("/hsui");
|
||||
CommandManager.RemoveHandler("/hui");
|
||||
}
|
||||
catch (Exception e) { Logger.Error("Error removing commands: " + e.Message); }
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Info("\tUnsubscribing from UIBuilder events...");
|
||||
UiBuilder.Draw -= Draw;
|
||||
UiBuilder.OpenConfigUi -= OpenConfigUi;
|
||||
}
|
||||
catch (Exception e) { Logger.Error("Error unsubscribing UIBuilder: " + e.Message); }
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Info("\tSaving configurations...");
|
||||
ConfigurationManager.Instance?.SaveConfigurations(true);
|
||||
ConfigurationManager.Instance?.CloseConfigWindow();
|
||||
}
|
||||
catch (Exception e) { Logger.Error("Error saving/closing config: " + e.Message); }
|
||||
|
||||
TryDispose("InputsHelper", () => InputsHelper.Instance?.Dispose());
|
||||
TryDispose("HudManager", () => _hudManager?.Dispose());
|
||||
TryDispose("BarTexturesManager", () => BarTexturesManager.Instance?.Dispose());
|
||||
TryDispose("ClipRectsHelper", () => ClipRectsHelper.Instance?.Dispose());
|
||||
TryDispose("ExperienceHelper", () => ExperienceHelper.Instance?.Dispose());
|
||||
TryDispose("FontsManager", () => FontsManager.Instance?.Dispose());
|
||||
TryDispose("GlobalColors", () => GlobalColors.Instance?.Dispose());
|
||||
TryDispose("NameplatesManager", () => NameplatesManager.Instance?.Dispose());
|
||||
TryDispose("PartyCooldownsManager", () => PartyCooldownsManager.Instance?.Dispose());
|
||||
TryDispose("PartyManager", () => PartyManager.Instance?.Dispose());
|
||||
TryDispose("PullTimerHelper", () => PullTimerHelper.Instance?.Dispose());
|
||||
TryDispose("ActionBarsManager", () => ActionBarsManager.Instance?.Dispose());
|
||||
TryDispose("ProfilesManager", () => ProfilesManager.Instance?.Dispose());
|
||||
TryDispose("SpellHelper", () => SpellHelper.Instance?.Dispose());
|
||||
TryDispose("TooltipsHelper", () => TooltipsHelper.Instance?.Dispose());
|
||||
TryDispose("HonorificHelper", () => HonorificHelper.Instance?.Dispose());
|
||||
TryDispose("PetRenamerHelper", () => PetRenamerHelper.Instance?.Dispose());
|
||||
TryDispose("WotsitHelper", () => WotsitHelper.Instance?.Dispose());
|
||||
TryDispose("WhosTalkingHelper", () => WhosTalkingHelper.Instance?.Dispose());
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Info("\tRebuilding fonts...");
|
||||
UiBuilder.FontAtlas.BuildFontsAsync();
|
||||
}
|
||||
catch (Exception e) { Logger.Error("Error rebuilding fonts: " + e.Message); }
|
||||
|
||||
try { KamiToolKitLibrary.Dispose(); }
|
||||
catch (Exception e) { Logger.Error("Error disposing KamiToolKit: " + e.Message); }
|
||||
|
||||
TryDispose("ConfigurationManager", () => ConfigurationManager.Instance?.Dispose());
|
||||
}
|
||||
|
||||
private static void TryDispose(string name, Action dispose)
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.Info("\tDisposing " + name + "...");
|
||||
dispose();
|
||||
}
|
||||
catch (Exception e) { Logger.Error("Error disposing " + name + ": " + e.Message); }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user