Merge pull request #22 from Bluewhale1337/dev

Dev
This commit is contained in:
Bluewhale1337
2026-10-04 23:51:53 +02:00
committed by GitHub
15 changed files with 430 additions and 93 deletions
+49 -19
View File
@@ -103,6 +103,21 @@ function HealBot_SlashCmd(cmd)
HealBot_AddDebug( "Channel active" );
return;
end
if (cmd=="debug" or cmd=="dbg") then
if not HealBot_Config then return; end
if HealBot_Config.DebugMode == 1 then
HealBot_Config.DebugMode = 0;
if DEFAULT_CHAT_FRAME then
DEFAULT_CHAT_FRAME:AddMessage("|cFFFFD100HealBot|r: Debug mode |cFFFF0000DISABLED|r.");
end
else
HealBot_Config.DebugMode = 1;
if DEFAULT_CHAT_FRAME then
DEFAULT_CHAT_FRAME:AddMessage("|cFFFFD100HealBot|r: Debug mode |cFF00FF00ENABLED|r.");
end
end
return;
end
end
-- HealBot_SendAddonMessage moved to HealBot_Controller_Comms.lua
@@ -222,27 +237,42 @@ end
-- SpiBonus and GetBonus functions moved to HealBot_Controller_Spells.lua
local HealBot_RaidUnitTokens = {}
for i=1, 40 do HealBot_RaidUnitTokens[i] = "raid"..i end
HealBot_NameToUnitID = {}
function HealBot_UpdateUnitIDCache()
HealBot_NameToUnitID = {}
local name = UnitName("player")
if name then HealBot_NameToUnitID[name] = "player" end
for i=1, 4 do
local unit = "party"..i
name = UnitName(unit)
if name then HealBot_NameToUnitID[name] = unit end
unit = "partypet"..i
name = UnitName(unit)
if name then HealBot_NameToUnitID[name] = unit end
end
local petName = UnitName("pet")
if petName then HealBot_NameToUnitID[petName] = "pet" end
for i=1, 40 do
local unit = "raid"..i
name = UnitName(unit)
if name then HealBot_NameToUnitID[name] = unit end
unit = "raidpet"..i
name = UnitName(unit)
if name then HealBot_NameToUnitID[name] = unit end
end
end
-- HealBot_FindUnitID: Finds a unit token for a given player name.
function HealBot_FindUnitID(unitname)
local text;
for _,unit in ipairs(HealBot_Action_HealGroup) do
text = UnitName(unit);
if text then
if text==unitname then
return unit;
end
end
end
for i=1,40 do
local unit = "raid"..i;
text = UnitName(unit);
if text then
if text==unitname then
return unit;
end
end
end
return nil;
if not unitname then return nil end
return HealBot_NameToUnitID[unitname]
end
-- HealBot_PlaySound: Plays an audio alert based on ID.
+1 -1
View File
@@ -1,6 +1,6 @@
## Interface: 11200
## Title: HealBotBlue
## Version: 1.7.3
## Version: 1.7.4
## Author: Bluewhale
## Notes: Raidframe replacement for healing, decursing, resource tracking and buffing with optional support for SuperWow, ClassicAPI, UnitXP_SP3 and Nampower.
## SavedVariablesPerCharacter: HealBot_Config
+42 -4
View File
@@ -107,13 +107,25 @@ function HealBot_CheckBuffs(unit)
hasBuff[k] = nil
end
local i = 1
if not HealBot_TextureToName then HealBot_TextureToName = {} end
while true do
local buffTexture = UnitBuff(unit, i)
if not buffTexture then break end
HealBot_ScanTooltip:SetOwner(UIParent, "ANCHOR_NONE")
HealBot_ScanTooltip:ClearLines()
HealBot_ScanTooltip:SetUnitBuff(unit, i)
local buffName = HealBot_ScanTooltipTextLeft1:GetText()
local buffName = HealBot_TextureToName[buffTexture]
if buffName == nil then
HealBot_ScanTooltip:SetOwner(UIParent, "ANCHOR_NONE")
HealBot_ScanTooltip:ClearLines()
HealBot_ScanTooltip:SetUnitBuff(unit, i)
local textObj = HealBot_ScanTooltipTextLeft1
if textObj then
buffName = textObj:GetText()
end
if buffName then
HealBot_TextureToName[buffTexture] = buffName
end
end
if buffName then
hasBuff[buffName] = true
end
@@ -260,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
@@ -270,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)
+18 -3
View File
@@ -30,8 +30,15 @@ function HealBot_AddChat(msg)
end
end
-- HealBot_AddDebug: Prints debug messages to internal chat channel.
-- HealBot_AddDebug: Prints debug messages to chat or internal debug channel.
function HealBot_AddDebug(msg)
if HealBot_Config and HealBot_Config.DebugMode == 1 then
if DEFAULT_CHAT_FRAME then
DEFAULT_CHAT_FRAME:AddMessage("|cFFFFD100[HB Debug]|r " .. msg);
end
return;
end
local chanid = HealBot_Get_DebugChan();
if chanid and HealBot_SpamCnt < 3 then
HealBot_SpamCnt = HealBot_SpamCnt + 1;
@@ -136,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]
+14 -7
View File
@@ -173,13 +173,6 @@ function HealBot_OnUpdate(this, arg1)
HealBot_Action_RefreshPower(unitID)
end
local powerID, _ = next(HealBot_View_DirtyPower)
while powerID do
HealBot_Action_RefreshPower(powerID)
HealBot_View_DirtyPower[powerID] = nil
powerID, _ = next(HealBot_View_DirtyPower)
end
if HealBot_EquipChangeTimer > 0 then
HealBot_EquipChangeTimer = HealBot_EquipChangeTimer - arg1
if HealBot_EquipChangeTimer <= 0 then
@@ -189,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
@@ -505,6 +508,7 @@ end
function HealBot_OnEvent_PartyMembersChanged(this)
HealBot_Model:PreserveStateByGUID()
HealBot_Integrations_PruneNampower()
if HealBot_UpdateUnitIDCache then HealBot_UpdateUnitIDCache() end
if HealBot_IsFighting then
HealBot_Action_PartyChanged()
end
@@ -564,12 +568,14 @@ end
function HealBot_OnEvent_SpellsChanged(this, arg1)
if arg1 then return; end
HealBot_AddDebug("HB: SpellsChanged");
if HealBot_CacheSpellIDs then HealBot_CacheSpellIDs() end
HealBot_SpellsInitFlag = 2;
end
-- HealBot_OnEvent_TalentsChanged: Internal utility: HealBot_OnEvent_TalentsChanged
function HealBot_OnEvent_TalentsChanged(this, arg1)
HealBot_AddDebug("HB: TalentsChanged");
if HealBot_UpdateTalentRanks then HealBot_UpdateTalentRanks() end
end
-- HealBot_OnEvent_PlayerEnteringWorld: Internal utility: HealBot_OnEvent_PlayerEnteringWorld
@@ -579,6 +585,7 @@ function HealBot_OnEvent_PlayerEnteringWorld(this)
if HealBot_ApplyRefreshHook then
HealBot_ApplyRefreshHook()
end
if HealBot_UpdateUnitIDCache then HealBot_UpdateUnitIDCache() end
end
-- HealBot_OnEvent_SpellcastStart: Internal utility: HealBot_OnEvent_SpellcastStart
+21
View File
@@ -8,12 +8,31 @@ function HealBot_ResetRangeScale()
_scale = 0
end
local _rangeCache = {}
local _lastFrameTime = 0
-- HealBot_Range_Check: Checks if unit is in range via map distance or API.
function HealBot_Range_Check(unit, range)
local currentTime = GetTime()
if _lastFrameTime ~= currentTime then
for r, cacheTable in pairs(_rangeCache) do
for u in pairs(cacheTable) do
cacheTable[u] = nil
end
end
_lastFrameTime = currentTime
end
local return_val = 0;
if not range then
range = 40;
end
_rangeCache[range] = _rangeCache[range] or {}
if _rangeCache[range][unit] ~= nil then
return _rangeCache[range][unit]
end
if ( unit == "player" ) then
return_val = 1;
elseif HealBot_Integrations_ClassicAPI_Active and UnitDistanceSquared and UnitInLineOfSight then
@@ -56,5 +75,7 @@ function HealBot_Range_Check(unit, range)
end
end
end
_rangeCache[range][unit] = return_val
return return_val;
end
+36 -42
View File
@@ -34,28 +34,27 @@ function HealBot_GetSpellName(id)
return spellName .. " (" .. subSpellName .. ")";
end
HealBot_SpellNameToId = {}
function HealBot_CacheSpellIDs()
HealBot_SpellNameToId = {}
local id = 1
while true do
local spellName, subSpellName = GetSpellName(id, BOOKTYPE_SPELL)
if not spellName then break end
if subSpellName and subSpellName ~= "" then
HealBot_SpellNameToId[spellName .. " (" .. subSpellName .. ")"] = id
HealBot_SpellNameToId[spellName .. "(" .. subSpellName .. ")"] = id
end
HealBot_SpellNameToId[spellName] = id
id = id + 1
end
end
-- HealBot_GetSpellId: Internal utility: HealBot_GetSpellId
function HealBot_GetSpellId(spell)
local id, idd = 1, 0;
while true do
local spellName, subSpellName = GetSpellName(id, BOOKTYPE_SPELL);
if (spellName) then
if (spell == spellName .. " (" .. subSpellName .. ")") or (spell == spellName .. "(" .. subSpellName .. ")") then
return id;
end
if (spell == spellName) then
idd = id;
end
else
break
end
id = id + 1;
end
if idd > 0 then
return idd
else
return nil;
end
if not spell then return nil end
return HealBot_SpellNameToId[spell]
end
-- HealBot_CastSpellByName: Internal utility: HealBot_CastSpellByName
@@ -212,18 +211,6 @@ end
-- HealBot_StartCasting: Initiates spell cast and broadcasts incoming heal.
function HealBot_StartCasting(spell, target, ttype)
HealBot_CastFailed = false;
-- Extract base spell for internal tracking
local baseSpell = spell
local parenIndex = string.find(spell, " %(")
if parenIndex then
baseSpell = string.sub(spell, 1, parenIndex - 1)
else
parenIndex = string.find(spell, "%(")
if parenIndex then
baseSpell = string.sub(spell, 1, parenIndex - 1)
end
end
HealBot_CastSpellByName(spell);
if ( SpellCanTargetUnit(target) ) then
@@ -238,11 +225,11 @@ function HealBot_StartCasting(spell, target, ttype)
end
end
if ttype == "fired" and HealBot_Spells[baseSpell] then
if ttype == "fired" and HealBot_Spells[spell] then
if not HealBot_CastFailed then
HealBot_CastingSpell = baseSpell;
HealBot_CastingSpell = spell;
HealBot_CastingTarget = target;
HealBot_Process_HealValue(baseSpell, target);
HealBot_Process_HealValue(spell, target);
HealBot_AnnounceCast(spell, target);
end
end
@@ -648,7 +635,6 @@ function HealBot_RecalcParty()
end
end
end
HealBot_Action_RefreshButtons();
end
-- HealBot_RecalcSpells: Recalculates spells and updates layout.
@@ -657,17 +643,24 @@ function HealBot_RecalcSpells()
HealBot_RecalcParty();
end
-- HealBot_GetTalentRank: Internal utility: HealBot_GetTalentRank
function HealBot_GetTalentRank(talentName)
HealBot_TalentRanks = {}
function HealBot_UpdateTalentRanks()
HealBot_TalentRanks = {}
for t = 1, GetNumTalentTabs() do
for i = 1, GetNumTalents(t) do
local nameTalent, icon, tier, column, currRank, maxRank = GetTalentInfo(t, i);
if nameTalent == talentName then
return currRank;
local nameTalent, icon, tier, column, currRank, maxRank = GetTalentInfo(t, i)
if nameTalent then
HealBot_TalentRanks[nameTalent] = currRank
end
end
end
return 0;
end
-- HealBot_GetTalentRank: Internal utility: HealBot_GetTalentRank
function HealBot_GetTalentRank(talentName)
if not next(HealBot_TalentRanks) then HealBot_UpdateTalentRanks() end
return HealBot_TalentRanks[talentName] or 0
end
-- HealBot_SpiBonus: Internal utility: HealBot_SpiBonus
@@ -702,6 +695,7 @@ end
-- HealBot_InitSpells: Scans spellbook to find available heals.
function HealBot_InitSpells()
HealBot_CacheSpellIDs()
local id = 1
local cnt = 0;
local class = HealBot_UnitClass("player")
+2
View File
@@ -1,4 +1,5 @@
HealBot_ConfigDefaults = {
DebugMode = 0,
ShowManaBars=0,
ManaBarsHealersOnly=0,
Version = HEALBOT_VERSION,
@@ -260,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
+1 -1
View File
@@ -18,7 +18,7 @@ if (GetLocale() == "frFR") then
HEALBOT_DRUID = "Druide";
HEALBOT_HUNTER = "Chasseur";
HEALBOT_MAGE = "Magier";
HEALBOT_MAGE = "Mage";
HEALBOT_PALADIN = "Paladin";
HEALBOT_PRIEST = "Pr\195\170tre";
HEALBOT_ROGUE = "Voleur";
+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>
+177 -10
View File
@@ -246,23 +246,38 @@ function HealBot_Action_EnableButton(button)
bar.txt:SetTextColor(sr, sg, sb, sa);
local fontName, fontHeight, fontFlags = bar.txt:GetFont()
local fontOutline = HealBot_Config.bfontoutline[HealBot_Config.Current_Skin] or 0
if fontOutline == 1 then
bar.txt:SetFont(fontName, fontHeight, "OUTLINE")
else
bar.txt:SetFont(fontName, fontHeight, "")
local expectedFlags = fontOutline == 1 and "OUTLINE" or ""
if fontFlags ~= expectedFlags then
bar.txt:SetFont(fontName, fontHeight, expectedFlags)
end
local iconSize = HealBot_Config.biconsize[HealBot_Config.Current_Skin] or 12
for i = 1, 10 do
local icon = getglobal(button:GetName() .. "BarIcon" .. i)
local icon = button["icon"..i]
if not icon then
icon = getglobal(button:GetName() .. "BarIcon" .. i)
button["icon"..i] = icon
end
if icon then
if HealBot_UnitIcons and HealBot_UnitIcons[unit] and HealBot_UnitIcons[unit][i] then
icon:SetTexture(HealBot_UnitIcons[unit][i])
icon:SetWidth(iconSize)
icon:SetHeight(iconSize)
icon:Show()
local currentTexture = icon:GetTexture()
local newTexture = HealBot_UnitIcons[unit][i]
if currentTexture ~= newTexture then
icon:SetTexture(newTexture)
end
if icon:GetWidth() ~= iconSize then
icon:SetWidth(iconSize)
icon:SetHeight(iconSize)
end
if not icon:IsVisible() then
icon:Show()
end
else
icon:Hide()
if icon:IsVisible() then
icon:Hide()
end
end
end
end
@@ -1224,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
+16 -2
View File
@@ -21,6 +21,7 @@ Default installation path: `C:\Program Files\World of Warcraft\Interface\AddOns\
* `/hb` - Toggles the main HealBot panel on and off
* `/hb options` - Toggles the HealBot options panel on and off
* `/hb reset` - Resets the contents of the main HealBot panel
* `/hb debug` - Toggles in-game debug message output to the main chat frame
### Key Features & Current Functionality
* **MVC & Observer Architecture:** High-performance, reactive engine that updates frames dynamically on state changes, drastically reducing CPU and memory overhead. Features memory recycling pool and event loop decoupling for zero-stutter combat.
@@ -30,7 +31,7 @@ Default installation path: `C:\Program Files\World of Warcraft\Interface\AddOns\
* **Talent-Based Calculations & Equipment Bonus:** Dynamic spell healing calculations for Druid, Priest, and Paladin based on talents, and automatically scans equipped gear to scale healing predictions.
* **Modifier-Aware Tooltips:** Tooltips dynamically update to show exact bound actions (Shift/Ctrl/Alt) and required mana costs (turns red if insufficient mana).
* **Blizzard Party Frame Toggle:** Toggle to automatically hide Blizzard's default party frames when in a group in favor of HealBot's layouts.
* **HoT & Buff Tracking:** Intelligently track active HoTs and buffs directly on the grid frames with custom icons (e.g., Renew, Rejuvenation, Regrowth, Fear Ward).
* **HoT & Buff Tracking:** Intelligently track active HoTs and buffs directly on the grid frames with custom icons (e.g., Renew, Rejuvenation, Regrowth, Fear Ward). Optional extended HoT timer tracker togglable below frames.
* **Mana Bars for Healers:** Enable and position mini-mana status bars next to unit frames, toggleable to display for healer classes only or all classes.
* **Curse & Debuff Warning (CDC):** Dynamic visual and audio alerts for cleanable debuffs. Customizable colors based on debuff type (Curse, Poison, Disease, Magic).
* **Pet & Familiar Frames:** Dedicated, toggleable frames for tracking and healing player pets.
@@ -49,6 +50,20 @@ Default installation path: `C:\Program Files\World of Warcraft\Interface\AddOns\
### Change Log
**v1.7.4**
* **Performance Fix - CPU Spikes** - Fixed massive FPS drops during raid combat. The `HealBot_CheckBuffs` function previously forced the client to generate hidden UI tooltips for up to 32 buffs per player on every single `UNIT_AURA` event. Tooltip parsing is now cached per texture, bypassing thousands of redundant API calls per second.
* **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.
* **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. Added support for tracking Power Word: Shield. Implemented with a pre-allocated static cache array for zero GC leaks, and precise GUID trapping for instant name resolution on self and non-party members.
**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().
* **Performance Fix - HealComm Networking** - Fixed issue where incoming `HealComm` messages triggered synchronous UI redraws, causing massive lag in 40-man raids during combat. `HealBot_RecalcHeals` is now decoupled and deferred through the `HealBot_OnUpdate` dirty queue, allowing multiple network events to collapse into a single frame redraw.
@@ -144,7 +159,6 @@ Default installation path: `C:\Program Files\World of Warcraft\Interface\AddOns\
* **Chat Announcements & Mouseover Casts:** Chat announcements are now correctly fired when using native Hovercasting (Mouseover). Additionally, added safety checks to prevent spamming chat announcements when accidentally clicking on a dead player (unless casting a resurrection spell).
* **New Priest Buff (Enlighten):** Added the custom "Enlighten" buff to Priest trackable buffs, complete with the `btnholyscriptures` icon on unit frames.
**1.3.3**
* **Chat Panel Refactor:** Completely replaced the spell selection dropdowns in the Chat tab with manual text input boxes, allowing users to type exact spell names (with or without ranks) directly, matching the behavior of the key-bindings configuration. Fixed a string matching bug caused by Vanilla WoW's hidden trailing spaces when comparing spell names, ensuring that both rankless (e.g. `Flash Heal`) and ranked inputs trigger correctly.
* **Chat Layout Stack:** Restructured the Chat Options XML layout to correctly stack the inputs into neat rows, preventing horizontal overlap and providing more space for typing long announce messages.