v1.0.8.21: Duty Information in duty, objective progress as X out of Y steps + checkmark, average wait from queue

Made-with: Cursor
This commit is contained in:
2026-03-02 04:06:13 -05:00
parent 2822f730f5
commit 27e743c80f
11 changed files with 1502 additions and 6 deletions
+101
View File
@@ -30,6 +30,8 @@ namespace HSUI.Interface
private bool _hidingCastBar = false;
private Vector2 _pullTimerPos = Vector2.Zero;
private bool _hidingPullTimer = false;
private bool _hidingDutyListOffScreen = false;
private bool _toDoListVisibleBeforeHide = true;
public HudHelper()
{
@@ -63,6 +65,7 @@ namespace HSUI.Interface
SetGameHudElementsHidden(Array.Empty<uint>(), true);
UpdateDefaultCastBar(true);
UpdateDefaultPulltimer(true);
UpdateDefaultDutyList(true);
UpdateDefaultNameplates(true);
UpdateJobGauges(true);
}
@@ -107,6 +110,7 @@ namespace HSUI.Interface
UpdateJobGauges();
UpdateDefaultHudElementsHidden();
UpdateDefaultCastBar();
UpdateDefaultDutyList();
UpdateDefaultNameplates();
}
catch (Exception ex)
@@ -253,6 +257,13 @@ namespace HSUI.Interface
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);
}
// NamePlate is not in HUD Layout config; use UpdateDefaultNameplates (IsVisible) for that
if (IsCurrentJobHudEnabled())
{
@@ -293,6 +304,8 @@ namespace HSUI.Interface
/// <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)
{
@@ -316,6 +329,17 @@ namespace HSUI.Interface
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
{
@@ -330,6 +354,44 @@ namespace HSUI.Interface
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)
@@ -418,6 +480,45 @@ namespace HSUI.Interface
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),