diff --git a/HealBot_Controller_Events.lua b/HealBot_Controller_Events.lua
index 5d76946..830392e 100644
--- a/HealBot_Controller_Events.lua
+++ b/HealBot_Controller_Events.lua
@@ -388,6 +388,11 @@ function HealBot_OnEvent_PlayerTargetChanged(this)
end
function HealBot_OnEvent_PartyMembersChanged(this)
+ HealBot_Model:PreserveStateByGUID()
+ HealBot_Integrations_PruneNampower()
+ if HealBot_IsFighting then
+ HealBot_Action_PartyChanged()
+ end
HealBot_Delay_RecalcParty = 1;
end
diff --git a/HealBot_Controller_Range.lua b/HealBot_Controller_Range.lua
index d24a191..2a95219 100644
--- a/HealBot_Controller_Range.lua
+++ b/HealBot_Controller_Range.lua
@@ -15,9 +15,12 @@ function HealBot_Range_Check(unit, range)
if ( unit == "player" ) then
return_val = 1;
elseif HealBot_Integrations_UnitXP_Active then
- local dist = UnitXP("distanceBetween", "player", unit, "Gaussian")
- if dist and dist <= range then
- return_val = 1;
+ local inSight = UnitXP("inSight", "player", unit)
+ if inSight then
+ local dist = UnitXP("distanceBetween", "player", unit, "Gaussian")
+ if dist and dist <= range then
+ return_val = 1;
+ end
end
elseif ( UnitIsVisible(unit) == 1 ) then
local tx, ty = GetPlayerMapPosition(unit)
diff --git a/HealBot_Data.lua b/HealBot_Data.lua
index c5ea038..7737148 100644
--- a/HealBot_Data.lua
+++ b/HealBot_Data.lua
@@ -257,6 +257,7 @@ HealBot_ConfigDefaults = {
HideParty = 0,
HealBot_Integrations_UnitXP = 0,
HealBot_Integrations_Nampower = 0,
+ HealBot_Integrations_SuperWoW = 0,
};
HealBot_Config = {};
diff --git a/HealBot_Integrations.lua b/HealBot_Integrations.lua
index 028bcce..d1926a9 100644
--- a/HealBot_Integrations.lua
+++ b/HealBot_Integrations.lua
@@ -15,9 +15,83 @@ function HealBot_Integrations_Toggle()
if HealBot_Config.HealBot_Integrations_Nampower == 1 then
HealBot_Integrations_Nampower_Active = true;
HealBot_AddDebug("nampower Integration: ENABLED");
- -- Add Nampower specific initialization here later
+ if not HealBot_NampowerFrame then
+ HealBot_Nampower_Auras = {}
+ HealBot_NampowerFrame = CreateFrame("Frame")
+ HealBot_NampowerFrame:RegisterEvent("AURA_CAST_ON_SELF")
+ HealBot_NampowerFrame:RegisterEvent("AURA_CAST_ON_OTHER")
+ HealBot_NampowerFrame:SetScript("OnEvent", function()
+ if not HealBot_Integrations_Nampower_Active then return end
+ 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
+
+ local spellName = GetSpellRecField(spellID, "name")
+ if not spellName then return end
+
+ if not HealBot_Nampower_Auras[target] then
+ HealBot_Nampower_Auras[target] = {}
+ end
+
+ local expirationTime = GetTime() + (duration / 1000)
+ HealBot_Nampower_Auras[target][spellName] = expirationTime
+
+ local unitID = HealBot_Model:GetUnitIDByName(target)
+ if unitID then
+ HealBot_OnEvent_UnitAura(nil, unitID)
+ end
+ end)
+ end
else
HealBot_Integrations_Nampower_Active = false;
HealBot_AddDebug("nampower Integration: DISABLED");
+ if HealBot_Nampower_Auras then
+ for k in pairs(HealBot_Nampower_Auras) do
+ HealBot_Nampower_Auras[k] = nil
+ end
+ end
+ end
+
+
+
+function HealBot_Integrations_PruneNampower()
+ if not HealBot_Integrations_Nampower_Active or not HealBot_Nampower_Auras then return end
+
+ 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
+ HealBot_Nampower_Auras[targetName] = nil
+ else
+ -- Prune expired auras
+ local hasAuras = false
+ for spellName, expiration in pairs(auras) do
+ if currentTime > expiration then
+ auras[spellName] = nil
+ else
+ hasAuras = true
+ end
+ end
+ if not hasAuras then
+ HealBot_Nampower_Auras[targetName] = nil
+ end
+ end
+ end
+end
+
+ -- SuperWoW
+ if HealBot_Config.HealBot_Integrations_SuperWoW == 1 and UnitExists ~= nil then
+ -- We can check if GetUnitGUID exists as a quick SuperWoW test, or UnitExists returns 2 args
+ -- Actually SuperWoW adds SpellInfo, or we can just rely on GetUnitGUID if it exists in the environment
+ if GetUnitGUID or (type(UnitExists) == "function") then
+ -- If the client actually supports GUID tracking via SuperWoW (or a similar mod)
+ HealBot_Integrations_SuperWoW_Active = true;
+ HealBot_AddDebug("SuperWoW Integration: ENABLED");
+ else
+ HealBot_Integrations_SuperWoW_Active = false;
+ HealBot_AddDebug("SuperWoW Integration: DISABLED (Mod Not Found)");
+ end
+ else
+ HealBot_Integrations_SuperWoW_Active = false;
+ HealBot_AddDebug("SuperWoW Integration: DISABLED");
end
end
diff --git a/HealBot_Model.lua b/HealBot_Model.lua
index 7fca64a..259a52b 100644
--- a/HealBot_Model.lua
+++ b/HealBot_Model.lua
@@ -35,7 +35,11 @@ HealBot_Model = {
partyMembers = {},
raidMembers = {},
playerPet = nil,
- target = nil
+ target = nil,
+
+ -- Integration maps
+ unitGUIDs = {}, -- unit -> GUID
+ guidUnits = {} -- GUID -> unit
}
--------------------------------------------------------------------------------
@@ -131,18 +135,105 @@ function HealBot_Model:UpdateUnitIdentity(unit)
if not self.units[unit] then return false end
local oldName = self.units[unit].name
+ local oldEnglishClass = self.units[unit].englishClass
local name = UnitName(unit)
local _, englishClass = UnitClass(unit)
- if oldName ~= name then
+ if oldName ~= name or oldEnglishClass ~= englishClass then
self.units[unit].name = name
self.units[unit].englishClass = englishClass
self.units[unit].class = UnitClass(unit)
+
+ if HealBot_Integrations_SuperWoW_Active and GetUnitGUID then
+ local guid = GetUnitGUID(unit)
+ if guid then
+ self.unitGUIDs[unit] = guid
+ self.guidUnits[guid] = unit
+ end
+ end
return true -- Identity changed
end
return false
end
+function HealBot_Model:GetUnitByGUID(guid)
+ if not guid then return nil end
+ return self.guidUnits[guid]
+end
+
+function HealBot_Model:GetUnitIDByName(name)
+ if not name then return nil end
+ for unit, data in pairs(self.units) do
+ if data.name == name then return unit end
+ end
+ return nil
+end
+
+function HealBot_Model:PreserveStateByGUID()
+ if not HealBot_Integrations_SuperWoW_Active or not GetUnitGUID then return end
+
+ local oldGUIDs = {}
+ for unit, guid in pairs(self.unitGUIDs) do
+ oldGUIDs[unit] = guid
+ end
+
+ local newUnitForGUID = {}
+ -- Scan the new roster
+ for _, unit in ipairs(self.partyMembers) do
+ local guid = GetUnitGUID(unit)
+ if guid then
+ newUnitForGUID[guid] = unit
+ self.unitGUIDs[unit] = guid
+ self.guidUnits[guid] = unit
+ end
+ end
+ for _, unit in ipairs(self.raidMembers) do
+ local guid = GetUnitGUID(unit)
+ if guid then
+ newUnitForGUID[guid] = unit
+ self.unitGUIDs[unit] = guid
+ self.guidUnits[guid] = unit
+ end
+ end
+
+ local stateSwaps = {}
+ local iconSwaps = {}
+ local missingBuffSwaps = {}
+ local debuffSwaps = {}
+
+ for oldUnit, guid in pairs(oldGUIDs) do
+ local newUnit = newUnitForGUID[guid]
+ if newUnit and newUnit ~= oldUnit then
+ stateSwaps[newUnit] = self.units[oldUnit]
+
+ -- Preserve external global tables if they exist
+ if HealBot_UnitIcons and HealBot_UnitIcons[oldUnit] then
+ iconSwaps[newUnit] = HealBot_UnitIcons[oldUnit]
+ end
+ if HealBot_MissingBuffs and HealBot_MissingBuffs[oldUnit] ~= nil then
+ missingBuffSwaps[newUnit] = HealBot_MissingBuffs[oldUnit]
+ end
+ if HealBot_UnitDebuff and HealBot_UnitDebuff[oldUnit] ~= nil then
+ debuffSwaps[newUnit] = HealBot_UnitDebuff[oldUnit]
+ end
+ end
+ end
+
+ for targetUnit, stateData in pairs(stateSwaps) do
+ self.units[targetUnit] = stateData
+
+ if HealBot_UnitIcons then
+ HealBot_UnitIcons[targetUnit] = iconSwaps[targetUnit]
+ end
+ if HealBot_MissingBuffs then
+ HealBot_MissingBuffs[targetUnit] = missingBuffSwaps[targetUnit]
+ end
+ if HealBot_UnitDebuff then
+ HealBot_UnitDebuff[targetUnit] = debuffSwaps[targetUnit]
+ end
+ end
+end
+
-- Updates health/maxHealth values. Returns true if changed.
function HealBot_Model:UpdateUnitHealth(unit)
if not self.units[unit] then return false end
diff --git a/HealBot_Options_Integrations.lua b/HealBot_Options_Integrations.lua
index f93f5de..31f23b2 100644
--- a/HealBot_Options_Integrations.lua
+++ b/HealBot_Options_Integrations.lua
@@ -1,4 +1,5 @@
function HealBot_Options_Integrations_OnShow(this)
HealBot_Options_Integrations_UnitXP:SetChecked(HealBot_Config.HealBot_Integrations_UnitXP);
HealBot_Options_Integrations_Nampower:SetChecked(HealBot_Config.HealBot_Integrations_Nampower);
+ HealBot_Options_Integrations_SuperWoW:SetChecked(HealBot_Config.HealBot_Integrations_SuperWoW);
end
diff --git a/HealBot_Options_Integrations.xml b/HealBot_Options_Integrations.xml
index d4e3154..a990483 100644
--- a/HealBot_Options_Integrations.xml
+++ b/HealBot_Options_Integrations.xml
@@ -42,6 +42,25 @@
+
+
+
+
+
+
+
+
+
+
+ getglobal(this:GetName().."Text"):SetText("Enable SuperWoW Integration (GUID Tracking)");
+
+
+ HealBot_Config.HealBot_Integrations_SuperWoW = this:GetChecked() and 1 or 0;
+ HealBot_Integrations_Toggle();
+
+
+
+
diff --git a/README.md b/README.md
index f30f68a..c4393b9 100644
--- a/README.md
+++ b/README.md
@@ -39,8 +39,7 @@ Default installation path: `C:\Program Files\World of Warcraft\Interface\AddOns\
* **Highly Customizable Skins:** Fully configure dimensions (width, height), row spacing, column layouts, custom textures, opacity, class-colored frames, and outline of fonts.
### To be implemented
-- **GUID-based frame mapping:** Migrate internal state to use lightweight GUID tracking to resolve bugs when targets switch or duplicate names appear (e.g., enemy mobs, warlock pets).
-- **Mod Integration Optimization:** Add conditional SuperWoW/UnitXP SP3 integration to reduce GC spikes and improve frame rendering accuracy.
+- **Strategy Pattern / Code Tree Splits:** Migrate heavy mod integrations (like Nampower aura tracking and SuperWoW UI rendering) into isolated code trees (e.g., `HealBot_Model_SuperWoW.lua`). This will allow dynamic overwriting of global functions at startup, eliminating the CPU overhead of running `if integration then` branch checks inside high-frequency update loops on Vanilla clients.
### Known issues
@@ -50,8 +49,14 @@ Default installation path: `C:\Program Files\World of Warcraft\Interface\AddOns\
### Change Log
**v1.7.0**
-* **External Addon Integrations** - Added a new 'Extras' tab in Options to optionally enable integrations with `UnitXP_SP3` (for ultra-precise 3D range and Line of Sight checks) and `nampower` (for accurate real-time heal/buff tracking).
+* **Integrations UI** - Added a new 'Extras' tab in the Options menu to easily toggle external mod integrations.
+* **UnitXP_SP3 Integration** - Added optional support for UnitXP's API to provide ultra-precise 3D range finding and strict Line of Sight (LoS) checks. This completely bypasses the limitations of Vanilla's 2D map coordinate distance checks.
+* **Nampower Integration** - Implemented background aura tracking to map explicit HoT expiration timestamps directly from nampower. This allows for accurate real-time heal and buff tracking instead of estimating based on cast times.
+* **SuperWoW Integration** - Added robust GUID-based state tracking leveraging SuperWoW's enhanced API. This tracks players by their unique IDs rather than volatile unit strings.
* **UI Layout** - Widened the Options UI and neatly centered elements to accommodate the new integrations tab.
+* **Bug Fix - Class Colors** - Fixed a bug where class colors failed to apply to new units due to a delayed server response.
+* **Bug Fix - Combat Updates** - Fixed an issue where new party members joining mid-combat failed to append to the grid without dropping combat.
+* **Bug Fix - Target Swaps** - Implemented optional SuperWoW GUID-based state preservation to prevent UI tracking bugs when group members are rearranged during combat.
**v1.6.3**
* **Hotfix string splitter** - added falback for string splitter if the the string is empty or nil.