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:
@@ -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 1–10, slotIndex 0–11. Used to mirror the default hotbar keybind display.
|
||||
|
||||
Reference in New Issue
Block a user