v1.0.2.5: More accurate hotbar cooldowns (ActionManager), fix tooltip cast/recast display

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-01-31 11:36:34 -05:00
parent 891eb59877
commit 95c4dfce38
6 changed files with 72 additions and 13 deletions
+57 -2
View File
@@ -3,10 +3,12 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.System.String;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using KamiToolKit.Controllers;
using static FFXIVClientStructs.FFXIV.Client.Game.ActionManager;
namespace HSUI.Helpers
{
@@ -114,19 +116,72 @@ namespace HSUI.Helpers
continue;
}
int secsLeft = 0;
int pct = slot->GetSlotActionCooldownPercentage(&secsLeft, 0);
bool usable = slot->IsSlotUsable(slot->ApparentSlotType, slot->ApparentActionId);
uint iconId = slot->IconId;
uint actionId = slot->ApparentActionId;
var slotType = slot->ApparentSlotType;
(int pct, int secsLeft) = GetSlotCooldown(slot);
list.Add(new SlotInfo(iconId, false, usable, pct, secsLeft, actionId, slotType, keybind));
}
return list;
}
/// <summary>
/// Gets cooldown for a hotbar slot. For Action/GeneralAction/PetAction, uses ActionManager recast API
/// (more accurate for adjusted IDs, recast groups). Falls back to slot's GetSlotActionCooldownPercentage for Items/Macros.
/// </summary>
private static unsafe (int CooldownPercent, int SecondsLeft) GetSlotCooldown(RaptureHotbarModule.HotbarSlot* slot)
{
if (slot == null) return (0, 0);
var slotType = slot->ApparentSlotType;
uint actionId = slot->ApparentActionId;
if (actionId == 0) return GetSlotCooldownFromSlot(slot);
var actionManager = ActionManager.Instance();
if (actionManager == null) return GetSlotCooldownFromSlot(slot);
ActionType? actionType = slotType switch
{
RaptureHotbarModule.HotbarSlotType.Action => ActionType.Action,
RaptureHotbarModule.HotbarSlotType.GeneralAction => ActionType.GeneralAction,
RaptureHotbarModule.HotbarSlotType.PetAction => ActionType.PetAction,
RaptureHotbarModule.HotbarSlotType.CraftAction => ActionType.CraftAction,
RaptureHotbarModule.HotbarSlotType.Item => ActionType.Item,
_ => null
};
if (actionType.HasValue)
{
// GetAdjustedActionId resolves Continuation, Egi Assaults, etc. Only applies to Action type
uint effectiveId = actionType.Value == ActionType.Action
? actionManager->GetAdjustedActionId(actionId)
: actionId;
float total = actionManager->GetRecastTime(actionType.Value, effectiveId);
float elapsed = actionManager->GetRecastTimeElapsed(actionType.Value, effectiveId);
if (total > 0.001f && elapsed < total)
{
float remaining = total - elapsed;
int pct = (int)Math.Clamp((remaining / total) * 100f, 0, 100);
int secsLeft = (int)Math.Ceiling(remaining);
return (pct, secsLeft);
}
}
return GetSlotCooldownFromSlot(slot);
}
private static unsafe (int CooldownPercent, int SecondsLeft) GetSlotCooldownFromSlot(RaptureHotbarModule.HotbarSlot* slot)
{
if (slot == null) return (0, 0);
int secsLeft = 0;
int pct = slot->GetSlotActionCooldownPercentage(&secsLeft, 0);
return (pct, secsLeft);
}
/// <summary>
/// Returns the default game keybind label for a hotbar slot (Hotbar 1: 1,2,...,0,-,=; Bar 2: Ctrl+1..12; etc.).
/// hotbarIndex 110, slotIndex 011. Used to mirror the default hotbar keybind display.