f37369cdda
Co-authored-by: Cursor <cursoragent@cursor.com>
486 lines
21 KiB
C#
486 lines
21 KiB
C#
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); }
|
|
}
|
|
}
|
|
}
|