feat: add Nampower integration for player HoT timers display for active auras

This commit is contained in:
Bluewhale1337
2026-10-04 19:24:34 +02:00
parent 2a0d607bcd
commit 97df6c9a98
10 changed files with 254 additions and 6 deletions
+26
View File
@@ -272,9 +272,11 @@ function HealBot_OnEvent_UnitAura(this, unit)
end
local b = 1
local activeBuffs = HealBot_GetTable()
while true do
local buff = UnitBuff(unit, b)
if not buff then break end
activeBuffs[buff] = true
if HealBot_TrackedHoTs[buff] and iconCount < 10 then
iconCount = iconCount + 1
HealBot_UnitIcons[unit][iconCount] = buff
@@ -282,6 +284,30 @@ function HealBot_OnEvent_UnitAura(this, unit)
b = b + 1
end
if HealBot_Integrations_Nampower_Active and HealBot_Nampower_Auras then
local guid = HealBot_GetUnitGUID and HealBot_GetUnitGUID(unit)
if guid then
if not HealBot_Nampower_NameCache then HealBot_Nampower_NameCache = HealBot_GetTable() end
HealBot_Nampower_NameCache[guid] = UnitName(unit)
if HealBot_Nampower_Auras[guid] then
local validHoTs = {
["Renew"] = "Interface\\Icons\\Spell_Holy_Renew",
["Rejuvenation"] = "Interface\\Icons\\Spell_Nature_Rejuvenation",
["Regrowth"] = "Interface\\Icons\\Spell_Nature_ResistNature",
["Power Word: Shield"] = "Interface\\Icons\\Spell_Holy_PowerWordShield",
}
for spellName, _ in pairs(HealBot_Nampower_Auras[guid]) do
local tex = validHoTs[spellName]
if tex and not activeBuffs[tex] then
HealBot_Nampower_Auras[guid][spellName] = nil
end
end
end
end
end
HealBot_ReleaseTable(activeBuffs)
local d = 1
while true do
local debuff = UnitDebuff(unit, d)
+10 -2
View File
@@ -143,9 +143,17 @@ function HealBot_OnEvent_AddonMsg(this, addon_id, inc_msg, dist_target, sender_i
-- Clear reusable args array
for i=1, 10 do HealBot_Comms_Args[i] = nil end
local argCount = 0
for word in string.gfind(inc_msg, "[^/]+") do
local start_pos = 1
local next_pos = string.find(inc_msg, "/", start_pos, true)
while next_pos do
argCount = argCount + 1
HealBot_Comms_Args[argCount] = word
HealBot_Comms_Args[argCount] = string.sub(inc_msg, start_pos, next_pos - 1)
start_pos = next_pos + 1
next_pos = string.find(inc_msg, "/", start_pos, true)
end
if start_pos <= string.len(inc_msg) then
argCount = argCount + 1
HealBot_Comms_Args[argCount] = string.sub(inc_msg, start_pos)
end
local cmd = HealBot_Comms_Args[1]
+10
View File
@@ -182,6 +182,16 @@ function HealBot_OnUpdate(this, arg1)
HealBot_RecalcSpells();
end
end
if HealBot_Config.HealBot_Integrations_ShowHoTTimers == 1 then
if not HealBot_HoT_Update_Timer or GetTime() >= HealBot_HoT_Update_Timer then
HealBot_HoT_Update_Timer = GetTime() + 0.25
if HealBot_Action_UpdatePlayerHoTs then
HealBot_Action_UpdatePlayerHoTs()
end
end
elseif HealBot_PlayerHoTsListFrame and HealBot_PlayerHoTsListFrame:IsVisible() then
HealBot_PlayerHoTsListFrame:Hide()
end
HealBot_Timer1 = HealBot_Timer1 + arg1;
if HealBot_Timer1 >= 2.5 then
+1
View File
@@ -261,6 +261,7 @@ HealBot_ConfigDefaults = {
HealBot_Integrations_Nampower = 0,
HealBot_Integrations_SuperWoW = 0,
HealBot_Integrations_ClassicAPI = 0,
HealBot_Integrations_ShowHoTTimers = 0,
};
HealBot_Config = {};
+20 -1
View File
@@ -10,6 +10,8 @@ function HealBot_GetUnitGUID(unit)
return UnitGUID(unit)
elseif HealBot_Integrations_SuperWoW_Active and GetUnitGUID then
return GetUnitGUID(unit)
elseif HealBot_Integrations_Nampower_Active and GetUnitGUID then
return GetUnitGUID(unit)
end
return nil
end
@@ -40,6 +42,11 @@ function HealBot_Integrations_Toggle()
local spellID, caster, target, _, _, _, _, duration = arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8
if not caster or not target or duration <= 0 then return end
-- The player is ALWAYS the caster in these events for Nampower
if not HealBot_PlayerGUID then
HealBot_PlayerGUID = caster
end
local spellName = GetSpellRecField(spellID, "name")
if not spellName then return end
@@ -76,7 +83,7 @@ function HealBot_Integrations_PruneNampower()
local currentTime = GetTime()
for targetName, auras in pairs(HealBot_Nampower_Auras) do
-- Check if target is still in the raid/party
if not HealBot_Model:GetUnitIDByName(targetName) then
if not HealBot_Model:GetUnitByGUID(targetName) and not HealBot_Model:GetUnitIDByName(targetName) then
HealBot_ReleaseTable(auras)
HealBot_Nampower_Auras[targetName] = nil
else
@@ -127,4 +134,16 @@ end
HealBot_Integrations_ClassicAPI_Active = false;
HealBot_AddDebug("ClassicAPI Integration: DISABLED");
end
if HealBot_Options_Integrations_ShowHoTTimers then
if HealBot_Integrations_Nampower_Active or HealBot_Integrations_SuperWoW_Active or HealBot_Integrations_ClassicAPI_Active then
HealBot_Options_Integrations_ShowHoTTimers:Enable()
getglobal(HealBot_Options_Integrations_ShowHoTTimers:GetName().."Text"):SetTextColor(1, 1, 1)
else
HealBot_Options_Integrations_ShowHoTTimers:Disable()
getglobal(HealBot_Options_Integrations_ShowHoTTimers:GetName().."Text"):SetTextColor(0.5, 0.5, 0.5)
HealBot_Options_Integrations_ShowHoTTimers:SetChecked(0)
HealBot_Config.HealBot_Integrations_ShowHoTTimers = 0
end
end
end
+2 -2
View File
@@ -156,7 +156,7 @@ function HealBot_Model:UpdateUnitIdentity(unit)
self.units[unit].englishClass = englishClass
self.units[unit].class = UnitClass(unit)
if (HealBot_Integrations_SuperWoW_Active or HealBot_Integrations_ClassicAPI_Active) and HealBot_GetUnitGUID then
if (HealBot_Integrations_SuperWoW_Active or HealBot_Integrations_ClassicAPI_Active or HealBot_Integrations_Nampower_Active) and HealBot_GetUnitGUID then
local guid = HealBot_GetUnitGUID(unit)
if guid and guid ~= "0" and guid ~= "0x0000000000000000" then
self.unitGUIDs[unit] = guid
@@ -185,7 +185,7 @@ end
-- HealBot_Model:PreserveStateByGUID: Preserves icon and debuff state when group indices shift during combat.
function HealBot_Model:PreserveStateByGUID()
if not (HealBot_Integrations_SuperWoW_Active or HealBot_Integrations_ClassicAPI_Active) or not HealBot_GetUnitGUID then return end
if not (HealBot_Integrations_SuperWoW_Active or HealBot_Integrations_ClassicAPI_Active or HealBot_Integrations_Nampower_Active) or not HealBot_GetUnitGUID then return end
for k in pairs(pool_oldGUIDs) do pool_oldGUIDs[k] = nil end
local oldGUIDs = pool_oldGUIDs
+1
View File
@@ -4,4 +4,5 @@ function HealBot_Options_Integrations_OnShow(this)
HealBot_Options_Integrations_Nampower:SetChecked(HealBot_Config.HealBot_Integrations_Nampower);
HealBot_Options_Integrations_SuperWoW:SetChecked(HealBot_Config.HealBot_Integrations_SuperWoW);
HealBot_Options_Integrations_ClassicAPI:SetChecked(HealBot_Config.HealBot_Integrations_ClassicAPI);
HealBot_Options_Integrations_ShowHoTTimers:SetChecked(HealBot_Config.HealBot_Integrations_ShowHoTTimers);
end
+30 -1
View File
@@ -65,7 +65,7 @@
<Anchors>
<Anchor point="TOPLEFT" relativeTo="HealBot_Options_Integrations_SuperWoW" relativePoint="BOTTOMLEFT">
<Offset>
<AbsDimension x="0" y="5"/>
<AbsDimension x="0" y="-5"/>
</Offset>
</Anchor>
</Anchors>
@@ -80,6 +80,24 @@
</Scripts>
</CheckButton>
<CheckButton name="HealBot_Options_Integrations_ShowHoTTimers" inherits="OptionsCheckButtonTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="HealBot_Options_Integrations_ClassicAPI" relativePoint="BOTTOMLEFT">
<Offset>
<AbsDimension x="0" y="-5"/>
</Offset>
</Anchor>
</Anchors>
<Scripts>
<OnLoad>
getglobal(this:GetName().."Text"):SetText("Track Player HoT Timers");
</OnLoad>
<OnClick>
HealBot_Config.HealBot_Integrations_ShowHoTTimers = this:GetChecked() and 1 or 0;
</OnClick>
</Scripts>
</CheckButton>
</Frames>
<Layers>
<Layer level="ARTWORK">
@@ -94,6 +112,17 @@
</Anchors>
<Color r="0.7" g="0.7" b="0.7"/>
</FontString>
<FontString name="HealBot_Options_Integrations_HoTInfo" inherits="GameFontHighlightSmall" justifyH="LEFT" justifyV="TOP" text="Note: HoT Timers require Nampower, SuperWoW, or ClassicAPI to be enabled and active to provide accurate aura times.">
<Size x="330" y="30"/>
<Anchors>
<Anchor point="TOPLEFT" relativeTo="HealBot_Options_Integrations_ShowHoTTimers" relativePoint="BOTTOMLEFT">
<Offset>
<AbsDimension x="34" y="-2"/>
</Offset>
</Anchor>
</Anchors>
<Color r="0.7" g="0.7" b="0.7"/>
</FontString>
</Layer>
</Layers>
<Scripts>
+152
View File
@@ -1239,3 +1239,155 @@ function HealBot_Action_AppendNewUnits()
end
HealBot_ReleaseTable(unitsToCheck)
end
local HealBot_PlayerHoTsListFrame = nil
local HealBot_PlayerHoTFrames = {}
local HealBot_PlayerHoTs_ValidHoTs = {
["Renew"] = "Interface\\Icons\\Spell_Holy_Renew",
["Rejuvenation"] = "Interface\\Icons\\Spell_Nature_Rejuvenation",
["Regrowth"] = "Interface\\Icons\\Spell_Nature_ResistNature",
["Power Word: Shield"] = "Interface\\Icons\\Spell_Holy_PowerWordShield",
}
local HealBot_PlayerHoTs_ListCache = {}
function HealBot_Action_UpdatePlayerHoTs()
if not HealBot_PlayerHoTsListFrame then
HealBot_PlayerHoTsListFrame = CreateFrame("Frame", "HealBot_PlayerHoTsListFrame", HealBot_Action)
HealBot_PlayerHoTsListFrame:SetWidth(128)
HealBot_PlayerHoTsListFrame:SetHeight(1)
HealBot_PlayerHoTsListFrame:SetPoint("TOPLEFT", HealBot_Action, "BOTTOMLEFT", 0, -2)
HealBot_PlayerHoTsListFrame:Show()
end
if HealBot_Config.HealBot_Integrations_ShowHoTTimers ~= 1 then
HealBot_PlayerHoTsListFrame:Hide()
return
else
HealBot_PlayerHoTsListFrame:Show()
end
local currentTime = GetTime()
-- Clear cache instead of reallocating
local numHoTs = 0
if HealBot_Integrations_Nampower_Active and HealBot_Nampower_Auras then
for targetName, auras in pairs(HealBot_Nampower_Auras) do
for spellName, expiration in pairs(auras) do
if HealBot_PlayerHoTs_ValidHoTs[spellName] then
if expiration > currentTime then
numHoTs = numHoTs + 1
if not HealBot_PlayerHoTs_ListCache[numHoTs] then
HealBot_PlayerHoTs_ListCache[numHoTs] = {}
end
HealBot_PlayerHoTs_ListCache[numHoTs].name = targetName
HealBot_PlayerHoTs_ListCache[numHoTs].spell = spellName
HealBot_PlayerHoTs_ListCache[numHoTs].expires = expiration
end
end
end
end
end
-- Blank out remaining cache entries so sort doesn't read them
for i = numHoTs + 1, table.getn(HealBot_PlayerHoTs_ListCache) do
HealBot_PlayerHoTs_ListCache[i].expires = 9999999999
end
-- Sort only the active portion by shifting expired ones to the end
table.sort(HealBot_PlayerHoTs_ListCache, function(a, b)
return (a.expires or 0) < (b.expires or 0)
end)
for i = 1, math.max(numHoTs, table.getn(HealBot_PlayerHoTFrames)) do
local frame = HealBot_PlayerHoTFrames[i]
if i <= numHoTs then
if not frame then
frame = CreateFrame("Frame", "HealBot_PlayerHoT" .. i, HealBot_PlayerHoTsListFrame)
frame:SetWidth(128)
frame:SetHeight(16)
if i == 1 then
frame:SetPoint("TOPLEFT", HealBot_PlayerHoTsListFrame, "TOPLEFT", 0, 0)
else
frame:SetPoint("TOPLEFT", HealBot_PlayerHoTFrames[i-1], "BOTTOMLEFT", 0, -2)
end
local icon = frame:CreateTexture(nil, "ARTWORK")
icon:SetWidth(14)
icon:SetHeight(14)
icon:SetPoint("LEFT", frame, "LEFT", 2, 0)
frame.icon = icon
local text = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
text:SetPoint("LEFT", icon, "RIGHT", 4, 0)
text:SetJustifyH("LEFT")
frame.text = text
HealBot_PlayerHoTFrames[i] = frame
end
local hot = HealBot_PlayerHoTs_ListCache[i]
local timeLeft = math.floor(hot.expires - currentTime)
-- Resolve GUID to Name
if not HealBot_Nampower_NameCache then HealBot_Nampower_NameCache = HealBot_GetTable() end
local dispName = HealBot_Nampower_NameCache[hot.name]
if not dispName then
dispName = hot.name
local resolved = false
local unitID = HealBot_Model:GetUnitByGUID(hot.name) or HealBot_Model:GetUnitIDByName(hot.name)
if unitID then
local n = UnitName(unitID)
if n then
dispName = n
resolved = true
end
elseif string.sub(hot.name, 1, 2) == "0x" or string.sub(hot.name, 1, 2) == "0X" then
if HealBot_PlayerGUID and string.lower(HealBot_PlayerGUID) == string.lower(hot.name) then
dispName = UnitName("player") or "Player"
resolved = true
elseif HealBot_GetUnitGUID then
if string.lower(HealBot_GetUnitGUID("player") or "") == string.lower(hot.name) then
dispName = UnitName("player") or "Player"
resolved = true
elseif string.lower(HealBot_GetUnitGUID("target") or "") == string.lower(hot.name) then
dispName = UnitName("target") or "Target"
resolved = true
elseif string.lower(HealBot_GetUnitGUID("mouseover") or "") == string.lower(hot.name) then
dispName = UnitName("mouseover") or "Mouseover"
resolved = true
end
end
end
if resolved then
HealBot_Nampower_NameCache[hot.name] = dispName
elseif string.sub(hot.name, 1, 2) == "0x" then
dispName = "Unknown"
end
end
local spellId = HealBot_GetSpellId and HealBot_GetSpellId(hot.spell) or nil
local tex = HealBot_PlayerHoTs_ValidHoTs[hot.spell] or "Interface\\Icons\\INV_Misc_QuestionMark"
if spellId then
tex = GetSpellTexture(spellId, "BOOKTYPE_SPELL") or tex
end
frame.icon:SetTexture(tex)
frame.text:SetText(dispName .. ": " .. timeLeft .. "s")
frame.text:SetTextColor(0.8, 1, 0.8)
frame:Show()
else
if frame then
frame:Hide()
end
end
end
HealBot_PlayerHoTsListFrame:SetHeight(math.max(1, numHoTs * 18))
end
+2
View File
@@ -55,12 +55,14 @@ Default installation path: `C:\Program Files\World of Warcraft\Interface\AddOns\
* **Performance Fix - Frame Rendering** - Eliminated redundant UI layout recalculations in `HealBot_Action_EnableButton`. The UI no longer forces `SetFont`, `SetWidth`, `SetHeight`, and `GetGlobal` lookups on every single combat tick (which previously caused 4,000+ layout updates per second in 40-man raids).
* **Performance Fix - Range Checking** - Greatly reduced CPU stutter caused by range calculations. `HealBot_Range_Check` now caches map positioning math and API calls per-frame, preventing the addon from calculating the exact same `math.sqrt` distance up to 5 times per unit during heavy combat.
* **Performance Fix - Unit ID Lookup** - Optimized `HealBot_FindUnitID` by pre-allocating raid unit tokens, preventing massive string concatenation and garbage collection spam during high-frequency combat events like `CHAT_MSG_ADDON` (HealComm).
* **Performance Fix - String GC Leak** - Fixed garbage collection hitches caused by `string.gfind` closures in the HealComm parsing loop by rewriting it to use closure-free `string.find` native loops.
* **Debug Output Command** - Added `/hb debug` (or `/hb dbg`) slash command to toggle real-time debug message printing directly into the standard chat frame without requiring custom chat channels.
* **Performance Fix - Spell Lookup** - Fixed a massive CPU drain and garbage generation issue where the entire spellbook was linearly scanned every frame per unit. Spell IDs are now cached via O(1) dictionary lookup on login and talent/spell changes.
* **Performance Fix - Unit Hash Lookup** - Eliminated heavy C-API calls to `UnitName()` during incoming heals by caching raid and party unit tokens in a fast O(1) hash table that updates on roster changes.
* **Performance Fix - Range Cache GC Leak** - Fixed a continuous memory leak in the range checker caused by string concatenation (`unit .. "_" .. range`) running every frame. Range states are now stored in a nested numeric table cache.
* **Performance Fix - Talent Rank Scan** - Fixed severe CPU stutter when gaining or losing auras that affect stats. Previously, inventory updates forced a full equipment and spell recalculation which linearly scanned the entire talent tree multiple times per rank. Talent ranks are now cached natively.
* **Integrations Bug Fix** - Removed stray translation text from `HealBot_Integrations.lua` that caused Lua syntax errors on load.
* **Feature - HoT Tracking** - Added an option in the Integrations tab to track Player-cast HoT timers in a dedicated list below the main HealBot frame. Requires Nampower, SuperWoW, or ClassicAPI to fetch exact durations.
**v1.7.3**
* **Performance Fix - Table Pooling Part 2** - Fixed remaining un-pooled table allocations in layout updates, macro parsing, nampower integration, and aura tracking by utilizing HealBot_GetTable().