Compare commits

...
12 Commits
Author SHA1 Message Date
Andreas 0569c100a1 Make status colors configurable
The aura based status colors can now be configured in frames.lua. This
is important in TBC where some healers don't care about e.g. magic
debuffs.
2026-10-31 11:59:51 +00:00
Andreas 4567bea3f9 Override role as TANK based on MT assignments
This is particularly helpful in fights like High King Maulgar,
Illidari Council or Kael'Thas where a DPS player is temporarily
made into a tank.
2026-10-29 12:54:24 +01:00
Andreas 949e3686aa Update config with TBC druid settings 2026-10-29 09:52:17 +01:00
omicron 76851011fe Fix font and status bars to use new API arguments 2026-10-29 09:28:49 +01:00
omicron 7f3269949f 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
2026-10-29 09:28:49 +01:00
omicron b9f1bad81d 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.
2026-10-29 09:28:48 +01:00
omicron 80859c1e44 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.
2026-10-29 09:28:48 +01:00
omicron 575e829025 Make UnitGroup trigger sort on UNIT_PET 2026-10-29 09:28:48 +01:00
omicron b473f7bf3d Add raid pets to the hardcoded configuration 2026-10-29 09:28:48 +01:00
omicron 14aeb2c43e 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.
2026-10-29 09:28:47 +01:00
omicron 991bc5d0fe 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.
2026-10-29 09:28:47 +01:00
omicron 2a24e64554 update config for TBC shaman play 2026-10-29 09:28:47 +01:00
8 changed files with 426 additions and 129 deletions

No files matched your search

