diff --git a/HealBot_Controller_Events.lua b/HealBot_Controller_Events.lua
index bad14f4..b26d44e 100644
--- a/HealBot_Controller_Events.lua
+++ b/HealBot_Controller_Events.lua
@@ -197,6 +197,28 @@ function HealBot_OnUpdate(this, arg1)
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
+ local displayMode = HealBot_Config.HealBot_Integrations_HoTTimerDisplayMode or 0
+ if displayMode == 1 then
+ -- Icon Overlay mode: dirty-flag units with active Nampower auras
+ if HealBot_Integrations_Nampower_Active and HealBot_Nampower_Auras then
+ local now = GetTime()
+ for guid, auras in pairs(HealBot_Nampower_Auras) do
+ local hasActive = false
+ for _, exp in pairs(auras) do
+ if exp > now then hasActive = true; break end
+ end
+ if hasActive then
+ local unitID = (HealBot_Model and HealBot_Model:GetUnitByGUID(guid))
+ if not unitID and HealBot_PlayerGUID and guid == HealBot_PlayerGUID then
+ unitID = "player"
+ end
+ if unitID and not string.find(unitID, "pet") then
+ HealBot_View_DirtyUnits[unitID] = true
+ end
+ end
+ end
+ end
+ end
if HealBot_Action_UpdatePlayerHoTs then
HealBot_Action_UpdatePlayerHoTs()
end
diff --git a/HealBot_Data.lua b/HealBot_Data.lua
index 3ab8840..0184c6a 100644
--- a/HealBot_Data.lua
+++ b/HealBot_Data.lua
@@ -269,6 +269,8 @@ HealBot_ConfigDefaults = {
HealBot_Integrations_SuperWoW = 0,
HealBot_Integrations_ClassicAPI = 0,
HealBot_Integrations_ShowHoTTimers = 0,
+ HealBot_Integrations_HoTTimerPosition = "BELOW",
+ HealBot_Integrations_HoTTimerDisplayMode = 0,
};
HealBot_Config = {};
@@ -621,11 +623,13 @@ HealBot_TrackedAura_Spells = {};
function HealBot_Init_TrackedAuras()
for spell, info in pairs(HealBot_TrackedAuras) do
+ info.spell = spell
HealBot_TrackedAura_Textures[info.texture] = info
HealBot_TrackedAura_Spells[spell] = info
if info.locKey then
local locName = getglobal(info.locKey)
if locName and locName ~= "" then
+ info.locName = locName
HealBot_TrackedAura_Spells[locName] = info
end
end
diff --git a/HealBot_Integrations.lua b/HealBot_Integrations.lua
index 65a34a7..f6e0884 100644
--- a/HealBot_Integrations.lua
+++ b/HealBot_Integrations.lua
@@ -145,5 +145,8 @@ end
HealBot_Options_Integrations_ShowHoTTimers:SetChecked(0)
HealBot_Config.HealBot_Integrations_ShowHoTTimers = 0
end
+ if HealBot_Options_Integrations_UpdateDropdownStates then
+ HealBot_Options_Integrations_UpdateDropdownStates()
+ end
end
end
diff --git a/HealBot_Options_Integrations.lua b/HealBot_Options_Integrations.lua
index d9d754b..ffd541c 100644
--- a/HealBot_Options_Integrations.lua
+++ b/HealBot_Options_Integrations.lua
@@ -1,3 +1,34 @@
+-- HealBot_Options_Integrations.lua
+
+local function HealBot_Options_SetDropDownState(frame, enabled)
+ if not frame then return end
+ local button = getglobal(frame:GetName() .. "Button")
+ local text = getglobal(frame:GetName() .. "Text")
+ if button then
+ if enabled then
+ button:Enable()
+ else
+ button:Disable()
+ end
+ end
+ if text then
+ if enabled then
+ text:SetTextColor(1, 1, 1)
+ else
+ text:SetTextColor(0.5, 0.5, 0.5)
+ end
+ end
+end
+
+-- Update enabled/disabled state of dropdowns based on current config
+function HealBot_Options_Integrations_UpdateDropdownStates()
+ local timersEnabled = HealBot_Config.HealBot_Integrations_ShowHoTTimers == 1
+ local displayMode = HealBot_Config.HealBot_Integrations_HoTTimerDisplayMode or 0
+
+ HealBot_Options_SetDropDownState(HealBot_Options_Integrations_DisplayModeDropdown, timersEnabled)
+ HealBot_Options_SetDropDownState(HealBot_Options_Integrations_PositionDropdown, timersEnabled and (displayMode == 0))
+end
+
-- HealBot_Options_Integrations_OnShow: UI handler for OnShow options panel.
function HealBot_Options_Integrations_OnShow(this)
HealBot_Options_Integrations_UnitXP:SetChecked(HealBot_Config.HealBot_Integrations_UnitXP);
@@ -5,4 +36,92 @@ function HealBot_Options_Integrations_OnShow(this)
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);
+
+ -- Set display mode dropdown text
+ local displayMode = HealBot_Config.HealBot_Integrations_HoTTimerDisplayMode or 0
+ if displayMode == 1 then
+ UIDropDownMenu_SetText("Icon Overlay", HealBot_Options_Integrations_DisplayModeDropdown)
+ else
+ UIDropDownMenu_SetText("List", HealBot_Options_Integrations_DisplayModeDropdown)
+ end
+
+ -- Set position dropdown text
+ local pos = HealBot_Config.HealBot_Integrations_HoTTimerPosition or "BELOW"
+ local posLabels = { BELOW = "Below", ABOVE = "Above", LEFT = "Left", RIGHT = "Right" }
+ UIDropDownMenu_SetText(posLabels[pos] or "Below", HealBot_Options_Integrations_PositionDropdown)
+
+ -- Update enabled/disabled state
+ HealBot_Options_Integrations_UpdateDropdownStates()
+end
+
+-- Display Mode dropdown click handler (relying on global this)
+function HealBot_Options_Integrations_DisplayModeDropdown_OnClick()
+ UIDropDownMenu_SetSelectedID(HealBot_Options_Integrations_DisplayModeDropdown, this:GetID())
+ HealBot_Config.HealBot_Integrations_HoTTimerDisplayMode = this.value
+ UIDropDownMenu_SetText(this:GetText(), HealBot_Options_Integrations_DisplayModeDropdown)
+ HealBot_Options_Integrations_UpdateDropdownStates()
+ if HealBot_Action_UnitButtons then
+ for u in pairs(HealBot_Action_UnitButtons) do
+ HealBot_View_DirtyUnits[u] = true
+ end
+ end
+ if HealBot_Action_UpdatePlayerHoTs then
+ HealBot_Action_UpdatePlayerHoTs()
+ end
+end
+
+-- Display Mode dropdown init
+function HealBot_Options_Integrations_DisplayModeDropdown_Init()
+ local mode = HealBot_Config.HealBot_Integrations_HoTTimerDisplayMode or 0
+ local info
+
+ info = {}
+ info.text = "List"
+ info.value = 0
+ info.func = HealBot_Options_Integrations_DisplayModeDropdown_OnClick
+ if mode == 0 then
+ info.checked = 1
+ end
+ UIDropDownMenu_AddButton(info)
+
+ info = {}
+ info.text = "Icon Overlay"
+ info.value = 1
+ info.func = HealBot_Options_Integrations_DisplayModeDropdown_OnClick
+ if mode == 1 then
+ info.checked = 1
+ end
+ UIDropDownMenu_AddButton(info)
+end
+
+-- Position dropdown click handler (relying on global this)
+function HealBot_Options_Integrations_PositionDropdown_OnClick()
+ UIDropDownMenu_SetSelectedID(HealBot_Options_Integrations_PositionDropdown, this:GetID())
+ HealBot_Config.HealBot_Integrations_HoTTimerPosition = this.value
+ UIDropDownMenu_SetText(this:GetText(), HealBot_Options_Integrations_PositionDropdown)
+ if HealBot_Action_UpdatePlayerHoTs then
+ HealBot_Action_UpdatePlayerHoTs()
+ end
+end
+
+-- Position dropdown init
+function HealBot_Options_Integrations_PositionDropdown_Init()
+ local positions = {
+ { value = "BELOW", label = "Below" },
+ { value = "ABOVE", label = "Above" },
+ { value = "LEFT", label = "Left" },
+ { value = "RIGHT", label = "Right" },
+ }
+ local currentPos = HealBot_Config.HealBot_Integrations_HoTTimerPosition or "BELOW"
+
+ for _, p in ipairs(positions) do
+ local info = {}
+ info.text = p.label
+ info.value = p.value
+ info.func = HealBot_Options_Integrations_PositionDropdown_OnClick
+ if currentPos == p.value then
+ info.checked = 1
+ end
+ UIDropDownMenu_AddButton(info)
+ end
end
diff --git a/HealBot_Options_Integrations.xml b/HealBot_Options_Integrations.xml
index bec1dde..47795a9 100644
--- a/HealBot_Options_Integrations.xml
+++ b/HealBot_Options_Integrations.xml
@@ -94,9 +94,81 @@
HealBot_Config.HealBot_Integrations_ShowHoTTimers = this:GetChecked() and 1 or 0;
+ HealBot_Options_Integrations_UpdateDropdownStates();
+ if HealBot_Action_UnitButtons then
+ for u in pairs(HealBot_Action_UnitButtons) do
+ HealBot_View_DirtyUnits[u] = true;
+ end
+ end
+ if HealBot_Action_UpdatePlayerHoTs then
+ HealBot_Action_UpdatePlayerHoTs();
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UIDropDownMenu_SetWidth(120, this);
+ UIDropDownMenu_Initialize(this, HealBot_Options_Integrations_DisplayModeDropdown_Init);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UIDropDownMenu_SetWidth(120, this);
+ UIDropDownMenu_Initialize(this, HealBot_Options_Integrations_PositionDropdown_Init);
+
+
+
+
@@ -114,9 +186,9 @@
-
+
-
+
diff --git a/HealBot_View_Layout.lua b/HealBot_View_Layout.lua
index e5bf629..8e06ccc 100644
--- a/HealBot_View_Layout.lua
+++ b/HealBot_View_Layout.lua
@@ -283,6 +283,10 @@ function HealBot_Action_EnableButton(button)
end
local iconSize = HealBot_Config.biconsize[HealBot_Config.Current_Skin] or 12
+ local isPet = string.find(unit, "pet") ~= nil
+ local overlayMode = (HealBot_Config.HealBot_Integrations_ShowHoTTimers == 1) and ((HealBot_Config.HealBot_Integrations_HoTTimerDisplayMode or 0) == 1) and not isPet
+ local unitGUID = overlayMode and ((HealBot_GetUnitGUID and HealBot_GetUnitGUID(unit)) or (unit == "player" and HealBot_PlayerGUID))
+ local currentTime = overlayMode and GetTime()
for i = 1, 10 do
local icon = button["icon"..i]
if not icon then
@@ -305,10 +309,54 @@ function HealBot_Action_EnableButton(button)
if not icon:IsVisible() then
icon:Show()
end
+
+ -- Icon overlay timer text
+ local iconText = button["iconText"..i]
+ if not iconText then
+ iconText = button:CreateFontString(nil, "OVERLAY")
+ iconText:SetFont("Fonts\\ARIALN.TTF", 10, "OUTLINE")
+ iconText:SetPoint("CENTER", icon, "CENTER", 0, 0)
+ iconText:SetTextColor(1, 1, 1, 1)
+ button["iconText"..i] = iconText
+ end
+ if overlayMode and unitGUID and HealBot_Nampower_Auras and HealBot_Nampower_Auras[unitGUID] then
+ local texturePath = HealBot_UnitIcons[unit][i]
+ local auraInfo = HealBot_TrackedAura_Textures and HealBot_TrackedAura_Textures[texturePath]
+ if auraInfo and auraInfo.showTimer then
+ local expiration = (auraInfo.locName and HealBot_Nampower_Auras[unitGUID][auraInfo.locName])
+ or (auraInfo.spell and HealBot_Nampower_Auras[unitGUID][auraInfo.spell])
+ if not expiration and HealBot_TrackedAura_Spells then
+ for spellName, spInfo in pairs(HealBot_TrackedAura_Spells) do
+ if spInfo.texture == texturePath and spInfo.showTimer then
+ expiration = HealBot_Nampower_Auras[unitGUID][spellName]
+ if expiration then break end
+ end
+ end
+ end
+ if expiration and expiration > currentTime then
+ local timeLeft = math.floor(expiration - currentTime)
+ iconText:SetText(timeLeft)
+ iconText:Show()
+ else
+ iconText:SetText("")
+ iconText:Hide()
+ end
+ else
+ iconText:SetText("")
+ iconText:Hide()
+ end
+ else
+ iconText:SetText("")
+ iconText:Hide()
+ end
else
if icon:IsVisible() then
icon:Hide()
end
+ local iconText = button["iconText"..i]
+ if iconText and iconText:IsVisible() then
+ iconText:Hide()
+ end
end
end
end
@@ -1380,22 +1428,65 @@ local function HealBot_PlayerHoTs_SortComparator(a, b)
return (a.expires or 0) < (b.expires or 0)
end
+-- Reposition the HoT timer list frame based on config
+local function HealBot_HoTTimerList_Reanchor()
+ if not HealBot_PlayerHoTsListFrame then return end
+ local pos = HealBot_Config.HealBot_Integrations_HoTTimerPosition or "BELOW"
+ HealBot_PlayerHoTsListFrame:ClearAllPoints()
+ if pos == "ABOVE" then
+ HealBot_PlayerHoTsListFrame:SetPoint("BOTTOMLEFT", HealBot_Action, "TOPLEFT", 0, 2)
+ elseif pos == "RIGHT" then
+ HealBot_PlayerHoTsListFrame:SetPoint("TOPLEFT", HealBot_Action, "TOPRIGHT", 2, 0)
+ elseif pos == "LEFT" then
+ HealBot_PlayerHoTsListFrame:SetPoint("TOPRIGHT", HealBot_Action, "TOPLEFT", -2, 0)
+ else -- BELOW (default)
+ HealBot_PlayerHoTsListFrame:SetPoint("TOPLEFT", HealBot_Action, "BOTTOMLEFT", 0, -2)
+ end
+end
+
+-- Reposition child HoT frames for stacking direction
+local function HealBot_HoTTimerList_StackChild(frame, index)
+ local pos = HealBot_Config.HealBot_Integrations_HoTTimerPosition or "BELOW"
+ frame:ClearAllPoints()
+ if pos == "ABOVE" then
+ if index == 1 then
+ frame:SetPoint("BOTTOMLEFT", HealBot_PlayerHoTsListFrame, "BOTTOMLEFT", 0, 0)
+ else
+ frame:SetPoint("BOTTOMLEFT", HealBot_PlayerHoTFrames[index-1], "TOPLEFT", 0, 2)
+ end
+ else
+ if index == 1 then
+ frame:SetPoint("TOPLEFT", HealBot_PlayerHoTsListFrame, "TOPLEFT", 0, 0)
+ else
+ frame:SetPoint("TOPLEFT", HealBot_PlayerHoTFrames[index-1], "BOTTOMLEFT", 0, -2)
+ end
+ end
+end
+
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
-
+
+ HealBot_HoTTimerList_Reanchor()
+
if HealBot_Config.HealBot_Integrations_ShowHoTTimers ~= 1 then
HealBot_PlayerHoTsListFrame:Hide()
return
- else
- HealBot_PlayerHoTsListFrame:Show()
end
+ -- If display mode is Icon Overlay, hide the list and return
+ local displayMode = HealBot_Config.HealBot_Integrations_HoTTimerDisplayMode or 0
+ if displayMode == 1 then
+ HealBot_PlayerHoTsListFrame:Hide()
+ return
+ end
+
+ HealBot_PlayerHoTsListFrame:Show()
+
local currentTime = GetTime()
-- Clear cache instead of reallocating
@@ -1403,17 +1494,20 @@ function HealBot_Action_UpdatePlayerHoTs()
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
- local auraInfo = HealBot_TrackedAura_Spells and HealBot_TrackedAura_Spells[spellName]
- if auraInfo and auraInfo.showTimer then
- if expiration > currentTime then
- numHoTs = numHoTs + 1
- if not HealBot_PlayerHoTs_ListCache[numHoTs] then
- HealBot_PlayerHoTs_ListCache[numHoTs] = {}
+ local unitID = HealBot_Model:GetUnitByGUID(targetName) or HealBot_Model:GetUnitIDByName(targetName)
+ if not (unitID and string.find(unitID, "pet")) then
+ for spellName, expiration in pairs(auras) do
+ local auraInfo = HealBot_TrackedAura_Spells and HealBot_TrackedAura_Spells[spellName]
+ if auraInfo and auraInfo.showTimer 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
- HealBot_PlayerHoTs_ListCache[numHoTs].name = targetName
- HealBot_PlayerHoTs_ListCache[numHoTs].spell = spellName
- HealBot_PlayerHoTs_ListCache[numHoTs].expires = expiration
end
end
end
@@ -1435,11 +1529,6 @@ function HealBot_Action_UpdatePlayerHoTs()
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)
@@ -1454,6 +1543,9 @@ function HealBot_Action_UpdatePlayerHoTs()
HealBot_PlayerHoTFrames[i] = frame
end
+
+ -- Reposition child for stacking direction
+ HealBot_HoTTimerList_StackChild(frame, i)
local hot = HealBot_PlayerHoTs_ListCache[i]
local timeLeft = math.floor(hot.expires - currentTime)
diff --git a/README.md b/README.md
index 53fa0d1..4578ab4 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,7 @@ Default installation path: `C:\Program Files\World of Warcraft\Interface\AddOns\
### Change Log
**v1.7.6**
+* **Feature - HoT Timer Position & Icon Overlay Display Modes** - Added configurable HoT Timer list position dropdown (`Below`, `Above`, `Left`, `Right`) relative to raid frames. Added alternative `Icon Overlay` display mode rendering countdown numbers directly on unit bar HoT icons. Excluded pets from timer lists and overlay numbers. Optimized expiration lookup with O(1) cached texture-to-spell mappings.
* **Refactor & Feature - Unified Aura & HoT Tracking** - Unified bar icons and HoT timers into a single master dictionary (`HealBot_TrackedAuras`) in `HealBot_Data.lua`. Added Weakened Soul, Fear Ward, Abolish Disease, and Abolish Poison to active timer bars with localized client support and debuff-aware pruning.
**v1.7.5**