From abeac379f388711d2b042b791dde1519d04c8e7f Mon Sep 17 00:00:00 2001 From: omicron Date: Sat, 7 Mar 2026 13:14:38 +0100 Subject: [PATCH 01/11] Enhance IconIndicator with tooltips, fix stacks/applications mixup --- src/types/indicators/iconindicator.lua | 62 +++++++++++++++++++++----- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/src/types/indicators/iconindicator.lua b/src/types/indicators/iconindicator.lua index 042f1b2..78e6d81 100644 --- a/src/types/indicators/iconindicator.lua +++ b/src/types/indicators/iconindicator.lua @@ -46,7 +46,8 @@ end ---@param showStacks boolean ---@param doFade nil|number ---@param doFlash nil|number -function IconIndicator:Init(unitframe, size, point, x, y, showStacks, fadeTime, flashTime) +---@param showTooltip nil|boolean +function IconIndicator:Init(unitframe, size, point, x, y, showStacks, fadeTime, flashTime, showTooltip) self.unitframe = unitframe self.frameParent = unitframe.overlays self.size = size @@ -57,13 +58,21 @@ function IconIndicator:Init(unitframe, size, point, x, y, showStacks, fadeTime, self.showStacks = showStacks self.fadeTime = fadeTime self.flashTime = flashTime + self.showTooltip = showTooltip or false + + self.instanceId = 0 + self.spellId = 0 end --- Show the square indicator ---@param data table function IconIndicator:Show(data) local frame, stacks = self:GetFrame() - local numStacks = data.stacks or 0 + local numStacks = data.applications or 0 + + self.auraInstanceID = data.auraInstanceID + self.spellId = data.spellId + self.frame = frame self.stacks = stacks if self.showStacks and numStacks > 0 then @@ -77,12 +86,23 @@ function IconIndicator:Show(data) self.expirationTime = data.expirationTime self.updateThrottle = 0 end + if self.showTooltip then + frame:SetScript("OnEnter", function() + GameTooltip:SetOwner(frame, "ANCHOR_RIGHT") + GameTooltip:SetSpellByID(self.spellId) + GameTooltip:Show() + end) + frame:SetScript("OnLeave", function() + GameTooltip:Hide() + end) + end if data.icon then self:SetIcon(data.icon) else self:SetIcon(self.icon) end frame:Show() + self:UpdateAlpha() end --- Set the icon of the Icon Indicator @@ -131,22 +151,23 @@ function IconIndicator:Update(data) if data.icon then self:SetIcon(data.icon) end - local numStacks = data.stacks or 0 + local numStacks = data.applications or 0 if numStacks > 0 then self.stacks:SetText(numStacks) self.stacks:Show() end self.expirationTime = data.expirationTime - self.frame:SetAlpha(1) + + self.auraInstanceID = data.auraInstanceID + self.spellId = data.spellId + + self:UpdateAlpha() end -function IconIndicator:OnUpdate(dt) - dt = self.updateThrottle + dt - if dt < 0.04 then - self.updateThrottle = dt +function IconIndicator:UpdateAlpha() + if self.expirationTime == 0 then + self.frame:SetAlpha(1) return - else - self.updateThrottle = 0 end local timeLeft = self.expirationTime - GetTime() @@ -159,15 +180,36 @@ function IconIndicator:OnUpdate(dt) elseif timeLeft < fadeTime then local alpha = timeLeft / fadeTime self.frame:SetAlpha(alpha) + else + self.frame:SetAlpha(1) end end +function IconIndicator:OnUpdate(dt) + dt = self.updateThrottle + dt + if dt < 0.04 then + self.updateThrottle = dt + return + else + self.updateThrottle = 0 + end + + self:UpdateAlpha() +end + --- Hide the square indicator. function IconIndicator:Hide() local frame = self.frame + if frame == nil then + return + end self.stacks:Hide() self.frame = nil self.stacks = nil + self.instanceId = nil + self.spellId = nil frame:SetScript("OnUpdate", nil) + frame:SetScript("OnLeave", nil) + frame:SetScript("OnEnter", nil) iconPool:Release(frame) end -- 2.54.0 From 2a24e6455484c9ecbcda1aa4381eee2c1e8c2db5 Mon Sep 17 00:00:00 2001 From: omicron Date: Wed, 18 Mar 2026 00:55:46 +0100 Subject: [PATCH 02/11] update config for TBC shaman play --- src/frames.lua | 68 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/src/frames.lua b/src/frames.lua index 45b1713..7cf2786 100644 --- a/src/frames.lua +++ b/src/frames.lua @@ -21,6 +21,7 @@ local colors = { white = { 1.0, 1.0, 1.0 }, orange = { 1.0, 0.5, 0 }, violet = { 1.0, 0, 1.0 }, + yellow = { 1.0, 1.0, 0 }, magic = { 0.4, 0.4, 1.0 }, disease = { 0.4, 0.2, 0.0 }, poison = { 0.0, 0.7, 0.7 }, @@ -61,25 +62,41 @@ local function MouseConfig() button = "mouse2", mods = {}, kind = "macro", - data = "/use [@UNIT,dead,help]Ancestral Vision; [@UNIT,help]Chain Heal", + data = "/use [@UNIT,dead,help]Ancestral Spirit; [@UNIT,help]Chain Heal", }, { button = "mouse3", mods = {}, kind = "macro", - data = "/use [@UNIT,dead,help]Ancestral Spirit; [@UNIT,help]Purify Spirit", + data = "/use [@UNIT,help]Cure Poison", }, { button = "wheel-up", mods = {}, kind = "macro", - data = "/use [@UNIT,help]Healing Surge", + data = "/use [@UNIT,help]Lesser Healing Wave", + }, + { + button = "wheel-down", + mods = {}, + kind = "macro", + data = "/use [@UNIT,help]Healing Wave", }, - { button = "wheel-down", mods = {}, kind = "macro", data = "/use [@UNIT,help]Riptide" }, -- alt - { button = "wheel-up", mods = { alt = true }, kind = "spell", data = "Healing Wave" }, { button = "wheel-down", mods = { alt = true }, kind = "spell", data = "Earth Shield" }, + { + button = "wheel-up", + mods = { alt = true }, + kind = "macro", + data = "/use [@UNIT,help,nodead]Nature's Swiftness\n/use [@UNIT,help]Healing Wave", + }, + { + button = "mouse3", + mods = { alt = true }, + kind = "macro", + data = "/use [@UNIT,help]Cure Disease", + }, -- Shift { button = "mouse2", mods = { shift = true }, kind = "togglemenu" }, @@ -89,6 +106,12 @@ local function MouseConfig() kind = "macro", data = "/cast [@UNIT,help]Water Walking;\n/stopspelltarget", }, + { + button = "wheel-down", + mods = { shift = true }, + kind = "macro", + data = "/use [@UNIT,help]Healing Wave(rank 1)", + }, } elseif class == "PRIEST" then return { @@ -259,7 +282,7 @@ local function TriggerClassConfig() return { { kind = "AuraTrigger", - spellId = 383648, + spellId = 32594, own = true, -- Second Earth Shield on yourself indicator = { kind = "SquareIndicator", @@ -273,29 +296,30 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 974, - own = true, -- Second Earth Shield on yourself + spellId = 29203, + own = true, -- Healing Way indicator = { kind = "SquareIndicator", size = 17, point = "TOPLEFT", - x = 3, + x = 20, y = -3, - color = colors.white, + color = colors.yellow, showStacks = true, }, }, { kind = "AuraTrigger", - spellId = 61295, - own = true, -- Riptide + spellId = 16237, + own = true, -- Ancestral Fortitude indicator = { - kind = "IconIndicator", + kind = "SquareIndicator", size = 17, - point = "BOTTOMLEFT", - x = 3, - y = 3, - fadeTime = 10.0, + point = "TOPLEFT", + x = 40, + y = -3, + color = colors.orange, + showStacks = false, }, }, { @@ -710,9 +734,13 @@ function CreatePartyFrames(left, top, config) local group = UnitGroup:new(left, top, width, height) local player = UnitFrame:new("player", config) group:AddUnitFrame(player) + local pet = UnitFrame:new("pet", config) + group:AddUnitFrame(pet) for i = 1, 4 do local frame = UnitFrame:new("party" .. i, config) group:AddUnitFrame(frame) + local partypet = UnitFrame:new("partypet" .. i, config) + group:AddUnitFrame(partypet) end group:Sort() end @@ -742,10 +770,10 @@ function CreateFrames() triggers = TriggerConfig(), } - CreatePartyFrames(-0.05, -290.05, config) + CreatePartyFrames(-0.05, -240.05, config) config.hideInRaid = false - CreateRaidFrames(-0.05, -290.05, config) - CreateTargetFrames(110 * 3 + 50 - 0.05, -245.05, config) + CreateRaidFrames(-0.05, -240.05, config) + CreateTargetFrames(110 * 3 + 50 - 0.05, -240.05, config) HideBlizzardFrames() end -- 2.54.0 From 991bc5d0fe7a6e2c49cacf0485a63854fd8c6ed6 Mon Sep 17 00:00:00 2001 From: omicron Date: Wed, 18 Mar 2026 01:29:43 +0100 Subject: [PATCH 03/11] Fix type in AuraList:UpdateAura Due to a typo AuraTrigger:UpdateAura always received nil as its first (before) parameter. This parameter is currently unused so it did not trigger an error. --- src/types/auralist.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/auralist.lua b/src/types/auralist.lua index 558a486..5fea69b 100644 --- a/src/types/auralist.lua +++ b/src/types/auralist.lua @@ -269,7 +269,7 @@ end ---@param aura UnitAuraInfo function AuraList:UpdateAura(aura) for _, trigger in ipairs(self.triggersBySpell[aura.spellId] or {}) do - trigger:UpdateAura(self.auras[aura.auraInstanceId], aura) + trigger:UpdateAura(self.auras[aura.auraInstanceID], aura) end self.auras[aura.auraInstanceID] = aura end -- 2.54.0 From 14aeb2c43ed765533011f79355bd2d28212c5c26 Mon Sep 17 00:00:00 2001 From: omicron Date: Wed, 18 Mar 2026 02:41:39 +0100 Subject: [PATCH 04/11] Fixed typos and logic error in UnitFrame:Disable This function is currently unused so these changes are not tested. The Disable function now always hides the UnitFrame and calls UnregisterUnitWatch with the correct parameters. --- src/types/unitframe.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/types/unitframe.lua b/src/types/unitframe.lua index 1d4cf6b..eeccab4 100644 --- a/src/types/unitframe.lua +++ b/src/types/unitframe.lua @@ -389,10 +389,9 @@ function UnitFrame:Disable() if self.hideInRaid then UnregisterAttributeDriver(self.secureFrame, "state-visibility") elseif self.unit ~= "player" then - UnregisterUnitWatch(self.secure_frame, self.condition) - else - self.secureFrame:Hide() + UnregisterUnitWatch(self.secureFrame, false) end + self.secureFrame:Hide() end -- returns true if this unit is a target unit -- 2.54.0 From b473f7bf3d3a1f32d9c1825a404f0e0a6ee5a4f7 Mon Sep 17 00:00:00 2001 From: omicron Date: Sat, 20 Jun 2026 00:21:25 +0200 Subject: [PATCH 05/11] Add raid pets to the hardcoded configuration --- src/frames.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frames.lua b/src/frames.lua index 7cf2786..ae9c1c2 100644 --- a/src/frames.lua +++ b/src/frames.lua @@ -724,6 +724,8 @@ function CreateRaidFrames(left, top, config) local num = i * 5 + j + 1 local frame = UnitFrame:new("raid" .. num, config) group:AddUnitFrame(frame) + local raidpet = UnitFrame:new("raidpet" .. num, config) + group:AddUnitFrame(raidpet) end end end -- 2.54.0 From 575e829025ce0c5227791111e32f0be31527668e Mon Sep 17 00:00:00 2001 From: omicron Date: Sat, 20 Jun 2026 00:24:18 +0200 Subject: [PATCH 06/11] Make UnitGroup trigger sort on UNIT_PET --- src/types/unitgroup.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/types/unitgroup.lua b/src/types/unitgroup.lua index 0ca51f9..c1fef22 100644 --- a/src/types/unitgroup.lua +++ b/src/types/unitgroup.lua @@ -84,6 +84,7 @@ function UnitGroup:RegisterEvents() frame:RegisterEvent("GROUP_ROSTER_UPDATE") frame:RegisterEvent("PLAYER_REGEN_ENABLED") frame:RegisterEvent("PLAYER_ENTERING_WORLD") + frame:RegisterEvent("UNIT_PET") end function UnitGroup:GROUP_ROSTER_UPDATE() @@ -94,6 +95,10 @@ function UnitGroup:PLAYER_ENTERING_WORLD() self:OnRosterUpdate() end +function UnitGroup:UNIT_PET() + self:OnRosterUpdate() +end + function UnitGroup:PLAYER_REGEN_ENABLED() self:OnLeaveCombat() end -- 2.54.0 From 80859c1e443fd046355f59e08c56fc2c2157dabf Mon Sep 17 00:00:00 2001 From: omicron Date: Wed, 24 Jun 2026 00:37:15 +0200 Subject: [PATCH 07/11] Change the sort algorithm to use UnitExist instead of frame:IsShown When UNIT_PET fires, pet units will exist but the UnitWatch will not yet have shown the frame. --- src/types/unitgroup.lua | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/types/unitgroup.lua b/src/types/unitgroup.lua index c1fef22..d534451 100644 --- a/src/types/unitgroup.lua +++ b/src/types/unitgroup.lua @@ -32,9 +32,10 @@ local function RoleValue(role) end local function UnitFrameDefaultCompare(lhs, rhs) - -- visible < invisible - if lhs.secureFrame:IsShown() ~= rhs.secureFrame:IsShown() then - return lhs.secureFrame:IsShown() + -- exists < not exists + local lexists, rexists = UnitExists(lhs.unit), UnitExists(rhs.unit) + if lexists ~= rexists then + return lexists end local lunit, runit = lhs.unit, rhs.unit @@ -69,7 +70,6 @@ function UnitGroup:Init(left, top, width, height) self.top = top self.left = left self.units = {} - self.sortAfterCombat = false self:RegisterEvents() end @@ -104,18 +104,14 @@ function UnitGroup:PLAYER_REGEN_ENABLED() end function UnitGroup:OnLeaveCombat() - if self.sortAfterCombat then - self:Sort() - self.sortAfterCombat = false - end + self:Sort() end function UnitGroup:OnRosterUpdate() if InCombatLockdown() then - self.sortAfterCombat = true - else - self:Sort() + return end + self:Sort() end function UnitGroup:AddUnitFrame(unit) -- 2.54.0 From b9f1bad81d1f0f774245d63f1c92e95615568bf6 Mon Sep 17 00:00:00 2001 From: omicron Date: Thu, 25 Jun 2026 21:58:04 +0200 Subject: [PATCH 08/11] Replace Trigger:SetState by Trigger:SetStateAndData Update the use of the old API in most triggers except AuraTrigger. This means the code isn't in a working state at this commit. --- src/types/triggers/multitrigger.lua | 11 ++++------- src/types/triggers/statustrigger.lua | 2 +- src/types/triggers/trigger.lua | 22 ++++++++++++++++++---- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/types/triggers/multitrigger.lua b/src/types/triggers/multitrigger.lua index f065bb7..ac38768 100644 --- a/src/types/triggers/multitrigger.lua +++ b/src/types/triggers/multitrigger.lua @@ -92,8 +92,7 @@ function MultiTrigger:OnChildActivate(idx, trigger, data) table.sort(activeChildren) -- The highest priority active trigger has changed if activeChild ~= activeChildren[1] then - self:SetData(data) - self:SetState(true, true) + self:SetStateAndData(true, data) end end @@ -117,11 +116,9 @@ function MultiTrigger:OnChildDeactivate(idx, trigger, data) local newActiveChild = activeChildren[1] if activeChild ~= newActiveChild then if activeChildren[1] ~= nil then - self:SetData(childrenData[newActiveChild]) - self:SetState(true, true) + self:SetStateAndData(true, childrenData[newActiveChild]) else - self:SetData({}) - self:SetState(false, false) + self:SetStateAndData(false, {}) end end end @@ -139,5 +136,5 @@ end -- Reset the trigger to the default state. If this changes the activation of the -- trigger then the target will be notified. function MultiTrigger:Reset() - self:SetState(false) + self:SetStateAndData(false, {}) end diff --git a/src/types/triggers/statustrigger.lua b/src/types/triggers/statustrigger.lua index c28ab32..a8e07e7 100644 --- a/src/types/triggers/statustrigger.lua +++ b/src/types/triggers/statustrigger.lua @@ -58,5 +58,5 @@ end -- status: -- Must be a valid UnitAuraInfo function StatusTrigger:UpdateStatus(count) - self:SetState(count >= self.requiredCount, false) + self:SetStateAndData(count >= self.requiredCount, nil) end diff --git a/src/types/triggers/trigger.lua b/src/types/triggers/trigger.lua index dfc306b..9209bb1 100644 --- a/src/types/triggers/trigger.lua +++ b/src/types/triggers/trigger.lua @@ -47,7 +47,9 @@ function Trigger:Init(invert, defaultData) end ---Save new data from the datasource into the trigger. This method will make ----sure the default data is always present +---sure the default data is always present. This method will not ensure the +---target gets notified of new state or new data. Subclasses of Trigger should +---use Trigger:SetStateAndData instead. ---@param data table function Trigger:SetData(data) if self.defaultData then @@ -69,9 +71,21 @@ function Trigger:SetTarget(target) end end ----Set the state of the trigger. Will activate the indicator if needed. +---Set the data and state of the trigger. Will update the trigger as needed. +--- +--- If the state changes this function will call Show or Hide on the indicator. +--- If the state remains the same and the data is not nil, it will call Update +--- on the indicator. +--- ---@param state boolean -function Trigger:SetState(state, doUpdate) +---@param data table|nil +function Trigger:SetStateAndData(state, data) + local doUpdate = false + if data then + doUpdate = true + self:SetData(data) + end + state = state ~= self.invert -- state xor inverted if self.active ~= state then if state then @@ -88,5 +102,5 @@ end -- Reset the trigger to the default state. If this changes the activation of the -- trigger then the target will be notified. function Trigger:Reset() - self:SetState(false, false) + self:SetStateAndData(false, nil) end -- 2.54.0 From 7f3269949fcb89dbf6f310371054471a3b50a6ee Mon Sep 17 00:00:00 2001 From: omicron Date: Fri, 26 Jun 2026 01:25:10 +0200 Subject: [PATCH 09/11] Add multiple spell ids support to AuraTrigger Before AuraTrigger only supported a single spell id but now multiple spells can activate the same trigger. Related changes: - Change the glue between AuraList and AuraTrigger to match this new functionality. - Update the default frame config --- src/frames.lua | 61 ++++++++++++-------- src/types/auralist.lua | 32 ++++++++--- src/types/triggers/auratrigger.lua | 90 ++++++++++++++++++++++-------- 3 files changed, 127 insertions(+), 56 deletions(-) diff --git a/src/frames.lua b/src/frames.lua index ae9c1c2..8ef661c 100644 --- a/src/frames.lua +++ b/src/frames.lua @@ -31,6 +31,19 @@ local colors = { light_blue = { 0.7, 0.7, 1 }, } +---@param list number[] +---@return table +local function set(list) + local s = {} + for _, v in ipairs(list) do + s[v] = true + end + return s +end + +local AURAS_PRIEST_RENEW = set({ 139, 6074, 6075, 6076, 6077, 6078, 10927, 10928, 10929, 25315, 25221, 25222 }) +local AURAS_PRIEST_PWS = set({ 17, 592, 600, 3747, 6065, 6066, 10898, 10899, 10900, 10901, 25217, 25218 }) + local function RangeConfig() local _, class = UnitClass("player") if class == "SHAMAN" then @@ -41,7 +54,7 @@ local function RangeConfig() } elseif class == "PRIEST" then return { - friendly = "Flash Heal", + friendly = "Heal", enemy = "Smite", fade = 0.2, } @@ -134,15 +147,15 @@ local function MouseConfig() button = "wheel-up", mods = {}, kind = "macro", - data = "/use [@UNIT,help]Flash Heal", + data = "/use [@UNIT,help]Lesser Heal", }, - { button = "wheel-down", mods = {}, kind = "macro", data = "/use [@UNIT,help]Renew" }, + { button = "wheel-down", mods = {}, kind = "macro", data = "/use [@UNIT,help]Heal" }, -- alt { button = "mouse1", mods = { alt = true }, kind = "spell", data = "Power Word: Life" }, { button = "mouse2", mods = { alt = true }, kind = "spell", data = "Pain Suppression" }, { button = "mouse3", mods = { alt = true }, kind = "spell", data = "Power Infusion" }, - { button = "wheel-up", mods = { alt = true }, kind = "spell", data = "Rapture" }, + { button = "wheel-up", mods = { alt = true }, kind = "spell", data = "Renew" }, { button = "wheel-down", mods = { alt = true }, kind = "spell", data = "Power Word: Shield" }, -- Shift @@ -282,7 +295,7 @@ local function TriggerClassConfig() return { { kind = "AuraTrigger", - spellId = 32594, + spellIds = set({ 32594 }), own = true, -- Second Earth Shield on yourself indicator = { kind = "SquareIndicator", @@ -296,7 +309,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 29203, + spellIds = set({ 29203 }), own = true, -- Healing Way indicator = { kind = "SquareIndicator", @@ -310,7 +323,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 16237, + spellIds = set({ 16237 }), own = true, -- Ancestral Fortitude indicator = { kind = "SquareIndicator", @@ -338,7 +351,7 @@ local function TriggerClassConfig() return { { kind = "AuraTrigger", - spellId = 17, + spellIds = AURAS_PRIEST_PWS, own = true, -- Power Word: Shield indicator = { kind = "SquareIndicator", @@ -352,7 +365,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 33206, + spellIds = set({ 33206 }), own = true, -- Pain Suppression indicator = { kind = "SquareIndicator", @@ -367,7 +380,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 41635, + spellIds = set({ 41635 }), own = true, -- Prayer of Mending indicator = { kind = "SquareIndicator", @@ -381,7 +394,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 139, + spellIds = AURAS_PRIEST_RENEW, own = true, -- Renew indicator = { kind = "SquareIndicator", @@ -395,7 +408,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 194384, + spellIds = set({ 194384 }), own = true, -- Atonement indicator = { kind = "SquareIndicator", @@ -410,7 +423,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 10060, + spellIds = set({ 10060 }), own = true, -- Power Infusion indicator = { kind = "SquareIndicator", @@ -438,7 +451,7 @@ local function TriggerClassConfig() return { { kind = "AuraTrigger", - spellId = 287280, -- Glimmer of Light + spellIds = set({ 287280 }), -- Glimmer of Light own = true, indicator = { kind = "SquareIndicator", @@ -453,7 +466,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 25771, -- Forbearance + spellIds = set({ 25771 }), -- Forbearance indicator = { kind = "SquareIndicator", size = 17, @@ -467,7 +480,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 53563, -- Beacon of Light + spellIds = set({ 53563 }), -- Beacon of Light own = true, indicator = { kind = "SquareIndicator", @@ -480,7 +493,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 156910, -- Beacon of Faith + spellIds = set({ 156910 }), -- Beacon of Faith own = true, indicator = { kind = "SquareIndicator", @@ -493,7 +506,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 200025, -- Beacon of Virtue + spellIds = set({ 200025 }), -- Beacon of Virtue own = true, indicator = { kind = "SquareIndicator", @@ -506,7 +519,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 1044, -- Blessing of Freedom + spellIds = set({ 1044 }), -- Blessing of Freedom own = true, indicator = { kind = "SquareIndicator", @@ -520,7 +533,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 1111, -- Blessing of Spellwarding + spellIds = set({ 1111 }), -- Blessing of Spellwarding own = true, indicator = { kind = "SquareIndicator", @@ -534,7 +547,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 1022, -- Blessing of Protection + spellIds = set({ 1022 }), -- Blessing of Protection own = true, indicator = { kind = "SquareIndicator", @@ -548,7 +561,7 @@ local function TriggerClassConfig() }, { kind = "AuraTrigger", - spellId = 6940, -- Blessing of Saccrifice + spellIds = set({ 6940 }), -- Blessing of Saccrifice own = true, indicator = { kind = "SquareIndicator", @@ -596,7 +609,7 @@ local function MakeDebuffTrigger(slot, spellid, color, stacks) end return { kind = "AuraTrigger", - spellId = spellid, + spellIds = set({ spellid }), indicator = { kind = "SquareIndicator", size = 15, @@ -676,7 +689,7 @@ local function TriggerConfig() -- Some more debuffs in the bottom row table.insert(triggers, { kind = "AuraTrigger", - spellId = 240443, -- Bursting + spellIds = set({ 240443 }), -- Bursting indicator = { kind = "SquareIndicator", size = 17, diff --git a/src/types/auralist.lua b/src/types/auralist.lua index 5fea69b..74e7e6d 100644 --- a/src/types/auralist.lua +++ b/src/types/auralist.lua @@ -16,6 +16,8 @@ -- Omicron Frames. If not, see . local omi = select(2, ...) local types = omi.GetModule("types") +local AuraTrigger = types.AuraTrigger +local StatusTrigger = types.StatusTrigger --- AuraList keeps track of all the auras attached to a unitframe ---@class AuraList: Object @@ -180,21 +182,35 @@ function AuraList:Init(unitframe) self.triggersByStatus = {} end ----@param trigger AuraTrigger | StatusTrigger -function AuraList:AddTrigger(trigger) +---@param trigger StatusTrigger +function AuraList:AddStatusTrigger(trigger) table.insert(self.triggers, trigger) - local spellId = trigger.spellId local status = trigger.status - if spellId then + if self.triggersByStatus[status] == nil then + self.triggersByStatus[status] = {} + end + table.insert(self.triggersByStatus[status], trigger) +end + +---@param trigger AuraTrigger +function AuraList:AddAuraTrigger(trigger) + table.insert(self.triggers, trigger) + for spellId in pairs(trigger.spellIds) do if self.triggersBySpell[spellId] == nil then self.triggersBySpell[spellId] = {} end table.insert(self.triggersBySpell[spellId], trigger) + end +end + +---@param trigger AuraTrigger | StatusTrigger +function AuraList:AddTrigger(trigger) + if trigger:IsInstanceOf(AuraTrigger) then + self:AddAuraTrigger(trigger) + elseif trigger:IsInstanceOf(StatusTrigger) then + self:AddStatusTrigger(trigger) else - if self.triggersByStatus[status] == nil then - self.triggersByStatus[status] = {} - end - table.insert(self.triggersByStatus[status], trigger) + error("AddTrigger: trigger is neither an AuraTrigger nor a StatusTrigger") end end diff --git a/src/types/triggers/auratrigger.lua b/src/types/triggers/auratrigger.lua index cef6135..c5c725c 100644 --- a/src/types/triggers/auratrigger.lua +++ b/src/types/triggers/auratrigger.lua @@ -21,7 +21,7 @@ local Trigger = types.Trigger ---AuraTrigger is a Trigger that can be attached to AuraList as datasource ---@class AuraTrigger: Trigger ----@field spellId number +---@field spellIds table ---@field count number ---@field requiredCount number local AuraTrigger = types.CreateClass("AuraTrigger", Trigger) @@ -33,7 +33,8 @@ types.AuraTrigger = AuraTrigger ---@param config table ---@return AuraTrigger function AuraTrigger.CreateFromConfig(unit, config) - local trigger = AuraTrigger:new(config.spellId, config.own, config.requiredCount, config.invert, config.defaultData) + local trigger = + AuraTrigger:new(config.spellIds, config.own, config.requiredCount, config.invert, config.defaultData) unit.auras:AddTrigger(trigger) return trigger end @@ -47,29 +48,33 @@ function AuraTrigger.new(cls, ...) end --- Initialize a new AuraTrigger object ----@param spellId number +---@param spellIds table ---@param own boolean ---@param requiredCount number | nil ---@param invert boolean | nil ---@param defaultData table | nil -function AuraTrigger:Init(spellId, own, requiredCount, invert, defaultData) +function AuraTrigger:Init(spellIds, own, requiredCount, invert, defaultData) Trigger.Init(self, invert, defaultData) - self.spellId = spellId + self.spellIds = spellIds + self.instanceData = {} self.requiredCount = requiredCount or 1 self.own = own self.count = 0 + self.bestAuraID = nil end --- Reset the AuraTrigger to the default state function AuraTrigger:Reset() self.count = 0 - self:SetState(self.count >= self.requiredCount, false) + self.bestAuraID = nil + table.wipe(self.instanceData) + self:SetStateAndData(self.count >= self.requiredCount, {}) end ---Check if a given aura matches this AuraTrigger ---@param aura UnitAuraInfo function AuraTrigger:IsMatching(aura) - if aura.spellId ~= aura.spellId then + if not self.spellIds[aura.spellId] then return false end if self.own and aura.sourceUnit ~= "player" then @@ -78,35 +83,68 @@ function AuraTrigger:IsMatching(aura) return true end +-- Get the ID of the current best matching aura in AuraTrigger.instanceData +function AuraTrigger:BestAuraID() + local found = nil + for k, v in pairs(self.instanceData) do + if not found then + found = k + elseif v.expirationTime > self.instanceData[found].expirationTime then + found = k + end + end + return found +end + +---Grab the best aura data and update the current recorded best aura +---It returns data if the current best aura is different from the previous +---@param updatedInstanceId number|nil the aura id that was recently updated +function AuraTrigger:BestAuraData(updatedInstanceId) + local bid = self:BestAuraID() + if bid == nil then + self.bestAuraID = nil + return nil + end + + local data = nil + if bid ~= self.bestAuraID or (updatedInstanceId and bid == updatedInstanceId) then + local aura = self.instanceData[bid] + data = { + stacks = aura.applications, + duration = aura.duration, + expirationTime = aura.expirationTime, + icon = aura.icon, + } + self.bestAuraID = bid + end + return data +end + ---Inform the trigger about an added aura ---@param aura UnitAuraInfo function AuraTrigger:AddAura(aura) if not self:IsMatching(aura) then return end + local aid = aura.auraInstanceID + self.instanceData[aid] = aura self.count = self.count + 1 - self:SetData({ - stacks = aura.applications, - duration = aura.duration, - expirationTime = aura.expirationTime, - icon = aura.icon, - }) - self:SetState(self.count >= self.requiredCount, false) + local newData = self:BestAuraData(nil) + self:SetStateAndData(self.count >= self.requiredCount, newData) end ---Inform the trigger about an updated aura ---@param before UnitAuraInfo ---@param after UnitAuraInfo function AuraTrigger:UpdateAura(before, after) - self:SetData({ - stacks = after.applications, - duration = after.duration, - expirationTime = after.expirationTime, - icon = after.icon, - }) - if self.active then - self.indicator:Update(self.data) + if not self:IsMatching(after) then + return end + local aid = after.auraInstanceID + self.instanceData[aid] = after + + local newData = self:BestAuraData(aid) + self:SetStateAndData(self.count >= self.requiredCount, newData) end ---Inform the trigger about an aura that got removed @@ -115,7 +153,11 @@ function AuraTrigger:RemoveAura(aura) if not self:IsMatching(aura) then return end + + local aid = aura.auraInstanceID + self.instanceData[aid] = nil self.count = self.count - 1 - self:SetData({}) - self:SetState(self.count >= self.requiredCount, false) + + local newData = self:BestAuraData(nil) + self:SetStateAndData(self.count >= self.requiredCount, newData) end -- 2.54.0 From 76851011fe2fb9a87f5e4976f6628fb7b098fa8c Mon Sep 17 00:00:00 2001 From: omicron Date: Wed, 28 Oct 2026 18:24:52 +0100 Subject: [PATCH 10/11] Fix font and status bars to use new API arguments --- src/types/unitframe.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/types/unitframe.lua b/src/types/unitframe.lua index eeccab4..0767e41 100644 --- a/src/types/unitframe.lua +++ b/src/types/unitframe.lua @@ -17,6 +17,11 @@ local omif = select(2, ...) local types = omif.GetModule("types") +local NameFont = CreateFont("OicronFrames_NameFont") +NameFont:SetFont("Interface\\AddOns\\OmicronFrames\\media\\fonts\\roboto\\Roboto-Bold.ttf", 13, "") +NameFont:SetShadowColor(0, 0, 0) +NameFont:SetShadowOffset(1, -1) + ---@class StatusBar local StatusBar = types.StatusBar @@ -101,13 +106,13 @@ function UnitFrame:Init(unit, config) self.absorb = StatusBar:new(self, width, height, 1, true, texture_top) self.absorb:SetColor(unpack(colors.absorb)) - self.absorb:SetFillStyle("REVERSE") + self.absorb:SetFillStyle(Enum.StatusBarFillStyle.Reverse) self.shield = StatusBar:new(self, width, height, 1, true, texture_bot) self.shield:SetColor(unpack(colors.shield)) self.shield:SetRange(0, 1) self.shield:SetValue(0.6) - self.shield:SetFillStyle("REVERSE") + self.shield:SetFillStyle(Enum.StatusBarFillStyle.Reverse) self.auras = AuraList:new(self) @@ -182,10 +187,8 @@ function UnitFrame:CreateName() local name = self.overlays:CreateFontString(nil, "BACKGROUND") self.name = name - name:SetFont("Interface\\AddOns\\OmicronFrames\\media\\fonts\\roboto\\Roboto-Bold.ttf", 13, "") + name:SetFontObject(NameFont) name:SetTextColor(0.8, 0.6, 0.1) - name:SetShadowColor(0, 0, 0) - name:SetShadowOffset(1, -1) name:SetPoint("CENTER") name:SetText("") name:Show() -- 2.54.0 From 949e3686aa83a384fd3d1d7e8c24885206a940ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Rud=C3=A9n?= Date: Wed, 28 Oct 2026 19:22:27 +0100 Subject: [PATCH 11/11] Update config with TBC druid settings --- src/frames.lua | 128 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/src/frames.lua b/src/frames.lua index 8ef661c..69ce9f2 100644 --- a/src/frames.lua +++ b/src/frames.lua @@ -29,6 +29,7 @@ local colors = { red = { 1, 0, 0 }, blue = { 0.4, 0.4, 1 }, light_blue = { 0.7, 0.7, 1 }, + green = { 0.2, 0.9, 0.2 }, } ---@param list number[] @@ -43,6 +44,9 @@ end local AURAS_PRIEST_RENEW = set({ 139, 6074, 6075, 6076, 6077, 6078, 10927, 10928, 10929, 25315, 25221, 25222 }) local AURAS_PRIEST_PWS = set({ 17, 592, 600, 3747, 6065, 6066, 10898, 10899, 10900, 10901, 25217, 25218 }) +local AURAS_DRUID_LIFEBLOOM = set({ 33763 }) +local AURAS_DRUID_REJUV = set({ 774, 1058, 1430, 2090, 2091, 3627, 8910, 9839, 9840, 9841, 25299, 26981, 26982 }) +local AURAS_DRUID_REGROWTH = set({ 8936, 8938, 8939, 8940, 8941, 9750, 9856, 9857, 9858, 26980 }) local function RangeConfig() local _, class = UnitClass("player") @@ -58,6 +62,12 @@ local function RangeConfig() enemy = "Smite", fade = 0.2, } + elseif class == "DRUID" then + return { + friendly = "Rejuvenation", + enemy = "Wrath", + fade = 0.2, + } else return { fade = 0.2, @@ -173,6 +183,66 @@ local function MouseConfig() data = "/cast [@UNIT,help]Leap of Faith;\n/stopspelltarget", }, } + elseif class == "DRUID" then + return { + -- No modifier + { button = "mouse1", mods = {}, kind = "target" }, + { + button = "mouse2", + mods = {}, + kind = "macro", + data = "/use [@UNIT,help]Rejuvenation", + }, + { + button = "wheel-up", + mods = {}, + kind = "macro", + data = "/use [@UNIT,help]Lifebloom", + }, + { + button = "wheel-down", + mods = {}, + kind = "macro", + data = "/use [@UNIT,help]Regrowth", + }, + + -- ctrl + { + button = "wheel-up", + mods = { ctrl = true }, + kind = "macro", + data = "/use [@UNIT,help]Abolish Poison" + }, + { + button = "wheel-down", + mods = { ctrl = true }, + kind = "macro", + data = "/use [@UNIT,help]Remove Curse" + }, + + -- alt + { + button = "mouse1", + mods = { alt = true }, + kind = "macro", + data = "/use [@UNIT,help]Innervate", + }, + { + button = "mouse2", + mods = { alt = true }, + kind = "macro", + data = "/use [@UNIT,dead,help]Rebirth", + }, + + -- Shift + { + button = "wheel-down", + mods = { shift = true }, + kind = "macro", + data = "/use [@UNIT,help]Swiftmend", + }, + { button = "mouse2", mods = { shift = true }, kind = "togglemenu" }, + } elseif class == "MONK" then return { -- No modifier @@ -447,6 +517,64 @@ local function TriggerClassConfig() }, }, } + elseif class == "DRUID" then + return { + { + kind = "AuraTrigger", + spellIds = AURAS_DRUID_LIFEBLOOM, + own = true, -- Lifebloom + indicator = { + kind = "SquareIndicator", + size = 17, + point = "BOTTOMLEFT", + x = 3, + y = 3, + color = colors.green, + showStacks = true, + fadeTime = 7.0, + flashTime = 2, + }, + }, + { + kind = "AuraTrigger", + spellIds = AURAS_DRUID_REGROWTH, + own = true, -- Regrowth + indicator = { + kind = "SquareIndicator", + size = 17, + point = "BOTTOMLEFT", + x = 22, + y = 3, + color = colors.yellow, + fadeTime = 21.0, + }, + }, + { + kind = "AuraTrigger", + spellIds = AURAS_DRUID_REJUV, + own = true, -- Rejuvenation + indicator = { + kind = "SquareIndicator", + size = 17, + point = "BOTTOMLEFT", + x = 41, + y = 3, + color = colors.violet, + fadeTime = 12.0, + }, + }, + { + kind = "StatusTrigger", + status = "Burn", + invert = false, + indicator = { + kind = "BorderIndicator", + thickness = 3.0, + color = colors.red, + level = 0, + }, + }, + } elseif class == "PALADIN" then return { { -- 2.54.0