+259 -41
View File
@@ -21,15 +21,35 @@ local colors = {
white = { 1.0, 1.0, 1.0 }, white = { 1.0, 1.0, 1.0 },
orange = { 1.0, 0.5, 0 }, orange = { 1.0, 0.5, 0 },
violet = { 1.0, 0, 1.0 }, violet = { 1.0, 0, 1.0 },
yellow = { 1.0, 1.0, 0 },
magic = { 0.4, 0.4, 1.0 }, magic = { 0.4, 0.4, 1.0 },
disease = { 0.4, 0.2, 0.0 }, disease = { 0.4, 0.2, 0.0 },
poison = { 0.0, 0.7, 0.7 }, poison = { 0.0, 0.7, 0.7 },
curse = { 0.7, 0.0, 0.7 }, curse = { 0.7, 0.0, 0.7 },
immune = { 0.0, 0.2, 0.4 },
bomb = { 1.0, 0.7, 0.7 },
red = { 1, 0, 0 }, red = { 1, 0, 0 },
blue = { 0.4, 0.4, 1 }, blue = { 0.4, 0.4, 1 },
light_blue = { 0.7, 0.7, 1 }, light_blue = { 0.7, 0.7, 1 },
green = { 0.2, 0.9, 0.2 },
} }
---@param list number[]
---@return table<number, true>
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 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 function RangeConfig()
local _, class = UnitClass("player") local _, class = UnitClass("player")
if class == "SHAMAN" then if class == "SHAMAN" then
@@ -40,10 +60,16 @@ local function RangeConfig()
} }
elseif class == "PRIEST" then elseif class == "PRIEST" then
return { return {
friendly = "Flash Heal", friendly = "Heal",
enemy = "Smite", enemy = "Smite",
fade = 0.2, fade = 0.2,
} }
elseif class == "DRUID" then
return {
friendly = "Rejuvenation",
enemy = "Wrath",
fade = 0.2,
}
else else
return { return {
fade = 0.2, fade = 0.2,
@@ -61,25 +87,41 @@ local function MouseConfig()
button = "mouse2", button = "mouse2",
mods = {}, mods = {},
kind = "macro", 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", button = "mouse3",
mods = {}, mods = {},
kind = "macro", kind = "macro",
data = "/use [@UNIT,dead,help]Ancestral Spirit; [@UNIT,help]Purify Spirit", data = "/use [@UNIT,help]Cure Poison",
}, },
{ {
button = "wheel-up", button = "wheel-up",
mods = {}, mods = {},
kind = "macro", 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 -- 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-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 -- Shift
{ button = "mouse2", mods = { shift = true }, kind = "togglemenu" }, { button = "mouse2", mods = { shift = true }, kind = "togglemenu" },
@@ -89,6 +131,12 @@ local function MouseConfig()
kind = "macro", kind = "macro",
data = "/cast [@UNIT,help]Water Walking;\n/stopspelltarget", 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 elseif class == "PRIEST" then
return { return {
@@ -111,15 +159,15 @@ local function MouseConfig()
button = "wheel-up", button = "wheel-up",
mods = {}, mods = {},
kind = "macro", 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 -- alt
{ button = "mouse1", mods = { alt = true }, kind = "spell", data = "Power Word: Life" }, { button = "mouse1", mods = { alt = true }, kind = "spell", data = "Power Word: Life" },
{ button = "mouse2", mods = { alt = true }, kind = "spell", data = "Pain Suppression" }, { button = "mouse2", mods = { alt = true }, kind = "spell", data = "Pain Suppression" },
{ button = "mouse3", mods = { alt = true }, kind = "spell", data = "Power Infusion" }, { 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" }, { button = "wheel-down", mods = { alt = true }, kind = "spell", data = "Power Word: Shield" },
-- Shift -- Shift
@@ -137,6 +185,66 @@ local function MouseConfig()
data = "/cast [@UNIT,help]Leap of Faith;\n/stopspelltarget", 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 elseif class == "MONK" then
return { return {
-- No modifier -- No modifier
@@ -259,7 +367,7 @@ local function TriggerClassConfig()
return { return {
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 383648, spellIds = set({ 32594 }),
own = true, -- Second Earth Shield on yourself own = true, -- Second Earth Shield on yourself
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -273,29 +381,30 @@ local function TriggerClassConfig()
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 974, spellIds = set({ 29203 }),
own = true, -- Second Earth Shield on yourself own = true, -- Healing Way
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
size = 17, size = 17,
point = "TOPLEFT", point = "TOPLEFT",
x = 3, x = 20,
y = -3, y = -3,
color = colors.white, color = colors.yellow,
showStacks = true, showStacks = true,
}, },
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 61295, spellIds = set({ 16237 }),
own = true, -- Riptide own = true, -- Ancestral Fortitude
indicator = { indicator = {
kind = "IconIndicator", kind = "SquareIndicator",
size = 17, size = 17,
point = "BOTTOMLEFT", point = "TOPLEFT",
x = 3, x = 40,
y = 3, y = -3,
fadeTime = 10.0, color = colors.orange,
showStacks = false,
}, },
}, },
{ {
@@ -314,7 +423,7 @@ local function TriggerClassConfig()
return { return {
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 17, spellIds = AURAS_PRIEST_PWS,
own = true, -- Power Word: Shield own = true, -- Power Word: Shield
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -328,7 +437,7 @@ local function TriggerClassConfig()
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 33206, spellIds = set({ 33206 }),
own = true, -- Pain Suppression own = true, -- Pain Suppression
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -343,7 +452,7 @@ local function TriggerClassConfig()
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 41635, spellIds = set({ 41635 }),
own = true, -- Prayer of Mending own = true, -- Prayer of Mending
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -357,7 +466,7 @@ local function TriggerClassConfig()
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 139, spellIds = AURAS_PRIEST_RENEW,
own = true, -- Renew own = true, -- Renew
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -371,7 +480,7 @@ local function TriggerClassConfig()
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 194384, spellIds = set({ 194384 }),
own = true, -- Atonement own = true, -- Atonement
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -386,7 +495,7 @@ local function TriggerClassConfig()
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 10060, spellIds = set({ 10060 }),
own = true, -- Power Infusion own = true, -- Power Infusion
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -410,11 +519,69 @@ 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 elseif class == "PALADIN" then
return { return {
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 287280, -- Glimmer of Light spellIds = set({ 287280 }), -- Glimmer of Light
own = true, own = true,
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -429,7 +596,7 @@ local function TriggerClassConfig()
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 25771, -- Forbearance spellIds = set({ 25771 }), -- Forbearance
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
size = 17, size = 17,
@@ -443,7 +610,7 @@ local function TriggerClassConfig()
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 53563, -- Beacon of Light spellIds = set({ 53563 }), -- Beacon of Light
own = true, own = true,
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -456,7 +623,7 @@ local function TriggerClassConfig()
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 156910, -- Beacon of Faith spellIds = set({ 156910 }), -- Beacon of Faith
own = true, own = true,
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -469,7 +636,7 @@ local function TriggerClassConfig()
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 200025, -- Beacon of Virtue spellIds = set({ 200025 }), -- Beacon of Virtue
own = true, own = true,
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -482,7 +649,7 @@ local function TriggerClassConfig()
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 1044, -- Blessing of Freedom spellIds = set({ 1044 }), -- Blessing of Freedom
own = true, own = true,
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -496,7 +663,7 @@ local function TriggerClassConfig()
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 1111, -- Blessing of Spellwarding spellIds = set({ 1111 }), -- Blessing of Spellwarding
own = true, own = true,
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -510,7 +677,7 @@ local function TriggerClassConfig()
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 1022, -- Blessing of Protection spellIds = set({ 1022 }), -- Blessing of Protection
own = true, own = true,
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -524,7 +691,7 @@ local function TriggerClassConfig()
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 6940, -- Blessing of Saccrifice spellIds = set({ 6940 }), -- Blessing of Saccrifice
own = true, own = true,
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -572,7 +739,7 @@ local function MakeDebuffTrigger(slot, spellid, color, stacks)
end end
return { return {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = spellid, spellIds = set({ spellid }),
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
size = 15, size = 15,
@@ -652,7 +819,7 @@ local function TriggerConfig()
-- Some more debuffs in the bottom row -- Some more debuffs in the bottom row
table.insert(triggers, { table.insert(triggers, {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 240443, -- Bursting spellIds = set({ 240443 }), -- Bursting
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
size = 17, size = 17,
@@ -668,6 +835,51 @@ local function TriggerConfig()
return triggers return triggers
end end
local function StatusColorClassConfig()
local _, class = UnitClass("player")
if class == "DRUID" then
return {
{ status = "Curse", color = colors.curse },
{ status = "Poison", color = colors.poison },
}
elseif class == "MAGE" then
return {
{ status = "Curse", color = colors.curse },
}
elseif class == "PALADIN" then
return {
{ status = "Magic", color = colors.magic },
{ status = "Poison", color = colors.poison },
{ status = "Disease", color = colors.disease },
}
elseif class == "PRIEST" then
return {
{ status = "Magic", color = colors.magic },
{ status = "Disease", color = colors.disease },
}
elseif class == "SHAMAN" then
return {
{ status = "Poison", color = colors.poison },
{ status = "Disease", color = colors.disease },
}
end
return {}
end
local function StatusColorConfig()
local statusColors = {
{ status = "Immune", color = colors.immune },
{ status = "Bomb", color = colors.bomb },
}
for _, entry in ipairs(StatusColorClassConfig()) do
table.insert(statusColors, entry)
end
return statusColors
end
local types = omif.GetModule("types") local types = omif.GetModule("types")
local UnitFrame = types.UnitFrame local UnitFrame = types.UnitFrame
local UnitGroup = types.UnitGroup local UnitGroup = types.UnitGroup
@@ -700,6 +912,8 @@ function CreateRaidFrames(left, top, config)
local num = i * 5 + j + 1 local num = i * 5 + j + 1
local frame = UnitFrame:new("raid" .. num, config) local frame = UnitFrame:new("raid" .. num, config)
group:AddUnitFrame(frame) group:AddUnitFrame(frame)
local raidpet = UnitFrame:new("raidpet" .. num, config)
group:AddUnitFrame(raidpet)
end end
end end
end end
@@ -710,9 +924,13 @@ function CreatePartyFrames(left, top, config)
local group = UnitGroup:new(left, top, width, height) local group = UnitGroup:new(left, top, width, height)
local player = UnitFrame:new("player", config) local player = UnitFrame:new("player", config)
group:AddUnitFrame(player) group:AddUnitFrame(player)
local pet = UnitFrame:new("pet", config)
group:AddUnitFrame(pet)
for i = 1, 4 do for i = 1, 4 do
local frame = UnitFrame:new("party" .. i, config) local frame = UnitFrame:new("party" .. i, config)
group:AddUnitFrame(frame) group:AddUnitFrame(frame)
local partypet = UnitFrame:new("partypet" .. i, config)
group:AddUnitFrame(partypet)
end end
group:Sort() group:Sort()
end end
@@ -742,10 +960,10 @@ function CreateFrames()
triggers = TriggerConfig(), triggers = TriggerConfig(),
} }
CreatePartyFrames(-0.05, -290.05, config) CreatePartyFrames(-0.05, -240.05, config)
config.hideInRaid = false config.hideInRaid = false
CreateRaidFrames(-0.05, -290.05, config) CreateRaidFrames(-0.05, -240.05, config)
CreateTargetFrames(110 * 3 + 50 - 0.05, -245.05, config) CreateTargetFrames(110 * 3 + 50 - 0.05, -240.05, config)
HideBlizzardFrames() HideBlizzardFrames()
end end
+25 -9
View File
@@ -16,6 +16,8 @@
-- Omicron Frames. If not, see <https://www.gnu.org/licenses/>. -- Omicron Frames. If not, see <https://www.gnu.org/licenses/>.
local omi = select(2, ...) local omi = select(2, ...)
local types = omi.GetModule("types") local types = omi.GetModule("types")
local AuraTrigger = types.AuraTrigger
local StatusTrigger = types.StatusTrigger
--- AuraList keeps track of all the auras attached to a unitframe --- AuraList keeps track of all the auras attached to a unitframe
---@class AuraList: Object ---@class AuraList: Object
@@ -180,21 +182,35 @@ function AuraList:Init(unitframe)
self.triggersByStatus = {} self.triggersByStatus = {}
end end
---@param trigger AuraTrigger | StatusTrigger ---@param trigger StatusTrigger
function AuraList:AddTrigger(trigger) function AuraList:AddStatusTrigger(trigger)
table.insert(self.triggers, trigger) table.insert(self.triggers, trigger)
local spellId = trigger.spellId
local status = trigger.status 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 if self.triggersBySpell[spellId] == nil then
self.triggersBySpell[spellId] = {} self.triggersBySpell[spellId] = {}
end end
table.insert(self.triggersBySpell[spellId], trigger) 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 else
if self.triggersByStatus[status] == nil then error("AddTrigger: trigger is neither an AuraTrigger nor a StatusTrigger")
self.triggersByStatus[status] = {}
end
table.insert(self.triggersByStatus[status], trigger)
end end
end end
@@ -269,7 +285,7 @@ end
---@param aura UnitAuraInfo ---@param aura UnitAuraInfo
function AuraList:UpdateAura(aura) function AuraList:UpdateAura(aura)
for _, trigger in ipairs(self.triggersBySpell[aura.spellId] or {}) do 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 end
self.auras[aura.auraInstanceID] = aura self.auras[aura.auraInstanceID] = aura
end end
+66 -24
View File
@@ -21,7 +21,7 @@ local Trigger = types.Trigger
---AuraTrigger is a Trigger that can be attached to AuraList as datasource ---AuraTrigger is a Trigger that can be attached to AuraList as datasource
---@class AuraTrigger: Trigger ---@class AuraTrigger: Trigger
---@field spellId number ---@field spellIds table<number, true>
---@field count number ---@field count number
---@field requiredCount number ---@field requiredCount number
local AuraTrigger = types.CreateClass("AuraTrigger", Trigger) local AuraTrigger = types.CreateClass("AuraTrigger", Trigger)
@@ -33,7 +33,8 @@ types.AuraTrigger = AuraTrigger
---@param config table ---@param config table
---@return AuraTrigger ---@return AuraTrigger
function AuraTrigger.CreateFromConfig(unit, config) 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) unit.auras:AddTrigger(trigger)
return trigger return trigger
end end
@@ -47,29 +48,33 @@ function AuraTrigger.new(cls, ...)
end end
--- Initialize a new AuraTrigger object --- Initialize a new AuraTrigger object
---@param spellId number ---@param spellIds table<number, true>
---@param own boolean ---@param own boolean
---@param requiredCount number | nil ---@param requiredCount number | nil
---@param invert boolean | nil ---@param invert boolean | nil
---@param defaultData table | 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) Trigger.Init(self, invert, defaultData)
self.spellId = spellId self.spellIds = spellIds
self.instanceData = {}
self.requiredCount = requiredCount or 1 self.requiredCount = requiredCount or 1
self.own = own self.own = own
self.count = 0 self.count = 0
self.bestAuraID = nil
end end
--- Reset the AuraTrigger to the default state --- Reset the AuraTrigger to the default state
function AuraTrigger:Reset() function AuraTrigger:Reset()
self.count = 0 self.count = 0
self:SetState(self.count >= self.requiredCount, false) self.bestAuraID = nil
table.wipe(self.instanceData)
self:SetStateAndData(self.count >= self.requiredCount, {})
end end
---Check if a given aura matches this AuraTrigger ---Check if a given aura matches this AuraTrigger
---@param aura UnitAuraInfo ---@param aura UnitAuraInfo
function AuraTrigger:IsMatching(aura) function AuraTrigger:IsMatching(aura)
if aura.spellId ~= aura.spellId then if not self.spellIds[aura.spellId] then
return false return false
end end
if self.own and aura.sourceUnit ~= "player" then if self.own and aura.sourceUnit ~= "player" then
@@ -78,35 +83,68 @@ function AuraTrigger:IsMatching(aura)
return true return true
end 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 ---Inform the trigger about an added aura
---@param aura UnitAuraInfo ---@param aura UnitAuraInfo
function AuraTrigger:AddAura(aura) function AuraTrigger:AddAura(aura)
if not self:IsMatching(aura) then if not self:IsMatching(aura) then
return return
end end
local aid = aura.auraInstanceID
self.instanceData[aid] = aura
self.count = self.count + 1 self.count = self.count + 1
self:SetData({ local newData = self:BestAuraData(nil)
stacks = aura.applications, self:SetStateAndData(self.count >= self.requiredCount, newData)
duration = aura.duration,
expirationTime = aura.expirationTime,
icon = aura.icon,
})
self:SetState(self.count >= self.requiredCount, false)
end end
---Inform the trigger about an updated aura ---Inform the trigger about an updated aura
---@param before UnitAuraInfo ---@param before UnitAuraInfo
---@param after UnitAuraInfo ---@param after UnitAuraInfo
function AuraTrigger:UpdateAura(before, after) function AuraTrigger:UpdateAura(before, after)
self:SetData({ if not self:IsMatching(after) then
stacks = after.applications, return
duration = after.duration,
expirationTime = after.expirationTime,
icon = after.icon,
})
if self.active then
self.indicator:Update(self.data)
end end
local aid = after.auraInstanceID
self.instanceData[aid] = after
local newData = self:BestAuraData(aid)
self:SetStateAndData(self.count >= self.requiredCount, newData)
end end
---Inform the trigger about an aura that got removed ---Inform the trigger about an aura that got removed
@@ -115,7 +153,11 @@ function AuraTrigger:RemoveAura(aura)
if not self:IsMatching(aura) then if not self:IsMatching(aura) then
return return
end end
local aid = aura.auraInstanceID
self.instanceData[aid] = nil
self.count = self.count - 1 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 end
+4 -7
View File
@@ -92,8 +92,7 @@ function MultiTrigger:OnChildActivate(idx, trigger, data)
table.sort(activeChildren) table.sort(activeChildren)
-- The highest priority active trigger has changed -- The highest priority active trigger has changed
if activeChild ~= activeChildren[1] then if activeChild ~= activeChildren[1] then
self:SetData(data) self:SetStateAndData(true, data)
self:SetState(true, true)
end end
end end
@@ -117,11 +116,9 @@ function MultiTrigger:OnChildDeactivate(idx, trigger, data)
local newActiveChild = activeChildren[1] local newActiveChild = activeChildren[1]
if activeChild ~= newActiveChild then if activeChild ~= newActiveChild then
if activeChildren[1] ~= nil then if activeChildren[1] ~= nil then
self:SetData(childrenData[newActiveChild]) self:SetStateAndData(true, childrenData[newActiveChild])
self:SetState(true, true)
else else
self:SetData({}) self:SetStateAndData(false, {})
self:SetState(false, false)
end end
end end
end end
@@ -139,5 +136,5 @@ end
-- Reset the trigger to the default state. If this changes the activation of the -- Reset the trigger to the default state. If this changes the activation of the
-- trigger then the target will be notified. -- trigger then the target will be notified.
function MultiTrigger:Reset() function MultiTrigger:Reset()
self:SetState(false) self:SetStateAndData(false, {})
end end
+1 -1
View File
@@ -58,5 +58,5 @@ end
-- status: -- status:
-- Must be a valid UnitAuraInfo -- Must be a valid UnitAuraInfo
function StatusTrigger:UpdateStatus(count) function StatusTrigger:UpdateStatus(count)
self:SetState(count >= self.requiredCount, false) self:SetStateAndData(count >= self.requiredCount, nil)
end end
+18 -4
View File
@@ -47,7 +47,9 @@ function Trigger:Init(invert, defaultData)
end end
---Save new data from the datasource into the trigger. This method will make ---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 ---@param data table
function Trigger:SetData(data) function Trigger:SetData(data)
if self.defaultData then if self.defaultData then
@@ -69,9 +71,21 @@ function Trigger:SetTarget(target)
end end
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 ---@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 state = state ~= self.invert -- state xor inverted
if self.active ~= state then if self.active ~= state then
if state then if state then
@@ -88,5 +102,5 @@ end
-- Reset the trigger to the default state. If this changes the activation of the -- Reset the trigger to the default state. If this changes the activation of the
-- trigger then the target will be notified. -- trigger then the target will be notified.
function Trigger:Reset() function Trigger:Reset()
self:SetState(false, false) self:SetStateAndData(false, nil)
end end
+39 -30
View File
@@ -17,6 +17,11 @@
local omif = select(2, ...) local omif = select(2, ...)
local types = omif.GetModule("types") 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 ---@class StatusBar
local StatusBar = types.StatusBar local StatusBar = types.StatusBar
@@ -45,12 +50,6 @@ local colors = {
low = { 1.0, 0.0, 0 }, low = { 1.0, 0.0, 0 },
offline = { 0.7, 0.7, 0.7 }, offline = { 0.7, 0.7, 0.7 },
dead = { 0.7, 0.7, 0.7 }, dead = { 0.7, 0.7, 0.7 },
magic = { 0.4, 0.4, 1.0 },
disease = { 0.4, 0.2, 0.0 },
poison = { 0.0, 0.7, 0.7 },
curse = { 0.7, 0.0, 0.7 },
immune = { 0.0, 0.2, 0.4 },
bomb = { 1.0, 0.7, 0.7 },
cyan = { 0.0, 0.8, 0.8 }, cyan = { 0.0, 0.8, 0.8 },
white = { 1.0, 1.0, 1.0 }, white = { 1.0, 1.0, 1.0 },
shield = { 0.85, 0.95, 1.0 }, shield = { 0.85, 0.95, 1.0 },
@@ -85,6 +84,8 @@ function UnitFrame:Init(unit, config)
self.rangeEnemy = config.range.enemy self.rangeEnemy = config.range.enemy
self.rangeFade = config.range.fade self.rangeFade = config.range.fade
self.statusColors = config.statusColors
local secure = self:CreateSecureFrame(width, height) local secure = self:CreateSecureFrame(width, height)
secure:Hide() secure:Hide()
@@ -101,13 +102,13 @@ function UnitFrame:Init(unit, config)
self.absorb = StatusBar:new(self, width, height, 1, true, texture_top) self.absorb = StatusBar:new(self, width, height, 1, true, texture_top)
self.absorb:SetColor(unpack(colors.absorb)) 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 = StatusBar:new(self, width, height, 1, true, texture_bot)
self.shield:SetColor(unpack(colors.shield)) self.shield:SetColor(unpack(colors.shield))
self.shield:SetRange(0, 1) self.shield:SetRange(0, 1)
self.shield:SetValue(0.6) self.shield:SetValue(0.6)
self.shield:SetFillStyle("REVERSE") self.shield:SetFillStyle(Enum.StatusBarFillStyle.Reverse)
self.auras = AuraList:new(self) self.auras = AuraList:new(self)
@@ -182,10 +183,8 @@ function UnitFrame:CreateName()
local name = self.overlays:CreateFontString(nil, "BACKGROUND") local name = self.overlays:CreateFontString(nil, "BACKGROUND")
self.name = name 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:SetTextColor(0.8, 0.6, 0.1)
name:SetShadowColor(0, 0, 0)
name:SetShadowOffset(1, -1)
name:SetPoint("CENTER") name:SetPoint("CENTER")
name:SetText("") name:SetText("")
name:Show() name:Show()
@@ -389,10 +388,9 @@ function UnitFrame:Disable()
if self.hideInRaid then if self.hideInRaid then
UnregisterAttributeDriver(self.secureFrame, "state-visibility") UnregisterAttributeDriver(self.secureFrame, "state-visibility")
elseif self.unit ~= "player" then elseif self.unit ~= "player" then
UnregisterUnitWatch(self.secure_frame, self.condition) UnregisterUnitWatch(self.secureFrame, false)
else
self.secureFrame:Hide()
end end
self.secureFrame:Hide()
end end
-- returns true if this unit is a target unit -- returns true if this unit is a target unit
@@ -539,10 +537,20 @@ function UnitFrame:UNIT_POWER_UPDATE()
self:UpdatePower() self:UpdatePower()
end end
function UnitFrame:UpdateRole(role) function UnitFrame:GetRole(role)
if role == nil then if role == nil then
role = UnitGroupRolesAssigned(self.unit) role = UnitGroupRolesAssigned(self.unit)
end end
if GetPartyAssignment("MAINTANK", self.unit) then
role = "TANK"
end
return role
end
function UnitFrame:UpdateRole(role)
role = self:GetRole(role)
if role == "HEALER" then if role == "HEALER" then
self:EnablePower(Enum.PowerType.Mana) self:EnablePower(Enum.PowerType.Mana)
elseif role == "TANK" and select(3, UnitClass(self.unit)) == 6 then elseif role == "TANK" and select(3, UnitClass(self.unit)) == 6 then
@@ -650,23 +658,24 @@ function UnitFrame:UpdateHealthColor()
if not isFriend then if not isFriend then
self.hp:SetColor(unpack(colors.hostile)) self.hp:SetColor(unpack(colors.hostile))
elseif self.auras.statusCount["Immune"] then return
self.hp:SetColor(unpack(colors.immune)) end
elseif self.auras.statusCount["Bomb"] then
self.hp:SetColor(unpack(colors.bomb)) local statusColor = nil
elseif self.auras.statusCount["Magic"] then for _, entry in ipairs(self.statusColors) do
self.hp:SetColor(unpack(colors.magic)) if self.auras.statusCount[entry.status] then
elseif self.auras.statusCount["Curse"] then statusColor = entry.color
self.hp:SetColor(unpack(colors.curse)) break
elseif self.auras.statusCount["Poison"] then end
self.hp:SetColor(unpack(colors.poison)) end
elseif self.auras.statusCount["Disease"] then
self.hp:SetColor(unpack(colors.disease)) if statusColor then
elseif isFriend and UnitIsDead(unit) then self.hp:SetColor(unpack(statusColor))
elseif UnitIsDead(unit) then
self.hp:SetColor(unpack(colors.dead)) self.hp:SetColor(unpack(colors.dead))
elseif isFriend and pct >= 0.90 then elseif pct >= 0.90 then
self.hp:SetColor(unpack(colors.healthy)) self.hp:SetColor(unpack(colors.healthy))
elseif isFriend and pct >= 0.75 then elseif pct >= 0.75 then
local progress = (pct - 0.75) / (0.90 - 0.75) local progress = (pct - 0.75) / (0.90 - 0.75)
self.hp:SetInterpolatedColor(colors.mid, colors.high, progress) self.hp:SetInterpolatedColor(colors.mid, colors.high, progress)
else else
+14 -13
View File
@@ -32,9 +32,10 @@ local function RoleValue(role)
end end
local function UnitFrameDefaultCompare(lhs, rhs) local function UnitFrameDefaultCompare(lhs, rhs)
-- visible < invisible -- exists < not exists
if lhs.secureFrame:IsShown() ~= rhs.secureFrame:IsShown() then local lexists, rexists = UnitExists(lhs.unit), UnitExists(rhs.unit)
return lhs.secureFrame:IsShown() if lexists ~= rexists then
return lexists
end end
local lunit, runit = lhs.unit, rhs.unit local lunit, runit = lhs.unit, rhs.unit
@@ -52,8 +53,8 @@ local function UnitFrameDefaultCompare(lhs, rhs)
end end
-- tank < healer < damage < ? -- tank < healer < damage < ?
local lrole = RoleValue(UnitGroupRolesAssigned(lunit)) local lrole = RoleValue(lhs:GetRole())
local rrole = RoleValue(UnitGroupRolesAssigned(runit)) local rrole = RoleValue(rhs:GetRole())
if lrole ~= rrole then if lrole ~= rrole then
return lrole < rrole return lrole < rrole
@@ -69,7 +70,6 @@ function UnitGroup:Init(left, top, width, height)
self.top = top self.top = top
self.left = left self.left = left
self.units = {} self.units = {}
self.sortAfterCombat = false
self:RegisterEvents() self:RegisterEvents()
end end
@@ -84,6 +84,7 @@ function UnitGroup:RegisterEvents()
frame:RegisterEvent("GROUP_ROSTER_UPDATE") frame:RegisterEvent("GROUP_ROSTER_UPDATE")
frame:RegisterEvent("PLAYER_REGEN_ENABLED") frame:RegisterEvent("PLAYER_REGEN_ENABLED")
frame:RegisterEvent("PLAYER_ENTERING_WORLD") frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("UNIT_PET")
end end
function UnitGroup:GROUP_ROSTER_UPDATE() function UnitGroup:GROUP_ROSTER_UPDATE()
@@ -94,23 +95,23 @@ function UnitGroup:PLAYER_ENTERING_WORLD()
self:OnRosterUpdate() self:OnRosterUpdate()
end end
function UnitGroup:UNIT_PET()
self:OnRosterUpdate()
end
function UnitGroup:PLAYER_REGEN_ENABLED() function UnitGroup:PLAYER_REGEN_ENABLED()
self:OnLeaveCombat() self:OnLeaveCombat()
end end
function UnitGroup:OnLeaveCombat() function UnitGroup:OnLeaveCombat()
if self.sortAfterCombat then self:Sort()
self:Sort()
self.sortAfterCombat = false
end
end end
function UnitGroup:OnRosterUpdate() function UnitGroup:OnRosterUpdate()
if InCombatLockdown() then if InCombatLockdown() then
self.sortAfterCombat = true return
else
self:Sort()
end end
self:Sort()
end end
function UnitGroup:AddUnitFrame(unit) function UnitGroup:AddUnitFrame(unit)