Compare commits

...
Author SHA1 Message Date
omicron 289a555cee Fix font and status bars to use new API arguments 2026-10-28 18:24:52 +01:00
omicron eaebe470b9 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-06-26 01:25:10 +02:00
omicron 8be0732930 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-06-25 21:58:04 +02:00
omicron 86e5ed78b0 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-06-24 00:37:15 +02:00
omicron 77ae4ddad3 Make UnitGroup trigger sort on UNIT_PET 2026-06-20 00:24:18 +02:00
omicron 5ac84782d2 Add raid pets to the hardcoded configuration 2026-06-20 00:21:25 +02:00
omicron 24a72cb02f 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-03-18 02:41:39 +01:00
omicron 851fb5740b 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-03-18 01:29:43 +01:00
omicron b9d16f5ba2 update config for TBC shaman play 2026-03-18 01:27:07 +01:00
omicron 90d71a54cf temporary enhancement hack: show debuffs in a suboptimal way
In new dungeons you don't really know what debuffs you want to see so
it's hard to configure them. To deal with this I added a quick but
suboptimal feature to show up to 5 debuffs automatically on the unit
frames. It's not great, but it's not terrible either. A hardcoded list
of debuffs to ignore is added too.
2026-03-07 13:21:12 +01:00
omicron abeac379f3 Enhance IconIndicator with tooltips, fix stacks/applications mixup 2026-03-07 13:17:25 +01:00
omicron 763bf0bdae Remove old unused textures for squares 2025-08-24 12:37:22 +02:00
omicron f181c0022c Apply stylua to the entire codebase 2025-08-24 12:28:06 +02:00
omicron 0cced419a6 Make devloop do an initial install on first run 2025-08-24 12:28:06 +02:00
omicron 5bbbde3ca7 Update default configuration
This comprises many different configuration changes over a longer time
for multiple classes.
2025-08-24 12:28:06 +02:00
omicron 6f954b9d15 Add more immunity and burn spells to auralist 2025-08-24 12:28:06 +02:00
omicron c56b847710 Add IconIndicator
Similar functionality to a SquareIndicator but uses an icon instead of a
solid color.
2025-08-24 12:28:06 +02:00
omicron dc62b031a9 Update UnitAuraSlots API for TWW 2025-08-24 12:28:06 +02:00
omicron ec7e2c74ff Update range check API to for TWW 2025-08-24 12:28:06 +02:00
omicron b1dca1639d Properly update unit frames on GROUP_ROSTER_UPDATE 2025-08-24 12:28:06 +02:00
omicron 47542c7147 Expand config with more auras 2025-08-24 12:28:06 +02:00
18 changed files with 876 additions and 176 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
indent_type = "Spaces"
+4
View File
@@ -11,6 +11,10 @@ if [[ ! -d "${WOW_ADDON_DIR}" ]]; then
exit 1 exit 1
fi fi
# Initial build / install
make -s build
rsync -q -a ./build/OmicronFrames "${WOW_ADDON_DIR}/" --delete
# Every time the source or texture directory tree is changed, wait 1 second and # Every time the source or texture directory tree is changed, wait 1 second and
# build the addon, then install it to the directory passed on the command line # build the addon, then install it to the directory passed on the command line
while true; do while true; do
Binary file not shown.
Binary file not shown.
+1
View File
@@ -17,6 +17,7 @@ types/statusbar.lua
types/auralist.lua types/auralist.lua
types/indicators/indicator.lua types/indicators/indicator.lua
types/indicators/squareindicator.lua types/indicators/squareindicator.lua
types/indicators/iconindicator.lua
types/indicators/borderindicator.lua types/indicators/borderindicator.lua
types/unitgroup.lua types/unitgroup.lua
types/unitframe.lua types/unitframe.lua
+370 -79
View File
@@ -21,32 +21,46 @@ 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 },
red = { 1, 0, 0 }, red = { 1, 0, 0 },
blue = { .4, 0.4, 1 }, blue = { 0.4, 0.4, 1 },
light_blue = { .7, .7, 1 }, light_blue = { 0.7, 0.7, 1 },
} }
---@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 function RangeConfig() local function RangeConfig()
local _, class = UnitClass("player") local _, class = UnitClass("player")
if class == "SHAMAN" then if class == "SHAMAN" then
return { return {
friendly = "Healing Surge", friendly = "Healing Wave",
enemy = "Lightning Bolt", enemy = "Lightning Bolt",
fade = 0.2 fade = 0.2,
} }
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,
} }
else else
return { return {
fade = 0.2 fade = 0.2,
} }
end end
end end
@@ -61,22 +75,41 @@ local function MouseConfig()
button = "mouse2", button = "mouse2",
mods = {}, mods = {},
kind = "macro", kind = "macro",
data = data = "/use [@UNIT,dead,help]Ancestral Spirit; [@UNIT,help]Chain Heal",
"/use [@UNIT,dead,help]Ancestral Vision; [@UNIT,help]Chain Heal"
}, },
{ {
button = "mouse3", button = "mouse3",
mods = {}, mods = {},
kind = "macro", kind = "macro",
data = data = "/use [@UNIT,help]Cure Poison",
"/use [@UNIT,dead,help]Ancestral Spirit; [@UNIT,help]Purify Spirit" },
{
button = "wheel-up",
mods = {},
kind = "macro",
data = "/use [@UNIT,help]Lesser Healing Wave",
},
{
button = "wheel-down",
mods = {},
kind = "macro",
data = "/use [@UNIT,help]Healing Wave",
}, },
{ button = "wheel-up", mods = {}, kind = "macro", data = "/use [@UNIT,help]Healing Surge" },
{ 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" },
@@ -84,8 +117,13 @@ local function MouseConfig()
button = "wheel-up", button = "wheel-up",
mods = { shift = true }, mods = { shift = true },
kind = "macro", kind = "macro",
data = data = "/cast [@UNIT,help]Water Walking;\n/stopspelltarget",
"/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
@@ -96,23 +134,28 @@ local function MouseConfig()
button = "mouse2", button = "mouse2",
mods = {}, mods = {},
kind = "macro", kind = "macro",
data = "/use [@UNIT,dead,help]Mass Resurrection; [@UNIT,help]Power Word: Radiance" data = "/use [@UNIT,dead,help]Mass Resurrection; [@UNIT,help]Power Word: Radiance",
}, },
{ {
button = "mouse3", button = "mouse3",
mods = {}, mods = {},
kind = "macro", kind = "macro",
data = "/use [@UNIT,dead,help]Resurrection; [@UNIT,help]Purify" data = "/use [@UNIT,dead,help]Resurrection; [@UNIT,help]Purify",
}, },
{ button = "mouse4", mods = {}, kind = "spell", data = "Shadow Covenant" }, { button = "mouse4", mods = {}, kind = "spell", data = "Shadow Covenant" },
{ button = "wheel-up", mods = {}, kind = "macro", data = "/use [@UNIT,help]Flash Heal" }, {
{ button = "wheel-down", mods = {}, kind = "macro", data = "/use [@UNIT,help]Renew" }, button = "wheel-up",
mods = {},
kind = "macro",
data = "/use [@UNIT,help]Lesser Heal",
},
{ 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
@@ -121,13 +164,13 @@ local function MouseConfig()
button = "wheel-up", button = "wheel-up",
mods = { shift = true }, mods = { shift = true },
kind = "macro", kind = "macro",
data = "/cast [@UNIT,help]Levitate;\n/stopspelltarget" data = "/cast [@UNIT,help]Levitate;\n/stopspelltarget",
}, },
{ {
button = "wheel-down", button = "wheel-down",
mods = { shift = true }, mods = { shift = true },
kind = "macro", kind = "macro",
data = "/cast [@UNIT,help]Leap of Faith;\n/stopspelltarget" data = "/cast [@UNIT,help]Leap of Faith;\n/stopspelltarget",
}, },
} }
elseif class == "MONK" then elseif class == "MONK" then
@@ -138,19 +181,26 @@ local function MouseConfig()
button = "mouse2", button = "mouse2",
mods = {}, mods = {},
kind = "macro", kind = "macro",
data = ("/use [@UNIT,known:Reawaken,dead,help]Reawaken; " data = (
"/use [@UNIT,known:Reawaken,dead,help]Reawaken; "
.. "[@UNIT,dead,help]Resuscitate; " .. "[@UNIT,dead,help]Resuscitate; "
.. "[@UNIT,help]Tiger's Lust"), .. "[@UNIT,help]Tiger's Lust"
),
}, },
{ {
button = "mouse3", button = "mouse3",
mods = {}, mods = {},
kind = "macro", kind = "macro",
data = "/use [@UNIT,dead,help]Resuscitate; [@UNIT,help]Detox" data = "/use [@UNIT,dead,help]Resuscitate; [@UNIT,help]Detox",
}, },
{ button = "mouse4", mods = {}, kind = "spell", data = "Shadow Covenant" }, { button = "mouse4", mods = {}, kind = "spell", data = "Shadow Covenant" },
{ button = "wheel-up", mods = {}, kind = "macro", data = "/use [@UNIT,help]Vivify" }, { button = "wheel-up", mods = {}, kind = "macro", data = "/use [@UNIT,help]Vivify" },
{ button = "wheel-down", mods = {}, kind = "macro", data = "/use [@UNIT,help]Soothing Mist" }, {
button = "wheel-down",
mods = {},
kind = "macro",
data = "/use [@UNIT,help]Soothing Mist",
},
-- alt -- alt
{ button = "mouse1", mods = { alt = true }, kind = "spell", data = "Power Word: Life" }, { button = "mouse1", mods = { alt = true }, kind = "spell", data = "Power Word: Life" },
@@ -165,15 +215,71 @@ local function MouseConfig()
button = "wheel-up", button = "wheel-up",
mods = { shift = true }, mods = { shift = true },
kind = "macro", kind = "macro",
data = "/cast [@UNIT,help]Levitate;\n/stopspelltarget" data = "/cast [@UNIT,help]Levitate;\n/stopspelltarget",
}, },
{ {
button = "wheel-down", button = "wheel-down",
mods = { shift = true }, mods = { shift = true },
kind = "macro", kind = "macro",
data = "/cast [@UNIT,help]Leap of Faith;\n/stopspelltarget" data = "/cast [@UNIT,help]Leap of Faith;\n/stopspelltarget",
}, },
} }
elseif class == "PALADIN" then
return {
-- No modifier
{ button = "mouse1", mods = {}, kind = "target" },
{
button = "mouse2",
mods = {},
kind = "macro",
data = (
"/use [@UNIT,combat,dead,help]Intercession; "
.. "[@UNIT,known:Absolution,dead,help]Absolution; "
.. "[@UNIT,dead,help]Redemption; "
.. "[@UNIT,help]Blessing of Freedom"
),
},
{
button = "mouse3",
mods = {},
kind = "macro",
data = "/use [@UNIT,dead,help]Redemption; [@UNIT,help]Cleanse Toxins",
},
{
button = "mouse4",
mods = {},
kind = "spell",
data = "Word of Glory",
},
{
button = "mouse5",
mods = {},
kind = "spell",
data = "Blessing of Summer",
},
{
button = "wheel-up",
mods = {},
kind = "macro",
data = "/use [@UNIT,help]Flash of Light",
},
{
button = "wheel-down",
mods = {},
kind = "macro",
data = "/use [@UNIT,help,known:Holy Shock]Holy Shock; [@UNIT,help]Word of Glory",
},
-- Shift
{ button = "mouse2", mods = { shift = true }, kind = "togglemenu" },
-- alt
{ button = "mouse1", mods = { alt = true }, kind = "spell", data = "Blessing of Sacrifice" },
{ button = "mouse2", mods = { alt = true }, kind = "spell", data = "Blessing of Protection" },
{ button = "mouse3", mods = { alt = true }, kind = "spell", data = "Blessing of Spellwarding" },
{ button = "wheel-up", mods = { alt = true }, kind = "spell", data = "Holy Light" },
{ button = "wheel-down", mods = { alt = true }, kind = "spell", data = "Lay on Hands" },
}
else else
return { return {
-- Super basic defaults -- Super basic defaults
@@ -189,7 +295,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",
@@ -199,35 +305,35 @@ local function TriggerClassConfig()
y = -3, y = -3,
color = colors.white, color = colors.white,
showStacks = true, showStacks = true,
} },
}, },
{ {
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 = "SquareIndicator", kind = "SquareIndicator",
size = 17, size = 17,
point = "BOTTOMLEFT", point = "TOPLEFT",
x = 3, x = 40,
y = 3, y = -3,
color = colors.cyan, color = colors.orange,
fadeTime = 10.0, showStacks = false,
} },
}, },
{ {
kind = "StatusTrigger", kind = "StatusTrigger",
@@ -238,14 +344,14 @@ local function TriggerClassConfig()
thickness = 3.0, thickness = 3.0,
color = colors.red, color = colors.red,
level = 0, level = 0,
} },
}, },
} }
elseif class == "PRIEST" then elseif class == "PRIEST" then
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",
@@ -255,11 +361,11 @@ local function TriggerClassConfig()
y = -3, y = -3,
color = colors.white, color = colors.white,
fadeTime = 10.0, fadeTime = 10.0,
} },
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 33206, spellIds = set({ 33206 }),
own = true, -- Pain Suppression own = true, -- Pain Suppression
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -270,11 +376,11 @@ local function TriggerClassConfig()
color = colors.orange, color = colors.orange,
fadeTime = 10.0, fadeTime = 10.0,
flashTime = 1.3, flashTime = 1.3,
} },
}, },
{ {
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",
@@ -284,11 +390,11 @@ local function TriggerClassConfig()
y = -3, y = -3,
color = { 0.7, 0.9, 1 }, color = { 0.7, 0.9, 1 },
showStacks = true, showStacks = true,
} },
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 139, spellIds = AURAS_PRIEST_RENEW,
own = true, -- Renew own = true, -- Renew
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -298,11 +404,11 @@ local function TriggerClassConfig()
y = 3, y = 3,
color = colors.cyan, color = colors.cyan,
fadeTime = 10.0, fadeTime = 10.0,
} },
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 194384, spellIds = set({ 194384 }),
own = true, -- Atonement own = true, -- Atonement
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -313,11 +419,11 @@ local function TriggerClassConfig()
color = colors.white, color = colors.white,
fadeTime = 10.0, fadeTime = 10.0,
flashTime = 1.3, flashTime = 1.3,
} },
}, },
{ {
kind = "AuraTrigger", kind = "AuraTrigger",
spellId = 10060, spellIds = set({ 10060 }),
own = true, -- Power Infusion own = true, -- Power Infusion
indicator = { indicator = {
kind = "SquareIndicator", kind = "SquareIndicator",
@@ -327,7 +433,7 @@ local function TriggerClassConfig()
y = 3, y = 3,
color = colors.violet, color = colors.violet,
fadeTime = 10.0, fadeTime = 10.0,
} },
}, },
{ {
kind = "StatusTrigger", kind = "StatusTrigger",
@@ -338,26 +444,161 @@ local function TriggerClassConfig()
thickness = 3.0, thickness = 3.0,
color = colors.red, color = colors.red,
level = 0, level = 0,
},
},
} }
}, elseif class == "PALADIN" then
--[[{ return {
kind="MultiTrigger", invert=false, {
children = { kind = "AuraTrigger",
{kind="StatusTrigger", status="Immune", defaultData={color=colors.violet}}, -- Renew spellIds = set({ 287280 }), -- Glimmer of Light
{kind="AuraTrigger", spellId=139, own=true, defaultData={color=colors.cyan}}, -- Renew own = true,
{kind="AuraTrigger", spellId=21562, own=true, defaultData={color=colors.white}}, -- pw:f
},
indicator = { indicator = {
kind="SquareIndicator", kind = "SquareIndicator",
size=17, size = 17,
point="TOPRIGHT", point = "TOPLEFT",
x=-3, y=-3, x = 3,
color=colors.white, y = -3,
} color = colors.white,
}--]] fadeTime = 10.0,
flashTime = 1.3,
},
},
{
kind = "AuraTrigger",
spellIds = set({ 25771 }), -- Forbearance
indicator = {
kind = "SquareIndicator",
size = 17,
point = "TOPLEFT",
x = 22,
y = -3,
color = colors.red,
fadeTime = 10.0,
flashTime = 1.3,
},
},
{
kind = "AuraTrigger",
spellIds = set({ 53563 }), -- Beacon of Light
own = true,
indicator = {
kind = "SquareIndicator",
size = 17,
point = "TOPLEFT",
x = 41,
y = -3,
color = colors.white,
},
},
{
kind = "AuraTrigger",
spellIds = set({ 156910 }), -- Beacon of Faith
own = true,
indicator = {
kind = "SquareIndicator",
size = 17,
point = "TOPLEFT",
x = 41,
y = -3,
color = colors.light_blue,
},
},
{
kind = "AuraTrigger",
spellIds = set({ 200025 }), -- Beacon of Virtue
own = true,
indicator = {
kind = "SquareIndicator",
size = 17,
point = "TOPLEFT",
x = 41,
y = -3,
color = colors.white,
},
},
{
kind = "AuraTrigger",
spellIds = set({ 1044 }), -- Blessing of Freedom
own = true,
indicator = {
kind = "SquareIndicator",
size = 17,
point = "BOTTOMLEFT",
x = 3,
y = 3,
color = colors.orange,
flashTime = 1.3,
},
},
{
kind = "AuraTrigger",
spellIds = set({ 1111 }), -- Blessing of Spellwarding
own = true,
indicator = {
kind = "SquareIndicator",
size = 17,
point = "BOTTOMLEFT",
x = 41,
y = 3,
color = colors.cyan,
flashTime = 1.3,
},
},
{
kind = "AuraTrigger",
spellIds = set({ 1022 }), -- Blessing of Protection
own = true,
indicator = {
kind = "SquareIndicator",
size = 17,
point = "BOTTOMLEFT",
x = 41,
y = 3,
color = colors.cyan,
flashTime = 1.3,
},
},
{
kind = "AuraTrigger",
spellIds = set({ 6940 }), -- Blessing of Saccrifice
own = true,
indicator = {
kind = "SquareIndicator",
size = 17,
point = "BOTTOMLEFT",
x = 61,
y = 3,
color = colors.red,
flashTime = 1.3,
},
},
{
kind = "StatusTrigger",
status = "Burn",
invert = false,
indicator = {
kind = "BorderIndicator",
thickness = 3.0,
color = colors.red,
level = 0,
},
},
} }
else else
return {} return {
{
kind = "StatusTrigger",
status = "Burn",
invert = false,
indicator = {
kind = "BorderIndicator",
thickness = 3.0,
color = colors.red,
level = 0,
},
},
}
end end
end end
@@ -368,7 +609,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,
@@ -377,12 +618,13 @@ local function MakeDebuffTrigger(slot, spellid, color, stacks)
y = -3, y = -3,
color = color, color = color,
showStacks = stacks, showStacks = stacks,
} },
} }
end end
local function TriggerConfig() local function TriggerConfig()
local triggers = TriggerClassConfig() local triggers = TriggerClassConfig()
print("Class triggers:", #triggers)
-- slot, spellid, color, stacks -- slot, spellid, color, stacks
local debuffs = { local debuffs = {
@@ -411,11 +653,54 @@ local function TriggerConfig()
{ 3, 408429, colors.violet }, -- Void Slash (P3 tank) { 3, 408429, colors.violet }, -- Void Slash (P3 tank)
{ 2, 404218, colors.blue }, -- Void Fracture (carry bomb) { 2, 404218, colors.blue }, -- Void Fracture (carry bomb)
{ 1, 401951, colors.cyan, true }, -- Oblivion { 1, 401951, colors.cyan, true }, -- Oblivion
-- Darkheart Thicket
{ 3, 225484, colors.violet }, -- Grievous Rip
{ 3, 196376, colors.violet }, -- Grievous Tear
-- Throne of the Tides
{ 1, 426660, colors.red, true }, -- Razor Jaws
-- Nymue, Amirdrassil
{ 1, 429785, colors.red }, -- Loom (line stun)
{ 1, 417807, colors.red, true }, -- Fyrakk
-- Algeth'ar Academy
{ 1, 389033, colors.poison, true },
{ 1, 388912, colors.red, true },
{ 2, 391977, colors.cyan, true },
{ 2, 389011, colors.cyan, true },
-- Nokhud Offensive
{ 1, 381692, colors.red, true },
-- Tazavesh
{ 1, 1240102, colors.orange, true },
-- Priory of the Sacred Flame
{ 1, 424414, colors.violet, true },
} }
for _, debuff in ipairs(debuffs) do for _, debuff in ipairs(debuffs) do
table.insert(triggers, MakeDebuffTrigger(debuff[1], debuff[2], debuff[3], debuff[4])) table.insert(triggers, MakeDebuffTrigger(debuff[1], debuff[2], debuff[3], debuff[4]))
end end
print("All triggers:", #triggers)
-- Some more debuffs in the bottom row
table.insert(triggers, {
kind = "AuraTrigger",
spellIds = set({ 240443 }), -- Bursting
indicator = {
kind = "SquareIndicator",
size = 17,
point = "BOTTOMRIGHT",
x = -3,
y = 3,
color = colors.red,
fadeTime = 3.0,
showStacks = true,
},
})
return triggers return triggers
end end
@@ -433,7 +718,7 @@ function HideBlizzardFrames()
TargetFrame, TargetFrame,
FocusFrame, FocusFrame,
PartyFrame, PartyFrame,
CompactRaidFrameContainer CompactRaidFrameContainer,
} }
for _, frame in ipairs(blizzardFrames) do for _, frame in ipairs(blizzardFrames) do
@@ -452,6 +737,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
@@ -462,9 +749,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
@@ -494,10 +785,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
+5 -3
View File
@@ -88,7 +88,7 @@ Commander.RegisterCommand("omi-pstart", {
PrintLn("To enable the script profiler run `/console scriptProfile 1`, then reload the UI.") PrintLn("To enable the script profiler run `/console scriptProfile 1`, then reload the UI.")
return return
end end
end end,
}) })
Commander.RegisterCommand("omi-pstop", { Commander.RegisterCommand("omi-pstop", {
@@ -117,7 +117,9 @@ Commander.RegisterCommand("omi-pstop", {
time = time * 1000, time = time * 1000,
}) })
end end
table.sort(data, function(a, b) return a.time < b.time end) table.sort(data, function(a, b)
return a.time < b.time
end)
end, end,
}) })
@@ -136,5 +138,5 @@ Commander.RegisterCommand("omi-pprint", {
local ms_per_sec = time / total local ms_per_sec = time / total
Printf("% 6.1f%% % 7.2fms %7.2fms/s %s\n", pct, time, ms_per_sec, item.name) Printf("% 6.1f%% % 7.2fms %7.2fms/s %s\n", pct, time, ms_per_sec, item.name)
end end
end end,
}) })
+53 -15
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
@@ -41,6 +43,7 @@ local statusLists = {
[411241] = true, -- Sarkareth, Void Claws (p2 tank) [411241] = true, -- Sarkareth, Void Claws (p2 tank)
[408429] = true, -- Sarkareth, Void Slash (p3 tank) [408429] = true, -- Sarkareth, Void Slash (p3 tank)
[404218] = true, -- Sarkareth, Void fracture (bombs) [404218] = true, -- Sarkareth, Void fracture (bombs)
[427722] = true, -- Nymue, Weaver's Burden (hehe jk it's private because reasons)
}, },
Burn = { Burn = {
-- Jade Serpent Temple -- Jade Serpent Temple
@@ -68,6 +71,8 @@ local statusLists = {
-- Atal'Dazar -- Atal'Dazar
[255582] = true, -- Priestess Alun'za, Molten Gold [255582] = true, -- Priestess Alun'za, Molten Gold
[250096] = true, -- Yazma, Wracking pain
[255434] = true, -- Rezan, Serrated Teeth
-- The MOTHERLODE!! -- The MOTHERLODE!!
[259853] = true, -- Rixxa Fluxflame, Chemical Burn [259853] = true, -- Rixxa Fluxflame, Chemical Burn
@@ -104,6 +109,27 @@ local statusLists = {
-- Aberrus -- Aberrus
[404010] = true, -- Zkarn, ??? [404010] = true, -- Zkarn, ???
[405462] = true, -- Zkarn, ??? [405462] = true, -- Zkarn, ???
-- Dawn of the infinite: Galakrond's fall
[407406] = true,
-- Darkheart Thicket
-- Amirdrassil
[427721] = true, -- Nymue, Weaver's burden (dot part)
-- Waycrest Manor
[263943] = true, -- Etch
[264378] = true, -- Fragment Soul
-- Ruby Lifepools
[381862] = true, -- Kyrakka, Infernocore
-- Nokhud Offensive
[381692] = true, -- Swift Stab
-- Priory of the Sacred Flame
[447439] = true, -- Savage Mauling
}, },
} }
@@ -111,7 +137,7 @@ local statusLists = {
-- callback function with UnitAuraInfo table as argument -- callback function with UnitAuraInfo table as argument
local function ForEachAuraSlots(unit, fn, continuationToken, ...) local function ForEachAuraSlots(unit, fn, continuationToken, ...)
local GetAuraDataBySlot = C_UnitAuras.GetAuraDataBySlot local GetAuraDataBySlot = C_UnitAuras.GetAuraDataBySlot
local n = select('#', ...) local n = select("#", ...)
for i = 1, n do for i = 1, n do
local slot = select(i, ...) local slot = select(i, ...)
fn(GetAuraDataBySlot(unit, slot)) fn(GetAuraDataBySlot(unit, slot))
@@ -121,11 +147,11 @@ end
-- Helper function that goes over all aura slots for a given filter -- Helper function that goes over all aura slots for a given filter
local function ForEachAuraFiltered(unit, filter, fn) local function ForEachAuraFiltered(unit, filter, fn)
local UnitAuraSlots = UnitAuraSlots local UnitAuraSlots = C_UnitAuras.GetAuraSlots
local continuationToken = nil local continuationToken = nil
repeat repeat
continuationToken = ForEachAuraSlots(unit, fn, UnitAuraSlots(unit, filter, nil, continuationToken)) continuationToken = ForEachAuraSlots(unit, fn, UnitAuraSlots(unit, filter, nil, continuationToken))
until (continuationToken == nil) until continuationToken == nil
end end
-- Similar to what AuraUtils.ForEachAura does except the callback fn takes a -- Similar to what AuraUtils.ForEachAura does except the callback fn takes a
@@ -135,7 +161,6 @@ local function ForEachAura(unit, fn)
ForEachAuraFiltered(unit, "HARMFUL", fn) ForEachAuraFiltered(unit, "HARMFUL", fn)
end end
---@return AuraList ---@return AuraList
function AuraList.new(cls, ...) function AuraList.new(cls, ...)
--- I really dislike duplicating this everywhere but it makes --- I really dislike duplicating this everywhere but it makes
@@ -152,27 +177,40 @@ function AuraList:Init(unitframe)
self.statusCount = {} self.statusCount = {}
self.auras = {} -- map AuraInstanceID to UnitAuraInfo table self.auras = {} -- map AuraInstanceID to UnitAuraInfo table
self.triggers = {} self.triggers = {}
self.triggersBySpell = {} self.triggersBySpell = {}
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.triggersBySpell[spellId] == nil then
self.triggersBySpell[spellId] = {}
end
table.insert(self.triggersBySpell[spellId], trigger)
else
if self.triggersByStatus[status] == nil then if self.triggersByStatus[status] == nil then
self.triggersByStatus[status] = {} self.triggersByStatus[status] = {}
end end
table.insert(self.triggersByStatus[status], trigger) 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
error("AddTrigger: trigger is neither an AuraTrigger nor a StatusTrigger")
end end
end end
@@ -247,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
+215
View File
@@ -0,0 +1,215 @@
-- Copyright 2025 <omicron.me@protonmail.com>
--
-- This file is part of Omicron Frames
--
-- Omicron Frames is free software: you can redistribute it and/or modify it
-- under the terms of the GNU General Public License as published by the Free
-- Software Foundation, either version 3 of the License, or (at your option)
-- any later version.
--
-- Omicron Frames is distributed in the hope that it will be useful, but
-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-- more details.
--
-- You should have received a copy of the GNU General Public License along with
-- Omicron Frames. If not, see <https://www.gnu.org/licenses/>.
local omi = select(2, ...)
local types = omi.GetModule("types")
local iconPool = CreateFramePool("Frame", nil, "BackdropTemplate")
---IconIndicator is an indicator that displays an icon with border
---@class IconIndicator: Indicator
local IconIndicator = types.CreateClass("IconIndicator", Indicator)
types.IconIndicator = IconIndicator
---@return IconIndicator
function IconIndicator.new(cls, ...)
--- I really dislike duplicating this everywhere but it makes
--lua-language-server able to deduce the type of Object:new calls and that
--is honestly worth it
return types.Object.new(cls, ...)
end
--- Initialize a new IconIndicator
-- unitframe Unitframe it is attached to
-- size Size of the indicator square
-- point Attachment point to the unitframe's overlay frame
-- x, y x and y offset for the attachment point
-- color Color of the square
---@param unitframe UnitFrame
---@param size number
---@param point string
---@param x number
---@param y number
---@param showStacks boolean
---@param doFade nil|number
---@param doFlash nil|number
---@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
self.point = point
self.x = x
self.y = y
self.icon = 134400
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.applications or 0
self.auraInstanceID = data.auraInstanceID
self.spellId = data.spellId
self.frame = frame
self.stacks = stacks
if self.showStacks and numStacks > 0 then
stacks:SetText(numStacks)
stacks:Show()
end
if self.flashTime or self.fadeTime then
self.frame:SetScript("OnUpdate", function(frame, dt)
self:OnUpdate(dt)
end)
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
function IconIndicator:SetIcon(icon)
self.icon = icon
local frame = self.frame
if frame then
frame.iconTexture:SetTexture(icon)
end
end
function IconIndicator:GetFrame()
local frame, new = iconPool:Acquire()
local stacks
if new then
local iconTexture = frame:CreateTexture(nil, "BACKGROUND")
iconTexture:SetAllPoints()
frame.iconTexture = iconTexture
frame.backdropInfo = {
bgFile = nil,
edgeFile = "Interface\\Addons\\OmicronFrames\\media\\textures\\pixel_edge",
edgeSize = 2,
insets = { left = 0, right = 0, top = 0, bottom = 0 },
}
frame:ApplyBackdrop()
stacks = frame:CreateFontString(nil, "OVERLAY")
frame.stacks = stacks
stacks:SetFont("Interface\\AddOns\\OmicronFrames\\media\\fonts\\roboto\\Roboto-Medium.ttf", 12, "OUTLINE")
stacks:SetPoint("CENTER")
stacks:SetTextColor(1, 1, 1)
end
frame:SetSize(self.size, self.size)
frame:SetParent(self.frameParent)
frame:SetPoint(self.point, self.x, self.y)
frame:SetBackdropBorderColor(0, 0, 0, 1)
frame:SetAlpha(1)
stacks = frame.stacks
return frame, stacks
end
---@param data table
function IconIndicator:Update(data)
if data.icon then
self:SetIcon(data.icon)
end
local numStacks = data.applications or 0
if numStacks > 0 then
self.stacks:SetText(numStacks)
self.stacks:Show()
end
self.expirationTime = data.expirationTime
self.auraInstanceID = data.auraInstanceID
self.spellId = data.spellId
self:UpdateAlpha()
end
function IconIndicator:UpdateAlpha()
if self.expirationTime == 0 then
self.frame:SetAlpha(1)
return
end
local timeLeft = self.expirationTime - GetTime()
local fadeTime = self.fadeTime or 0
local flashTime = self.flashTime or 0
if timeLeft < flashTime then
local alpha = ((timeLeft * 4) % 1)
self.frame:SetAlpha(alpha)
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
+3 -6
View File
@@ -26,13 +26,10 @@ local Indicator = types.CreateClass("Indicator")
types.Indicator = Indicator types.Indicator = Indicator
--- Show the indicator and supply it with data --- Show the indicator and supply it with data
function Indicator:Show(data) function Indicator:Show(data) end
end
--- Update the indicator with new data --- Update the indicator with new data
function Indicator:Update(data) function Indicator:Update(data) end
end
--- Hide the indicator --- Hide the indicator
function Indicator:Hide() function Indicator:Hide() end
end
+3 -1
View File
@@ -21,7 +21,9 @@ types.StatusBar = types.CreateClass("StatusBar")
local StatusBar = types.StatusBar local StatusBar = types.StatusBar
function StatusBar:Init(parent, width, height, level, top, texture, bgtexture) function StatusBar:Init(parent, width, height, level, top, texture, bgtexture)
if top == nil then top = true end if top == nil then
top = true
end
level = level or 0 level = level or 0
-- parent: the parent frame -- parent: the parent frame
self.parent = parent self.parent = parent
+66 -21
View File
@@ -21,26 +21,20 @@ 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)
types.AuraTrigger = AuraTrigger types.AuraTrigger = AuraTrigger
---Creates a new AuraTrigger from a config description and attaches it to the ---Creates a new AuraTrigger from a config description and attaches it to the
---correct data source ---correct data source
---@param unit UnitFrame ---@param unit UnitFrame
---@param config table ---@param config table
---@return AuraTrigger ---@return AuraTrigger
function AuraTrigger.CreateFromConfig(unit, config) function AuraTrigger.CreateFromConfig(unit, config)
local trigger = AuraTrigger:new( local trigger =
config.spellId, AuraTrigger:new(config.spellIds, config.own, config.requiredCount, config.invert, config.defaultData)
config.own,
config.requiredCount,
config.invert,
config.defaultData
)
unit.auras:AddTrigger(trigger) unit.auras:AddTrigger(trigger)
return trigger return trigger
end end
@@ -54,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
@@ -85,25 +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({ stacks = aura.applications, duration = aura.duration, expirationTime = aura.expirationTime }) local newData = self:BestAuraData(nil)
self:SetState(self.count >= self.requiredCount, false) self:SetStateAndData(self.count >= self.requiredCount, newData)
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({ stacks = after.applications, duration = after.duration, expirationTime = after.expirationTime }) if not self:IsMatching(after) then
if self.active then return
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
@@ -112,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
+5 -9
View File
@@ -73,7 +73,7 @@ function MultiTrigger:AddTrigger(trigger)
end, end,
Hide = function(_, data) Hide = function(_, data)
self:OnChildDeactivate(idx, trigger, data) self:OnChildDeactivate(idx, trigger, data)
end end,
}) })
end end
@@ -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,6 +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
+2 -3
View File
@@ -29,8 +29,7 @@ types.StatusTrigger = StatusTrigger
---@param unit UnitFrame ---@param unit UnitFrame
---@param config table ---@param config table
function StatusTrigger.CreateFromConfig(unit, config) function StatusTrigger.CreateFromConfig(unit, config)
local trigger = StatusTrigger:new(config.status, config.requiredCount, local trigger = StatusTrigger:new(config.status, config.requiredCount, config.invert, config.defaultData)
config.invert, config.defaultData)
unit.auras:AddTrigger(trigger) unit.auras:AddTrigger(trigger)
return trigger return trigger
end end
@@ -59,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 -6
View File
@@ -28,7 +28,6 @@ local types = omi.GetModule("types")
local Trigger = types.CreateClass("Trigger") local Trigger = types.CreateClass("Trigger")
types.Trigger = Trigger types.Trigger = Trigger
---@param unit UnitFrame ---@param unit UnitFrame
---@param config table ---@param config table
---@return Trigger ---@return Trigger
@@ -48,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
@@ -70,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
@@ -89,6 +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
+1 -2
View File
@@ -24,7 +24,7 @@ local function CreateClassRaw(name, parent)
c.__typeInfo = { c.__typeInfo = {
name = name, name = name,
class = c, class = c,
parent = parent parent = parent,
} }
c.__index = c c.__index = c
if parent ~= nil then if parent ~= nil then
@@ -49,6 +49,5 @@ function types.IsDerivedFrom(a, b)
end end
current = current.__typeInfo.parent current = current.__typeInfo.parent
until current == nil until current == nil
;
return false return false
end end
+118 -21
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
@@ -26,6 +31,9 @@ local AuraList = types.AuraList
---@class SquareIndicator ---@class SquareIndicator
local SquareIndicator = types.SquareIndicator local SquareIndicator = types.SquareIndicator
---@class IconIndicator
local IconIndicator = types.IconIndicator
---@class SquareIndicator ---@class SquareIndicator
local BorderIndicator = types.BorderIndicator local BorderIndicator = types.BorderIndicator
@@ -33,7 +41,6 @@ local BorderIndicator = types.BorderIndicator
local UnitFrame = types.CreateClass("UnitFrame") local UnitFrame = types.CreateClass("UnitFrame")
types.UnitFrame = UnitFrame types.UnitFrame = UnitFrame
local colors = { local colors = {
hostile = { 0.5, 0.0, 0.0 }, hostile = { 0.5, 0.0, 0.0 },
neutral = { 0.7, 0.7, 0.0 }, neutral = { 0.7, 0.7, 0.0 },
@@ -51,8 +58,8 @@ local colors = {
bomb = { 1.0, 0.7, 0.7 }, 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, .95, 1.0 }, shield = { 0.85, 0.95, 1.0 },
absorb = { 0.5, .4, .75 }, absorb = { 0.5, 0.4, 0.75 },
} }
-- This trucates _codepoints_ not graphemes. If combination codepoints are -- This trucates _codepoints_ not graphemes. If combination codepoints are
@@ -99,17 +106,16 @@ 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)
local overlays = CreateFrame("Frame", nil, secure) local overlays = CreateFrame("Frame", nil, secure)
overlays:SetFrameStrata("MEDIUM") overlays:SetFrameStrata("MEDIUM")
overlays:SetFrameLevel(100) overlays:SetFrameLevel(100)
@@ -120,6 +126,8 @@ function UnitFrame:Init(unit, config)
self:CreateTriggers(config.triggers) self:CreateTriggers(config.triggers)
self:InitDebuffs()
self:RegisterEvents() self:RegisterEvents()
self:Enable() self:Enable()
end end
@@ -148,12 +156,19 @@ function UnitFrame:CreateIndicator(indicator)
indicator.fadeTime, indicator.fadeTime,
indicator.flashTime indicator.flashTime
) )
elseif kind == "BorderIndicator" then elseif kind == "IconIndicator" then
return BorderIndicator:new( return IconIndicator:new(
self, self,
indicator.thickness, indicator.size,
indicator.color indicator.point,
indicator.x,
indicator.y,
indicator.showStacks,
indicator.fadeTime,
indicator.flashTime
) )
elseif kind == "BorderIndicator" then
return BorderIndicator:new(self, indicator.thickness, indicator.color)
else else
error(string.format("Invalid Indicator kind `%s` requested", indicator.kind)) error(string.format("Invalid Indicator kind `%s` requested", indicator.kind))
end end
@@ -174,10 +189,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()
@@ -201,9 +214,9 @@ function UnitFrame:IsInRange()
-- Prefer to use configured spells -- Prefer to use configured spells
if friendlySpell and UnitCanAssist("player", unit) then if friendlySpell and UnitCanAssist("player", unit) then
return IsSpellInRange(friendlySpell, unit) == 1 return C_Spell.IsSpellInRange(friendlySpell, unit) == true
elseif enemySpell and UnitCanAttack("player", unit) then elseif enemySpell and UnitCanAttack("player", unit) then
return IsSpellInRange(enemySpell, unit) == 1 return C_Spell.IsSpellInRange(enemySpell, unit) == true
end end
-- Fall back to raid/party only range check -- Fall back to raid/party only range check
@@ -282,9 +295,15 @@ function UnitFrame:PrepareWheelBinds(bindings)
if bind.button == "wheel-up" or bind.button == "wheel-down" then if bind.button == "wheel-up" or bind.button == "wheel-down" then
local prefix = ModifiersToPrefix(bind.mods) local prefix = ModifiersToPrefix(bind.mods)
local button = bind.button == "wheel-up" and "MOUSEWHEELUP" or "MOUSEWHEELDOWN" local button = bind.button == "wheel-up" and "MOUSEWHEELUP" or "MOUSEWHEELDOWN"
table.insert(bindScript, table.insert(
string.format([[self:SetBindingClick(false, "%s%s", "OmicronSecureFrame%s", "%s%s")]], bindScript,
prefix, button, unit, prefix, bind.button string.format(
[[self:SetBindingClick(false, "%s%s", "OmicronSecureFrame%s", "%s%s")]],
prefix,
button,
unit,
prefix,
bind.button
) )
) )
end end
@@ -375,10 +394,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
@@ -425,6 +443,7 @@ function UnitFrame:RegisterEvents()
end end
end end
secure:RegisterEvent("GROUP_ROSTER_UPDATE")
secure:RegisterUnitEvent("UNIT_AURA", unit) secure:RegisterUnitEvent("UNIT_AURA", unit)
secure:RegisterUnitEvent("UNIT_HEALTH", unit) secure:RegisterUnitEvent("UNIT_HEALTH", unit)
secure:RegisterUnitEvent("UNIT_MAXHEALTH", unit) secure:RegisterUnitEvent("UNIT_MAXHEALTH", unit)
@@ -467,6 +486,81 @@ function UnitFrame:UNIT_ABSORB_AMOUNT_CHANGED()
self:UpdateShield() self:UpdateShield()
end end
function UnitFrame:GROUP_ROSTER_UPDATE()
if self:HasUnitChanged() then
self:UpdateAll(true)
return
end
end
-- FIXME: This is a total hack job, just quickly want to get debuffs rendering on my unit frames to learn new dungeons
function UnitFrame:InitDebuffs()
local icons = {}
self.debuffIndicators = icons
for i = 1, 5 do
local icon = IconIndicator:new(self, 17, "BOTTOMRIGHT", -2 - (i - 1) * 19, 2, true, 10.0, nil, true)
icons[i] = icon
end
end
local ignoredDebuffs = {
[57723] = true, -- Sated, shaman lust debuff (alliance)
[57724] = true, -- Sated, shaman lust debuff (horde)
[80354] = true, -- Temporal displacement
[390435] = true, -- Exhausstion
[264689] = true, -- Fatigued
[87024] = true, -- Cauterized
[45181] = true, -- Cheated Death
[41425] = true, -- hypothermia
[124275] = true, -- Light stagger
[124274] = true, -- moderate stagger
[124273] = true, -- heavy stagger
[25771] = true, -- Forbearance
[456447] = true, -- Battle-Scarred Veteran
[382912] = true, -- Well-Honed Instincts
[1223611] = true, -- Ethereal Exhaustion
[456779] = true, -- Will of the Dawn
[448005] = true, -- Light of the Martyr
[157131] = true, -- Recently Saved by the Light
[387847] = true,
[206151] = true, -- Challenger's Burden
[113942] = true, -- Demonic Gateway
[1216943] = true, -- void pulsar
[462661] = true, -- Voidbinding
[1239229] = true, -- k'areshi surge
[449042] = true, -- Radiant Light
-- blood draw
}
function UnitFrame:UpdateDebuffs()
local auras = {}
local i = 1
while #auras < 5 do
local aura = C_UnitAuras.GetAuraDataByIndex(self.unit, i, "HARMFUL")
if aura == nil then
break
end
if not ignoredDebuffs[aura.spellId] then
table.insert(auras, aura)
end
i = i + 1
end
for i = 1, 5 do
local aura = auras[i]
local icon = self.debuffIndicators[i]
if aura then
if icon.frame then
icon:Update(aura)
else
icon:Show(aura)
end
elseif icon.frame then
icon:Hide()
end
end
end
function UnitFrame:UNIT_AURA(unit, info) function UnitFrame:UNIT_AURA(unit, info)
if self:HasUnitChanged() then if self:HasUnitChanged() then
self:UpdateAll(true) self:UpdateAll(true)
@@ -475,6 +569,8 @@ function UnitFrame:UNIT_AURA(unit, info)
if self.auras:Update(info) then if self.auras:Update(info) then
self:UpdateHealthColor() self:UpdateHealthColor()
end end
self:UpdateDebuffs()
end end
function UnitFrame:UNIT_HEALTH() function UnitFrame:UNIT_HEALTH()
@@ -578,6 +674,7 @@ function UnitFrame:UpdateAll(unitChanged)
self:UpdateRange() self:UpdateRange()
self:UpdateName() self:UpdateName()
self:UpdateRole() -- Also calls UpdatePower if power is visible self:UpdateRole() -- Also calls UpdatePower if power is visible
self:UpdateDebuffs()
end end
function UnitFrame:UpdateHealth() function UnitFrame:UpdateHealth()
+11 -10
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
@@ -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)