7c46a50b7e
Made-with: Cursor
668 lines
27 KiB
C#
668 lines
27 KiB
C#
using Dalamud.Game.Addon.Lifecycle;
|
|
using Dalamud.Game.ClientState.Conditions;
|
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
|
using HSUI.Config;
|
|
using HSUI.Enums;
|
|
using HSUI.Helpers;
|
|
using HSUI.Interface.EnemyList;
|
|
using HSUI.Interface.GeneralElements;
|
|
using HSUI.Interface.Party;
|
|
using HSUI.Interface.Jobs;
|
|
using HSUI.Interface.Nameplates;
|
|
using HSUI.Interface.StatusEffects;
|
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Numerics;
|
|
using Dalamud.Utility.Signatures;
|
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
|
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
|
|
|
namespace HSUI.Interface
|
|
{
|
|
public class HudHelper : IDisposable
|
|
{
|
|
private HUDOptionsConfig Config => ConfigurationManager.Instance.GetConfigObject<HUDOptionsConfig>();
|
|
|
|
private bool _firstUpdate = true;
|
|
|
|
private Vector2 _castBarPos = Vector2.Zero;
|
|
private bool _hidingCastBar = false;
|
|
private Vector2 _pullTimerPos = Vector2.Zero;
|
|
private bool _hidingPullTimer = false;
|
|
private bool _hidingDutyListOffScreen = false;
|
|
private bool _toDoListVisibleBeforeHide = true;
|
|
|
|
public HudHelper()
|
|
{
|
|
Config.ValueChangeEvent += ConfigValueChanged;
|
|
}
|
|
|
|
~HudHelper()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
protected void Dispose(bool disposing)
|
|
{
|
|
if (!disposing)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Config.ValueChangeEvent -= ConfigValueChanged;
|
|
|
|
void RestoreDefaults()
|
|
{
|
|
try
|
|
{
|
|
SetGameHudElementsHidden(Array.Empty<uint>(), true);
|
|
UpdateDefaultCastBar(true);
|
|
UpdateDefaultPulltimer(true);
|
|
UpdateDefaultDutyList(true);
|
|
UpdateDefaultNameplates(true);
|
|
UpdateJobGauges(true);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Plugin.Logger.Error($"Exception during HudHelper.Dispose restore: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
if (Plugin.Framework.IsInFrameworkUpdateThread && Plugin.ObjectTable.LocalPlayer != null)
|
|
{
|
|
RestoreDefaults();
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
Plugin.Framework.RunOnFrameworkThread(() =>
|
|
{
|
|
if (Plugin.ObjectTable.LocalPlayer != null)
|
|
RestoreDefaults();
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Plugin.Logger.Error($"Exception scheduling HudHelper.Dispose restore: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
try
|
|
{
|
|
if (_firstUpdate)
|
|
{
|
|
_firstUpdate = false;
|
|
UpdateDefaultCastBar();
|
|
UpdateDefaultPulltimer();
|
|
}
|
|
|
|
UpdateJobGauges();
|
|
UpdateDefaultHudElementsHidden();
|
|
UpdateDefaultCastBar();
|
|
UpdateDefaultDutyList();
|
|
UpdateDefaultNameplates();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
if ((now - _lastHudElementsErrorLog).TotalSeconds >= HudElementsErrorLogIntervalSeconds)
|
|
{
|
|
_lastHudElementsErrorLog = now;
|
|
Plugin.Logger.Warning($"[HSUI] HudHelper.Update: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsElementHidden(HudElement element)
|
|
{
|
|
// When this element's context menu is open, ignore visibility so the element stays visible
|
|
if (ContextMenuVisibilityHelper.IsContextMenuOpenFor(element))
|
|
return false;
|
|
|
|
IHudElementWithVisibilityConfig? e = element as IHudElementWithVisibilityConfig;
|
|
if (e == null || e.VisibilityConfig == null) { return false; }
|
|
|
|
return !e.VisibilityConfig.IsElementVisible(element);
|
|
}
|
|
|
|
private void ConfigValueChanged(object sender, OnChangeBaseArgs e)
|
|
{
|
|
switch (e.PropertyName)
|
|
{
|
|
case "HideDefaultHudWhenReplaced":
|
|
UpdateDefaultHudElementsHidden();
|
|
UpdateDefaultCastBar();
|
|
break;
|
|
case "HideDefaultCastbar":
|
|
UpdateDefaultCastBar();
|
|
break;
|
|
case "HideDefaultPulltimer":
|
|
UpdateDefaultPulltimer();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static DateTime _lastHudElementsErrorLog = DateTime.MinValue;
|
|
private const double HudElementsErrorLogIntervalSeconds = 10.0;
|
|
|
|
private void UpdateDefaultHudElementsHidden()
|
|
{
|
|
try
|
|
{
|
|
UpdateDefaultHudElementsHiddenCore();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
if ((now - _lastHudElementsErrorLog).TotalSeconds >= HudElementsErrorLogIntervalSeconds)
|
|
{
|
|
_lastHudElementsErrorLog = now;
|
|
Plugin.Logger.Warning($"[HSUI] HudHelper: skipped HUD hide update (resolver not ready): {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateDefaultHudElementsHiddenCore()
|
|
{
|
|
if (Plugin.Condition.Any(
|
|
ConditionFlag.OccupiedInEvent,
|
|
ConditionFlag.OccupiedInQuestEvent,
|
|
ConditionFlag.OccupiedInCutSceneEvent,
|
|
ConditionFlag.OccupiedSummoningBell,
|
|
ConditionFlag.Occupied,
|
|
ConditionFlag.Occupied30,
|
|
ConditionFlag.Occupied33,
|
|
ConditionFlag.Occupied38,
|
|
ConditionFlag.Occupied39,
|
|
ConditionFlag.WatchingCutscene,
|
|
ConditionFlag.WatchingCutscene78,
|
|
ConditionFlag.CreatingCharacter,
|
|
ConditionFlag.BetweenAreas,
|
|
ConditionFlag.BetweenAreas51,
|
|
ConditionFlag.BoundByDuty95,
|
|
ConditionFlag.ChocoboRacing,
|
|
ConditionFlag.PlayingLordOfVerminion)
|
|
|| Plugin.ClientState.IsPvP)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!Config.HideDefaultHudWhenReplaced)
|
|
{
|
|
SetGameHudElementsHidden(Array.Empty<uint>(), false);
|
|
return;
|
|
}
|
|
|
|
var hashesToHide = new List<uint>();
|
|
void AddElements(params ElementKind[] elements)
|
|
{
|
|
foreach (var e in elements)
|
|
hashesToHide.Add((uint)e);
|
|
}
|
|
|
|
// Use ElementKind hashes (HUD Layout config) — the proper way per DelvUI. Source: HUDManager ElementKind.cs
|
|
var hotbarsConfig = ConfigurationManager.Instance?.GetConfigObject<HotbarsConfig>();
|
|
if (hotbarsConfig?.Enabled == true && AnyHotbarEnabled())
|
|
{
|
|
AddElements(ElementKind.Hotbar1, ElementKind.Hotbar2, ElementKind.Hotbar3, ElementKind.Hotbar4, ElementKind.Hotbar5,
|
|
ElementKind.Hotbar6, ElementKind.Hotbar7, ElementKind.Hotbar8, ElementKind.Hotbar9, ElementKind.Hotbar10, ElementKind.CrossHotbar);
|
|
}
|
|
|
|
var playerUnitFrame = ConfigurationManager.Instance?.GetConfigObject<PlayerUnitFrameConfig>();
|
|
if (playerUnitFrame?.Enabled == true)
|
|
AddElements(ElementKind.ParameterBar);
|
|
|
|
var targetUnitFrame = ConfigurationManager.Instance?.GetConfigObject<TargetUnitFrameConfig>();
|
|
if (targetUnitFrame?.Enabled == true)
|
|
AddElements(ElementKind.TargetBar, ElementKind.TargetInfoHp, ElementKind.TargetInfoProgressBar, ElementKind.TargetInfoStatus);
|
|
|
|
var focusUnitFrame = ConfigurationManager.Instance?.GetConfigObject<FocusTargetUnitFrameConfig>();
|
|
if (focusUnitFrame?.Enabled == true)
|
|
AddElements(ElementKind.FocusTargetBar);
|
|
|
|
var playerCastbar = ConfigurationManager.Instance?.GetConfigObject<PlayerCastbarConfig>();
|
|
if (playerCastbar?.Enabled == true)
|
|
AddElements(ElementKind.ProgressBar);
|
|
|
|
var expBar = ConfigurationManager.Instance?.GetConfigObject<ExperienceBarConfig>();
|
|
if (expBar?.Enabled == true)
|
|
AddElements(ElementKind.ExperienceBar);
|
|
|
|
var limitBreak = ConfigurationManager.Instance?.GetConfigObject<LimitBreakConfig>();
|
|
if (limitBreak?.Enabled == true)
|
|
AddElements(ElementKind.LimitGauge);
|
|
|
|
var playerBuffs = ConfigurationManager.Instance?.GetConfigObject<PlayerBuffsListConfig>();
|
|
var playerDebuffs = ConfigurationManager.Instance?.GetConfigObject<PlayerDebuffsListConfig>();
|
|
if (playerBuffs?.Enabled == true || playerDebuffs?.Enabled == true)
|
|
AddElements(ElementKind.StatusEffects, ElementKind.StatusInfoEnhancements, ElementKind.StatusInfoEnfeeblements, ElementKind.StatusInfoOther, ElementKind.StatusInfoConditionalEnhancements);
|
|
|
|
var partyFrames = ConfigurationManager.Instance?.GetConfigObject<PartyFramesConfig>();
|
|
if (partyFrames?.Enabled == true)
|
|
AddElements(ElementKind.PartyList);
|
|
|
|
var alliance1 = ConfigurationManager.Instance?.GetConfigObject<AllianceFrames1Config>();
|
|
var alliance2 = ConfigurationManager.Instance?.GetConfigObject<AllianceFrames2Config>();
|
|
if ((alliance1?.Enabled == true || alliance2?.Enabled == true))
|
|
AddElements(ElementKind.AllianceList1, ElementKind.AllianceList2);
|
|
|
|
var enemyList = ConfigurationManager.Instance?.GetConfigObject<EnemyListConfig>();
|
|
if (enemyList?.Enabled == true)
|
|
AddElements(ElementKind.EnemyList);
|
|
|
|
var dutyListScenario = ConfigurationManager.Instance?.GetConfigObject<DutyListScenarioConfig>();
|
|
if (dutyListScenario?.Enabled == true)
|
|
{
|
|
// Only hide Scenario Tree via layout. Keep ToDoList in layout so it stays created/updated; we move it off-screen in UpdateDefaultDutyList so we can read objective text.
|
|
AddElements(ElementKind.ScenarioTree);
|
|
}
|
|
|
|
var mainMenu = ConfigurationManager.Instance?.GetConfigObject<MainMenuConfig>();
|
|
// Disabled while troubleshooting: keep game's Main Menu visible so we can compare behavior.
|
|
// if (mainMenu?.Enabled == true)
|
|
// AddElements(ElementKind.MainMenu);
|
|
|
|
// NamePlate is not in HUD Layout config; use UpdateDefaultNameplates (IsVisible) for that
|
|
if (IsCurrentJobHudEnabled())
|
|
{
|
|
var player = Plugin.ObjectTable.LocalPlayer;
|
|
if (player != null)
|
|
{
|
|
foreach (var ek in ElementKindHelper.GetJobGaugeElementKinds(player.ClassJob.RowId))
|
|
hashesToHide.Add((uint)ek);
|
|
}
|
|
}
|
|
|
|
SetGameHudElementsHidden(hashesToHide.ToArray(), false);
|
|
}
|
|
|
|
private static bool AnyHotbarEnabled()
|
|
{
|
|
var cfg = ConfigurationManager.Instance;
|
|
if (cfg == null) return true;
|
|
var bars = new HotbarBarConfig?[]
|
|
{
|
|
cfg.GetConfigObject<Hotbar1BarConfig>(),
|
|
cfg.GetConfigObject<Hotbar2BarConfig>(),
|
|
cfg.GetConfigObject<Hotbar3BarConfig>(),
|
|
cfg.GetConfigObject<Hotbar4BarConfig>(),
|
|
cfg.GetConfigObject<Hotbar5BarConfig>(),
|
|
cfg.GetConfigObject<Hotbar6BarConfig>(),
|
|
cfg.GetConfigObject<Hotbar7BarConfig>(),
|
|
cfg.GetConfigObject<Hotbar8BarConfig>(),
|
|
cfg.GetConfigObject<Hotbar9BarConfig>(),
|
|
cfg.GetConfigObject<Hotbar10BarConfig>()
|
|
};
|
|
foreach (var bar in bars)
|
|
{
|
|
if (bar?.Enabled == true) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>Visibility (ByteValue2) of game HUD elements before we hid them. Restored on disable/unload.</summary>
|
|
private readonly Dictionary<uint, byte> _gameHudVisibilityBeforeHide = new();
|
|
/// <summary>When we force-show ToDoList for off-screen text reading, save its ByteValue2 here to restore on disable.</summary>
|
|
private readonly Dictionary<uint, byte> _gameHudForceShowBeforeRestore = new();
|
|
|
|
private unsafe void SetGameHudElementsHidden(uint[] hashesToHide, bool forceRestore)
|
|
{
|
|
AddonConfig* config = AddonConfig.Instance();
|
|
if (config == null || !config->IsLoaded || config->ActiveDataSet == null)
|
|
return;
|
|
|
|
var hashSet = new HashSet<uint>(hashesToHide);
|
|
Span<AddonConfigEntry> entries = config->ActiveDataSet->HudLayoutConfigEntries;
|
|
bool hasChanges = false;
|
|
|
|
if (forceRestore || hashesToHide.Length == 0)
|
|
{
|
|
foreach (ref var entry in entries)
|
|
{
|
|
if (!_gameHudVisibilityBeforeHide.TryGetValue(entry.AddonNameHash, out byte saved))
|
|
continue;
|
|
if (entry.ByteValue2 == saved)
|
|
continue;
|
|
entry.ByteValue2 = saved;
|
|
hasChanges = true;
|
|
}
|
|
_gameHudVisibilityBeforeHide.Clear();
|
|
|
|
foreach (ref var entry in entries)
|
|
{
|
|
if (!_gameHudForceShowBeforeRestore.TryGetValue(entry.AddonNameHash, out byte saved))
|
|
continue;
|
|
if (entry.ByteValue2 == saved)
|
|
continue;
|
|
entry.ByteValue2 = saved;
|
|
hasChanges = true;
|
|
}
|
|
_gameHudForceShowBeforeRestore.Clear();
|
|
}
|
|
else
|
|
{
|
|
foreach (ref var entry in entries)
|
|
{
|
|
if (!hashSet.Contains(entry.AddonNameHash))
|
|
continue;
|
|
if (!_gameHudVisibilityBeforeHide.ContainsKey(entry.AddonNameHash))
|
|
_gameHudVisibilityBeforeHide[entry.AddonNameHash] = entry.ByteValue2;
|
|
if (entry.ByteValue2 == 0x0)
|
|
continue;
|
|
entry.ByteValue2 = 0x0;
|
|
hasChanges = true;
|
|
}
|
|
|
|
// When Duty List/Scenario is enabled, force-show the Duty List (ToDoList) so the addon is created and we can move it off-screen for text reading
|
|
var dutyListScenario = ConfigurationManager.Instance?.GetConfigObject<DutyListScenarioConfig>();
|
|
if (dutyListScenario?.Enabled == true)
|
|
{
|
|
uint toDoListHash = (uint)ElementKind.ToDoList;
|
|
foreach (ref var entry in entries)
|
|
{
|
|
if (entry.AddonNameHash != toDoListHash)
|
|
continue;
|
|
if (!_gameHudForceShowBeforeRestore.ContainsKey(entry.AddonNameHash))
|
|
_gameHudForceShowBeforeRestore[entry.AddonNameHash] = entry.ByteValue2;
|
|
if (entry.ByteValue2 != 0x0)
|
|
continue;
|
|
entry.ByteValue2 = 0x1;
|
|
hasChanges = true;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint toDoListHash = (uint)ElementKind.ToDoList;
|
|
if (_gameHudForceShowBeforeRestore.TryGetValue(toDoListHash, out byte saved))
|
|
{
|
|
foreach (ref var entry in entries)
|
|
{
|
|
if (entry.AddonNameHash != toDoListHash)
|
|
continue;
|
|
if (entry.ByteValue2 != saved)
|
|
{
|
|
entry.ByteValue2 = saved;
|
|
hasChanges = true;
|
|
}
|
|
_gameHudForceShowBeforeRestore.Remove(toDoListHash);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasChanges)
|
|
{
|
|
config->SaveFile(true);
|
|
config->ApplyHudLayout();
|
|
}
|
|
}
|
|
|
|
private unsafe void UpdateDefaultCastBar(bool forceVisible = false)
|
|
{
|
|
// Hide when explicit config says so, or when HSUI is replacing it (HideDefaultHudWhenReplaced + player castbar enabled)
|
|
bool playerCastbarEnabled = ConfigurationManager.Instance?.GetConfigObject<PlayerCastbarConfig>()?.Enabled ?? false;
|
|
bool shouldHide = Config.HideDefaultCastbar || (Config.HideDefaultHudWhenReplaced && playerCastbarEnabled);
|
|
|
|
if (shouldHide && !_hidingCastBar)
|
|
{
|
|
Plugin.AddonLifecycle.RegisterListener(AddonEvent.PreDraw, "_CastBar", (addonEvent, args) =>
|
|
{
|
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon.Address;
|
|
|
|
if (!_hidingCastBar)
|
|
{
|
|
_castBarPos = new Vector2(addon->RootNode->GetXFloat(), addon->RootNode->GetYFloat());
|
|
}
|
|
|
|
addon->RootNode->SetPositionFloat(-9999.0f, -9999.0f);
|
|
});
|
|
|
|
_hidingCastBar = true;
|
|
}
|
|
else if ((forceVisible || !shouldHide) && _hidingCastBar)
|
|
{
|
|
Plugin.AddonLifecycle.UnregisterListener(AddonEvent.PreDraw, "_CastBar");
|
|
|
|
AtkUnitBase* addon = (AtkUnitBase*)Plugin.GameGui.GetAddonByName("_CastBar", 1).Address;
|
|
if (addon != null)
|
|
{
|
|
addon->RootNode->SetPositionFloat(_castBarPos.X, _castBarPos.Y);
|
|
}
|
|
|
|
_hidingCastBar = false;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
private unsafe void UpdateDefaultPulltimer(bool forceVisible = false)
|
|
{
|
|
if (Config.HideDefaultPulltimer && !_hidingPullTimer)
|
|
{
|
|
Plugin.AddonLifecycle.RegisterListener(AddonEvent.PreDraw, "ScreenInfo_CountDown", (addonEvent, args) =>
|
|
{
|
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon.Address;
|
|
|
|
if (!_hidingPullTimer)
|
|
{
|
|
_pullTimerPos = new Vector2(addon->RootNode->GetXFloat(), addon->RootNode->GetYFloat());
|
|
}
|
|
|
|
addon->RootNode->SetPositionFloat(-9999.0f, -9999.0f);
|
|
});
|
|
|
|
AtkUnitBase* addon = (AtkUnitBase*)Plugin.GameGui.GetAddonByName("ScreenInfo_CountDown", 1).Address;
|
|
if (addon != null)
|
|
{
|
|
_pullTimerPos = new Vector2(addon->RootNode->GetXFloat(), addon->RootNode->GetYFloat());
|
|
addon->RootNode->SetPositionFloat(-9999, -9999);
|
|
}
|
|
|
|
_hidingPullTimer = true;
|
|
}
|
|
else if ((forceVisible || !Config.HideDefaultPulltimer) && _hidingPullTimer)
|
|
{
|
|
Plugin.AddonLifecycle.UnregisterListener(AddonEvent.PreDraw, "ScreenInfo_CountDown");
|
|
|
|
AtkUnitBase* addon = (AtkUnitBase*)Plugin.GameGui.GetAddonByName("ScreenInfo_CountDown", 1).Address;
|
|
if (addon != null)
|
|
{
|
|
addon->RootNode->SetPositionFloat(_pullTimerPos.X, _pullTimerPos.Y);
|
|
}
|
|
|
|
_hidingPullTimer = false;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/// <summary>When Duty List/Scenario is enabled, hide the game's Duty List addon with IsVisible=false so it stays at layout position and remains populated for objective text; restore on disable.</summary>
|
|
private unsafe void UpdateDefaultDutyList(bool forceRestore = false)
|
|
{
|
|
var dutyListScenario = ConfigurationManager.Instance?.GetConfigObject<DutyListScenarioConfig>();
|
|
bool shouldHide = !forceRestore && (dutyListScenario?.Enabled ?? false);
|
|
|
|
if (shouldHide && !_hidingDutyListOffScreen)
|
|
{
|
|
Plugin.AddonLifecycle.RegisterListener(AddonEvent.PreDraw, "_ToDoList", (addonEvent, args) =>
|
|
{
|
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon.Address;
|
|
if (addon == null) return;
|
|
addon->IsVisible = false;
|
|
});
|
|
|
|
var addon = (AtkUnitBase*)Plugin.GameGui.GetAddonByName("_ToDoList", 1).Address;
|
|
if (addon == null) addon = (AtkUnitBase*)Plugin.GameGui.GetAddonByName("_ToDoList", 0).Address;
|
|
if (addon != null)
|
|
{
|
|
_toDoListVisibleBeforeHide = addon->IsVisible;
|
|
addon->IsVisible = false;
|
|
}
|
|
|
|
_hidingDutyListOffScreen = true;
|
|
}
|
|
else if ((forceRestore || !shouldHide) && _hidingDutyListOffScreen)
|
|
{
|
|
Plugin.AddonLifecycle.UnregisterListener(AddonEvent.PreDraw, "_ToDoList");
|
|
|
|
var addon = (AtkUnitBase*)Plugin.GameGui.GetAddonByName("_ToDoList", 1).Address;
|
|
if (addon == null) addon = (AtkUnitBase*)Plugin.GameGui.GetAddonByName("_ToDoList", 0).Address;
|
|
if (addon != null)
|
|
addon->IsVisible = _toDoListVisibleBeforeHide;
|
|
|
|
_toDoListVisibleBeforeHide = true;
|
|
_hidingDutyListOffScreen = false;
|
|
}
|
|
}
|
|
|
|
private static readonly Dictionary<uint, Type> _jobIdToConfigType = new()
|
|
{
|
|
[JobIDs.PLD] = typeof(PaladinConfig), [JobIDs.WAR] = typeof(WarriorConfig),
|
|
[JobIDs.DRK] = typeof(DarkKnightConfig), [JobIDs.GNB] = typeof(GunbreakerConfig),
|
|
[JobIDs.WHM] = typeof(WhiteMageConfig), [JobIDs.SCH] = typeof(ScholarConfig),
|
|
[JobIDs.AST] = typeof(AstrologianConfig), [JobIDs.SGE] = typeof(SageConfig),
|
|
[JobIDs.MNK] = typeof(MonkConfig), [JobIDs.DRG] = typeof(DragoonConfig),
|
|
[JobIDs.NIN] = typeof(NinjaConfig), [JobIDs.SAM] = typeof(SamuraiConfig),
|
|
[JobIDs.RPR] = typeof(ReaperConfig), [JobIDs.VPR] = typeof(ViperConfig),
|
|
[JobIDs.BRD] = typeof(BardConfig), [JobIDs.MCH] = typeof(MachinistConfig),
|
|
[JobIDs.DNC] = typeof(DancerConfig), [JobIDs.BLM] = typeof(BlackMageConfig),
|
|
[JobIDs.SMN] = typeof(SummonerConfig), [JobIDs.RDM] = typeof(RedMageConfig),
|
|
[JobIDs.BLU] = typeof(BlueMageConfig), [JobIDs.PCT] = typeof(PictomancerConfig),
|
|
};
|
|
|
|
private static bool IsCurrentJobHudEnabled()
|
|
{
|
|
var player = Plugin.ObjectTable.LocalPlayer;
|
|
if (player == null) return false;
|
|
if (!_jobIdToConfigType.TryGetValue(player.ClassJob.RowId, out var configType))
|
|
return false;
|
|
var config = ConfigurationManager.Instance?.GetConfigObjectForType(configType) as JobConfig;
|
|
return config?.Enabled ?? false;
|
|
}
|
|
|
|
private static IEnumerable<string> GetCurrentJobGaugeAddonNames()
|
|
{
|
|
var player = Plugin.ObjectTable.LocalPlayer;
|
|
if (player == null) yield break;
|
|
if (!JobsHelper.JobNames.TryGetValue(player.ClassJob.RowId, out var jobName))
|
|
yield break;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
string addonName = $"JobHud{jobName}{i}";
|
|
if (_specialCases.TryGetValue(addonName, out var name) && name != null)
|
|
addonName = name;
|
|
yield return addonName;
|
|
}
|
|
}
|
|
|
|
private bool _hidingNameplates = false;
|
|
private bool _nameplateVisibilityBeforeHide = true;
|
|
|
|
private unsafe void UpdateDefaultNameplates(bool forceVisible = false)
|
|
{
|
|
var nameplatesConfig = ConfigurationManager.Instance?.GetConfigObject<NameplatesGeneralConfig>();
|
|
bool shouldHide = !forceVisible && Config.HideDefaultHudWhenReplaced && (nameplatesConfig?.Enabled ?? false);
|
|
|
|
var addon = (AtkUnitBase*)Plugin.GameGui.GetAddonByName("NamePlate", 1).Address;
|
|
if (addon == null) return;
|
|
|
|
if (shouldHide)
|
|
{
|
|
if (!_hidingNameplates)
|
|
{
|
|
_nameplateVisibilityBeforeHide = addon->IsVisible;
|
|
_hidingNameplates = true;
|
|
}
|
|
addon->IsVisible = false;
|
|
}
|
|
else
|
|
{
|
|
if (_hidingNameplates)
|
|
{
|
|
addon->IsVisible = _nameplateVisibilityBeforeHide;
|
|
_hidingNameplates = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Dictionary<string, string> _specialCases = new()
|
|
{
|
|
["JobHudPCT0"] = "JobHudRPM0",
|
|
["JobHudPCT1"] = "JobHudRPM1",
|
|
|
|
["JobHudNIN1"] = "JobHudNIN1v70",
|
|
|
|
["JobHudVPR0"] = "JobHudRDB0",
|
|
["JobHudVPR1"] = "JobHudRDB1"
|
|
};
|
|
|
|
private unsafe void UpdateJobGauges(bool forceVisible = false)
|
|
{
|
|
IPlayerCharacter? player = Plugin.ObjectTable.LocalPlayer;
|
|
if (player == null) { return; }
|
|
|
|
string jobName = JobsHelper.JobNames[player.ClassJob.RowId];
|
|
int i = 0;
|
|
bool stop = false;
|
|
|
|
do
|
|
{
|
|
string addonName = $"JobHud{jobName}{i}";
|
|
if (_specialCases.TryGetValue(addonName, out string? name) && name != null)
|
|
{
|
|
addonName = name;
|
|
}
|
|
|
|
AtkUnitBase* addon = (AtkUnitBase*)Plugin.GameGui.GetAddonByName(addonName, 1).Address;
|
|
if (addon == null)
|
|
{
|
|
stop = true;
|
|
}
|
|
else
|
|
{
|
|
addon->IsVisible = forceVisible || !Config.HideDefaultJobGauges;
|
|
}
|
|
|
|
i++;
|
|
} while (!stop);
|
|
}
|
|
}
|
|
|
|
internal class StrataLevelComparer<TKey> : IComparer<TKey> where TKey : PluginConfigObject
|
|
{
|
|
public int Compare(TKey? a, TKey? b)
|
|
{
|
|
MovablePluginConfigObject? configA = a is MovablePluginConfigObject ? a as MovablePluginConfigObject : null;
|
|
MovablePluginConfigObject? configB = b is MovablePluginConfigObject ? b as MovablePluginConfigObject : null;
|
|
|
|
if (configA == null && configB == null) { return 0; }
|
|
if (configA == null && configB != null) { return -1; }
|
|
if (configA != null && configB == null) { return 1; }
|
|
|
|
if (configA!.StrataLevel == configB!.StrataLevel)
|
|
{
|
|
return configA.ID.CompareTo(configB.ID);
|
|
}
|
|
|
|
if (configA.StrataLevel < configB.StrataLevel)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
} |