Various changes #1

Merged
omicron merged 11 commits from absorb into main 2025-08-24 12:40:06 +00:00
19 changed files with 699 additions and 156 deletions

1
.stylua.toml Normal file
View File

@@ -0,0 +1 @@
indent_type = "Spaces"

View File

@@ -11,6 +11,10 @@ if [[ ! -d "${WOW_ADDON_DIR}" ]]; then
exit 1
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
# build the addon, then install it to the directory passed on the command line
while true; do

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -17,6 +17,7 @@ types/statusbar.lua
types/auralist.lua
types/indicators/indicator.lua
types/indicators/squareindicator.lua
types/indicators/iconindicator.lua
types/indicators/borderindicator.lua
types/unitgroup.lua
types/unitframe.lua

View File

@@ -26,27 +26,27 @@ local colors = {
poison = { 0.0, 0.7, 0.7 },
curse = { 0.7, 0.0, 0.7 },
red = { 1, 0, 0 },
blue = { .4, 0.4, 1 },
light_blue = { .7, .7, 1 },
blue = { 0.4, 0.4, 1 },
light_blue = { 0.7, 0.7, 1 },
}
local function RangeConfig()
local _, class = UnitClass("player")
if class == "SHAMAN" then
return {
friendly = "Healing Surge",
friendly = "Healing Wave",
enemy = "Lightning Bolt",
fade = 0.2
fade = 0.2,
}
elseif class == "PRIEST" then
return {
friendly = "Flash Heal",
enemy = "Smite",
fade = 0.2
fade = 0.2,
}
else
return {
fade = 0.2
fade = 0.2,
}
end
end
@@ -56,128 +56,198 @@ local function MouseConfig()
if class == "SHAMAN" then
return {
-- No modifier
{ button = "mouse1", mods = {}, kind = "target" },
{ button = "mouse1", mods = {}, kind = "target" },
{
button = "mouse2",
mods = {},
kind = "macro",
data =
"/use [@UNIT,dead,help]Ancestral Vision; [@UNIT,help]Chain Heal"
data = "/use [@UNIT,dead,help]Ancestral Vision; [@UNIT,help]Chain Heal",
},
{
button = "mouse3",
mods = {},
kind = "macro",
data =
"/use [@UNIT,dead,help]Ancestral Spirit; [@UNIT,help]Purify Spirit"
data = "/use [@UNIT,dead,help]Ancestral Spirit; [@UNIT,help]Purify Spirit",
},
{ button = "wheel-up", mods = {}, kind = "macro", data = "/use [@UNIT,help]Healing Surge" },
{ button = "wheel-down", mods = {}, kind = "macro", data = "/use [@UNIT,help]Riptide" },
{
button = "wheel-up",
mods = {},
kind = "macro",
data = "/use [@UNIT,help]Healing Surge",
},
{ button = "wheel-down", mods = {}, kind = "macro", data = "/use [@UNIT,help]Riptide" },
-- alt
{ button = "wheel-up", mods = { alt = true }, kind = "spell", data = "Healing Wave" },
{ button = "wheel-down", mods = { alt = true }, kind = "spell", data = "Earth Shield" },
{ button = "wheel-up", mods = { alt = true }, kind = "spell", data = "Healing Wave" },
{ button = "wheel-down", mods = { alt = true }, kind = "spell", data = "Earth Shield" },
-- Shift
{ button = "mouse2", mods = { shift = true }, kind = "togglemenu" },
{ button = "mouse2", mods = { shift = true }, kind = "togglemenu" },
{
button = "wheel-up",
mods = { shift = true },
kind = "macro",
data =
"/cast [@UNIT,help]Water Walking;\n/stopspelltarget"
data = "/cast [@UNIT,help]Water Walking;\n/stopspelltarget",
},
}
elseif class == "PRIEST" then
return {
-- No modifier
{ button = "mouse1", mods = {}, kind = "target" },
{ button = "mouse1", mods = {}, kind = "target" },
{
button = "mouse2",
mods = {},
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",
mods = {},
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 = "wheel-up", mods = {}, kind = "macro", data = "/use [@UNIT,help]Flash Heal" },
{ button = "wheel-down", mods = {}, kind = "macro", data = "/use [@UNIT,help]Renew" },
{ 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" },
-- alt
{ button = "mouse1", mods = { alt = true }, kind = "spell", data = "Power Word: Life" },
{ button = "mouse2", mods = { alt = true }, kind = "spell", data = "Pain Suppression" },
{ button = "mouse3", mods = { alt = true }, kind = "spell", data = "Power Infusion" },
{ button = "wheel-up", mods = { alt = true }, kind = "spell", data = "Rapture" },
{ button = "wheel-down", mods = { alt = true }, kind = "spell", data = "Power Word: Shield" },
{ button = "mouse1", mods = { alt = true }, kind = "spell", data = "Power Word: Life" },
{ button = "mouse2", mods = { alt = true }, kind = "spell", data = "Pain Suppression" },
{ button = "mouse3", mods = { alt = true }, kind = "spell", data = "Power Infusion" },
{ button = "wheel-up", mods = { alt = true }, kind = "spell", data = "Rapture" },
{ button = "wheel-down", mods = { alt = true }, kind = "spell", data = "Power Word: Shield" },
-- Shift
{ button = "mouse2", mods = { shift = true }, kind = "togglemenu" },
{ button = "mouse2", mods = { shift = true }, kind = "togglemenu" },
{
button = "wheel-up",
mods = { shift = true },
kind = "macro",
data = "/cast [@UNIT,help]Levitate;\n/stopspelltarget"
data = "/cast [@UNIT,help]Levitate;\n/stopspelltarget",
},
{
button = "wheel-down",
mods = { shift = true },
kind = "macro",
data = "/cast [@UNIT,help]Leap of Faith;\n/stopspelltarget"
data = "/cast [@UNIT,help]Leap of Faith;\n/stopspelltarget",
},
}
elseif class == "MONK" then
return {
-- No modifier
{ button = "mouse1", mods = {}, kind = "target" },
{ button = "mouse1", mods = {}, kind = "target" },
{
button = "mouse2",
mods = {},
kind = "macro",
data = ("/use [@UNIT,known:Reawaken,dead,help]Reawaken; "
data = (
"/use [@UNIT,known:Reawaken,dead,help]Reawaken; "
.. "[@UNIT,dead,help]Resuscitate; "
.. "[@UNIT,help]Tiger's Lust"),
.. "[@UNIT,help]Tiger's Lust"
),
},
{
button = "mouse3",
mods = {},
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 = "wheel-up", mods = {}, kind = "macro", data = "/use [@UNIT,help]Vivify" },
{
button = "wheel-down",
mods = {},
kind = "macro",
data = "/use [@UNIT,help]Soothing Mist",
},
{ button = "mouse4", mods = {}, kind = "spell", data = "Shadow Covenant" },
{ button = "wheel-up", mods = {}, kind = "macro", data = "/use [@UNIT,help]Vivify" },
{ button = "wheel-down", mods = {}, kind = "macro", data = "/use [@UNIT,help]Soothing Mist" },
-- alt
{ button = "mouse1", mods = { alt = true }, kind = "spell", data = "Power Word: Life" },
{ button = "mouse2", mods = { alt = true }, kind = "spell", data = "Pain Suppression" },
{ button = "mouse3", mods = { alt = true }, kind = "spell", data = "Power Infusion" },
{ button = "wheel-up", mods = { alt = true }, kind = "spell", data = "Rapture" },
{ button = "wheel-down", mods = { alt = true }, kind = "spell", data = "Power Word: Shield" },
{ button = "mouse1", mods = { alt = true }, kind = "spell", data = "Power Word: Life" },
{ button = "mouse2", mods = { alt = true }, kind = "spell", data = "Pain Suppression" },
{ button = "mouse3", mods = { alt = true }, kind = "spell", data = "Power Infusion" },
{ button = "wheel-up", mods = { alt = true }, kind = "spell", data = "Rapture" },
{ button = "wheel-down", mods = { alt = true }, kind = "spell", data = "Power Word: Shield" },
-- Shift
{ button = "mouse2", mods = { shift = true }, kind = "togglemenu" },
{ button = "mouse2", mods = { shift = true }, kind = "togglemenu" },
{
button = "wheel-up",
mods = { shift = true },
kind = "macro",
data = "/cast [@UNIT,help]Levitate;\n/stopspelltarget"
data = "/cast [@UNIT,help]Levitate;\n/stopspelltarget",
},
{
button = "wheel-down",
mods = { shift = true },
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
return {
-- Super basic defaults
{ button = "mouse1", mods = {}, kind = "target" },
{ button = "mouse1", mods = {}, kind = "target" },
{ button = "mouse2", mods = { shift = true }, kind = "togglemenu" },
}
end
@@ -199,7 +269,7 @@ local function TriggerClassConfig()
y = -3,
color = colors.white,
showStacks = true,
}
},
},
{
kind = "AuraTrigger",
@@ -213,21 +283,20 @@ local function TriggerClassConfig()
y = -3,
color = colors.white,
showStacks = true,
}
},
},
{
kind = "AuraTrigger",
spellId = 61295,
own = true, -- Riptide
indicator = {
kind = "SquareIndicator",
kind = "IconIndicator",
size = 17,
point = "BOTTOMLEFT",
x = 3,
y = 3,
color = colors.cyan,
fadeTime = 10.0,
}
},
},
{
kind = "StatusTrigger",
@@ -238,7 +307,7 @@ local function TriggerClassConfig()
thickness = 3.0,
color = colors.red,
level = 0,
}
},
},
}
elseif class == "PRIEST" then
@@ -255,7 +324,7 @@ local function TriggerClassConfig()
y = -3,
color = colors.white,
fadeTime = 10.0,
}
},
},
{
kind = "AuraTrigger",
@@ -270,7 +339,7 @@ local function TriggerClassConfig()
color = colors.orange,
fadeTime = 10.0,
flashTime = 1.3,
}
},
},
{
kind = "AuraTrigger",
@@ -284,7 +353,7 @@ local function TriggerClassConfig()
y = -3,
color = { 0.7, 0.9, 1 },
showStacks = true,
}
},
},
{
kind = "AuraTrigger",
@@ -298,7 +367,7 @@ local function TriggerClassConfig()
y = 3,
color = colors.cyan,
fadeTime = 10.0,
}
},
},
{
kind = "AuraTrigger",
@@ -313,7 +382,7 @@ local function TriggerClassConfig()
color = colors.white,
fadeTime = 10.0,
flashTime = 1.3,
}
},
},
{
kind = "AuraTrigger",
@@ -327,7 +396,7 @@ local function TriggerClassConfig()
y = 3,
color = colors.violet,
fadeTime = 10.0,
}
},
},
{
kind = "StatusTrigger",
@@ -338,26 +407,161 @@ local function TriggerClassConfig()
thickness = 3.0,
color = colors.red,
level = 0,
}
},
--[[{
kind="MultiTrigger", invert=false,
children = {
{kind="StatusTrigger", status="Immune", defaultData={color=colors.violet}}, -- Renew
{kind="AuraTrigger", spellId=139, own=true, defaultData={color=colors.cyan}}, -- Renew
{kind="AuraTrigger", spellId=21562, own=true, defaultData={color=colors.white}}, -- pw:f
},
},
}
elseif class == "PALADIN" then
return {
{
kind = "AuraTrigger",
spellId = 287280, -- Glimmer of Light
own = true,
indicator = {
kind="SquareIndicator",
size=17,
point="TOPRIGHT",
x=-3, y=-3,
color=colors.white,
}
}--]]
kind = "SquareIndicator",
size = 17,
point = "TOPLEFT",
x = 3,
y = -3,
color = colors.white,
fadeTime = 10.0,
flashTime = 1.3,
},
},
{
kind = "AuraTrigger",
spellId = 25771, -- Forbearance
indicator = {
kind = "SquareIndicator",
size = 17,
point = "TOPLEFT",
x = 22,
y = -3,
color = colors.red,
fadeTime = 10.0,
flashTime = 1.3,
},
},
{
kind = "AuraTrigger",
spellId = 53563, -- Beacon of Light
own = true,
indicator = {
kind = "SquareIndicator",
size = 17,
point = "TOPLEFT",
x = 41,
y = -3,
color = colors.white,
},
},
{
kind = "AuraTrigger",
spellId = 156910, -- Beacon of Faith
own = true,
indicator = {
kind = "SquareIndicator",
size = 17,
point = "TOPLEFT",
x = 41,
y = -3,
color = colors.light_blue,
},
},
{
kind = "AuraTrigger",
spellId = 200025, -- Beacon of Virtue
own = true,
indicator = {
kind = "SquareIndicator",
size = 17,
point = "TOPLEFT",
x = 41,
y = -3,
color = colors.white,
},
},
{
kind = "AuraTrigger",
spellId = 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",
spellId = 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",
spellId = 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",
spellId = 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
return {}
return {
{
kind = "StatusTrigger",
status = "Burn",
invert = false,
indicator = {
kind = "BorderIndicator",
thickness = 3.0,
color = colors.red,
level = 0,
},
},
}
end
end
@@ -377,45 +581,89 @@ local function MakeDebuffTrigger(slot, spellid, color, stacks)
y = -3,
color = color,
showStacks = stacks,
}
},
}
end
local function TriggerConfig()
local triggers = TriggerClassConfig()
print("Class triggers:", #triggers)
-- slot, spellid, color, stacks
local debuffs = {
-- Kezzara, Aberrus
{ 1, 406525, colors.orange }, -- Dread Rift
{ 2, 402253, colors.red }, -- Ray of Anguish
{ 1, 406525, colors.orange }, -- Dread Rift
{ 2, 402253, colors.red }, -- Ray of Anguish
{ 3, 404743, colors.violet, true }, -- Terror Claws
-- Forgotten Experiment, Aberrus
{ 1, 406365, colors.red, true }, -- Rending Charge (p1)
{ 1, 407327, colors.cyan, true }, -- Unstable Essence (p2/p3)
{ 1, 406365, colors.red, true }, -- Rending Charge (p1)
{ 1, 407327, colors.cyan, true }, -- Unstable Essence (p2/p3)
{ 3, 407313, colors.violet, true }, -- Infused Strikes
-- Uldaman: Legacy of Tyr
{ 1, 377510, colors.cyan, true }, -- Stolen Time
{ 1, 377510, colors.cyan, true }, -- Stolen Time
-- Brackenhide Hollow
{ 1, 367521, colors.cyan, true }, -- Bone Bolt
{ 1, 367521, colors.cyan, true }, -- Bone Bolt
-- The Underrot
--{1, 273226, colors.green, true}, -- Decaying Spores
-- Sarkareth, Aberrus
{ 3, 401330, colors.violet }, -- Burning Claws (P1 tank)
{ 3, 411241, colors.violet }, -- Void Claws (P2 tank)
{ 3, 408429, colors.violet }, -- Void Slash (P3 tank)
{ 2, 404218, colors.blue }, -- Void Fracture (carry bomb)
{ 1, 401951, colors.cyan, true }, -- Oblivion
{ 3, 401330, colors.violet }, -- Burning Claws (P1 tank)
{ 3, 411241, colors.violet }, -- Void Claws (P2 tank)
{ 3, 408429, colors.violet }, -- Void Slash (P3 tank)
{ 2, 404218, colors.blue }, -- Void Fracture (carry bomb)
{ 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
table.insert(triggers, MakeDebuffTrigger(debuff[1], debuff[2], debuff[3], debuff[4]))
end
print("All triggers:", #triggers)
-- Some more debuffs in the bottom row
table.insert(triggers, {
kind = "AuraTrigger",
spellId = 240443, -- Bursting
indicator = {
kind = "SquareIndicator",
size = 17,
point = "BOTTOMRIGHT",
x = -3,
y = 3,
color = colors.red,
fadeTime = 3.0,
showStacks = true,
},
})
return triggers
end
@@ -433,7 +681,7 @@ function HideBlizzardFrames()
TargetFrame,
FocusFrame,
PartyFrame,
CompactRaidFrameContainer
CompactRaidFrameContainer,
}
for _, frame in ipairs(blizzardFrames) do

View File

@@ -88,7 +88,7 @@ Commander.RegisterCommand("omi-pstart", {
PrintLn("To enable the script profiler run `/console scriptProfile 1`, then reload the UI.")
return
end
end
end,
})
Commander.RegisterCommand("omi-pstop", {
@@ -117,7 +117,9 @@ Commander.RegisterCommand("omi-pstop", {
time = time * 1000,
})
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,
})
@@ -136,5 +138,5 @@ Commander.RegisterCommand("omi-pprint", {
local ms_per_sec = time / total
Printf("% 6.1f%% % 7.2fms %7.2fms/s %s\n", pct, time, ms_per_sec, item.name)
end
end
end,
})

View File

@@ -24,10 +24,10 @@ types.AuraList = AuraList
local statusLists = {
Immune = {
[642] = true, -- Divine Shield, Paladin
[642] = true, -- Divine Shield, Paladin
[186265] = true, -- Aspect of the Turtle, Hunter
[45438] = true, -- Ice Block
[31224] = true, -- Cloak of Shadows
[45438] = true, -- Ice Block
[31224] = true, -- Cloak of Shadows
[196555] = true, -- Netherwalk
},
Bomb = {
@@ -41,6 +41,7 @@ local statusLists = {
[411241] = true, -- Sarkareth, Void Claws (p2 tank)
[408429] = true, -- Sarkareth, Void Slash (p3 tank)
[404218] = true, -- Sarkareth, Void fracture (bombs)
[427722] = true, -- Nymue, Weaver's Burden (hehe jk it's private because reasons)
},
Burn = {
-- Jade Serpent Temple
@@ -68,6 +69,8 @@ local statusLists = {
-- Atal'Dazar
[255582] = true, -- Priestess Alun'za, Molten Gold
[250096] = true, -- Yazma, Wracking pain
[255434] = true, -- Rezan, Serrated Teeth
-- The MOTHERLODE!!
[259853] = true, -- Rixxa Fluxflame, Chemical Burn
@@ -104,6 +107,27 @@ local statusLists = {
-- Aberrus
[404010] = 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 +135,7 @@ local statusLists = {
-- callback function with UnitAuraInfo table as argument
local function ForEachAuraSlots(unit, fn, continuationToken, ...)
local GetAuraDataBySlot = C_UnitAuras.GetAuraDataBySlot
local n = select('#', ...)
local n = select("#", ...)
for i = 1, n do
local slot = select(i, ...)
fn(GetAuraDataBySlot(unit, slot))
@@ -121,11 +145,11 @@ end
-- Helper function that goes over all aura slots for a given filter
local function ForEachAuraFiltered(unit, filter, fn)
local UnitAuraSlots = UnitAuraSlots
local UnitAuraSlots = C_UnitAuras.GetAuraSlots
local continuationToken = nil
repeat
continuationToken = ForEachAuraSlots(unit, fn, UnitAuraSlots(unit, filter, nil, continuationToken))
until (continuationToken == nil)
until continuationToken == nil
end
-- Similar to what AuraUtils.ForEachAura does except the callback fn takes a
@@ -135,7 +159,6 @@ local function ForEachAura(unit, fn)
ForEachAuraFiltered(unit, "HARMFUL", fn)
end
---@return AuraList
function AuraList.new(cls, ...)
--- I really dislike duplicating this everywhere but it makes
@@ -152,7 +175,6 @@ function AuraList:Init(unitframe)
self.statusCount = {}
self.auras = {} -- map AuraInstanceID to UnitAuraInfo table
self.triggers = {}
self.triggersBySpell = {}
self.triggersByStatus = {}

View File

@@ -0,0 +1,173 @@
-- 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
function IconIndicator:Init(unitframe, size, point, x, y, showStacks, fadeTime, flashTime)
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
end
--- Show the square indicator
---@param data table
function IconIndicator:Show(data)
local frame, stacks = self:GetFrame()
local numStacks = data.stacks or 0
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 data.icon then
self:SetIcon(data.icon)
else
self:SetIcon(self.icon)
end
frame:Show()
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.stacks or 0
if numStacks > 0 then
self.stacks:SetText(numStacks)
self.stacks:Show()
end
self.expirationTime = data.expirationTime
self.frame:SetAlpha(1)
end
function IconIndicator:OnUpdate(dt)
dt = self.updateThrottle + dt
if dt < 0.04 then
self.updateThrottle = dt
return
else
self.updateThrottle = 0
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)
end
end
--- Hide the square indicator.
function IconIndicator:Hide()
local frame = self.frame
self.stacks:Hide()
self.frame = nil
self.stacks = nil
frame:SetScript("OnUpdate", nil)
iconPool:Release(frame)
end

View File

@@ -26,13 +26,10 @@ local Indicator = types.CreateClass("Indicator")
types.Indicator = Indicator
--- Show the indicator and supply it with data
function Indicator:Show(data)
end
function Indicator:Show(data) end
--- Update the indicator with new data
function Indicator:Update(data)
end
function Indicator:Update(data) end
--- Hide the indicator
function Indicator:Hide()
end
function Indicator:Hide() end

View File

@@ -20,8 +20,10 @@ local types = omi.GetModule("types")
types.StatusBar = types.CreateClass("StatusBar")
local StatusBar = types.StatusBar
function StatusBar:Init(parent, width, height, level, top)
if top == nil then top = true end
function StatusBar:Init(parent, width, height, level, top, texture, bgtexture)
if top == nil then
top = true
end
level = level or 0
-- parent: the parent frame
self.parent = parent
@@ -38,15 +40,17 @@ function StatusBar:Init(parent, width, height, level, top)
end
bar:SetSize(width, height)
bar:SetStatusBarTexture("Interface\\Addons\\OmicronFrames\\media\\textures\\bar_subtle_diagonal")
bar:SetStatusBarTexture(texture)
bar:GetStatusBarTexture():SetHorizTile(false)
bar:GetStatusBarTexture():SetVertTile(false)
self.bar = bar
local bg = bar:CreateTexture(nil, "BACKGROUND")
bg:SetTexture("Interface\\Addons\\OmicronFrames\\media\\textures\\bar_subtle_diagonal")
bg:SetAllPoints(true)
self.bg = bg
if bgtexture ~= nil then
local bg = bar:CreateTexture(nil, "BACKGROUND")
bg:SetTexture(bgtexture)
bg:SetAllPoints(true)
self.bg = bg
end
self:SetRange(0, 1)
self:SetValue(1)
@@ -61,6 +65,10 @@ function StatusBar:SetValue(value)
self.bar:SetValue(value)
end
function StatusBar:SetFillStyle(style)
self.bar:SetFillStyle(style)
end
function StatusBar:Show()
self.bar:Show()
end
@@ -78,5 +86,7 @@ end
function StatusBar:SetColor(r, g, b)
self.bar:SetStatusBarColor(r, g, b)
self.bg:SetVertexColor(0.2 * r, 0.2 * g, 0.2 * b)
if self.bg ~= nil then
self.bg:SetVertexColor(0.2 * r, 0.2 * g, 0.2 * b)
end
end

View File

@@ -27,20 +27,13 @@ local Trigger = types.Trigger
local AuraTrigger = types.CreateClass("AuraTrigger", Trigger)
types.AuraTrigger = AuraTrigger
---Creates a new AuraTrigger from a config description and attaches it to the
---correct data source
---@param unit UnitFrame
---@param config table
---@return AuraTrigger
function AuraTrigger.CreateFromConfig(unit, config)
local trigger = AuraTrigger:new(
config.spellId,
config.own,
config.requiredCount,
config.invert,
config.defaultData
)
local trigger = AuraTrigger:new(config.spellId, config.own, config.requiredCount, config.invert, config.defaultData)
unit.auras:AddTrigger(trigger)
return trigger
end
@@ -92,7 +85,12 @@ function AuraTrigger:AddAura(aura)
return
end
self.count = self.count + 1
self:SetData({ stacks = aura.applications, duration = aura.duration, expirationTime = aura.expirationTime })
self:SetData({
stacks = aura.applications,
duration = aura.duration,
expirationTime = aura.expirationTime,
icon = aura.icon,
})
self:SetState(self.count >= self.requiredCount, false)
end
@@ -100,7 +98,12 @@ end
---@param before UnitAuraInfo
---@param after UnitAuraInfo
function AuraTrigger:UpdateAura(before, after)
self:SetData({ stacks = after.applications, duration = after.duration, expirationTime = after.expirationTime })
self:SetData({
stacks = after.applications,
duration = after.duration,
expirationTime = after.expirationTime,
icon = after.icon,
})
if self.active then
self.indicator:Update(self.data)
end

View File

@@ -1,7 +1,7 @@
-- Copyright 2023 <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)
@@ -13,7 +13,7 @@
-- 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/>.
-- Omicron Frames. If not, see <https://www.gnu.org/licenses/>.
local omi = select(2, ...)
local types = omi.GetModule("types")
@@ -26,7 +26,7 @@ local Trigger = types.Trigger
---indicator.
---@class MultiTrigger: Trigger
---@field active boolean
---@field invert boolean
---@field invert boolean
local MultiTrigger = types.CreateClass("MultiTrigger", Trigger)
types.MultiTrigger = MultiTrigger
@@ -73,7 +73,7 @@ function MultiTrigger:AddTrigger(trigger)
end,
Hide = function(_, data)
self:OnChildDeactivate(idx, trigger, data)
end
end,
})
end
@@ -141,4 +141,3 @@ end
function MultiTrigger:Reset()
self:SetState(false)
end

View File

@@ -1,7 +1,7 @@
-- Copyright 2023 <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)
@@ -13,7 +13,7 @@
-- 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/>.
-- Omicron Frames. If not, see <https://www.gnu.org/licenses/>.
local omi = select(2, ...)
local types = omi.GetModule("types")
---@class Trigger
@@ -29,8 +29,7 @@ types.StatusTrigger = StatusTrigger
---@param unit UnitFrame
---@param config table
function StatusTrigger.CreateFromConfig(unit, config)
local trigger = StatusTrigger:new(config.status, config.requiredCount,
config.invert, config.defaultData)
local trigger = StatusTrigger:new(config.status, config.requiredCount, config.invert, config.defaultData)
unit.auras:AddTrigger(trigger)
return trigger
end
@@ -45,7 +44,7 @@ end
--- Initialize a new AuraTrigger object
---@param status string The kind of status to trigger on
---@param requiredCount number | nil
---@param requiredCount number | nil
---@param invert boolean | nil
function StatusTrigger:Init(status, requiredCount, invert, defaultData)
Trigger.Init(self, invert, defaultData)

View File

@@ -1,7 +1,7 @@
-- Copyright 2023 <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)
@@ -13,22 +13,21 @@
-- 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/>.
-- Omicron Frames. If not, see <https://www.gnu.org/licenses/>.
local omi = select(2, ...)
local types = omi.GetModule("types")
--- Triggers objects provide a link between indicators and some data source. They
-- can pass data from the data source into the indicator. This is a supertype for
-- all triggers and should not be constructed on its own.
-- can pass data from the data source into the indicator. This is a supertype for
-- all triggers and should not be constructed on its own.
---@class Trigger: Object
---@field active boolean
---@field invert boolean
---@field invert boolean
---@field indicator Indicator
---@field data table The most recent data updated by the data source
local Trigger = types.CreateClass("Trigger")
types.Trigger = Trigger
---@param unit UnitFrame
---@param config table
---@return Trigger
@@ -91,4 +90,3 @@ end
function Trigger:Reset()
self:SetState(false, false)
end

View File

@@ -24,7 +24,7 @@ local function CreateClassRaw(name, parent)
c.__typeInfo = {
name = name,
class = c,
parent = parent
parent = parent,
}
c.__index = c
if parent ~= nil then
@@ -49,6 +49,5 @@ function types.IsDerivedFrom(a, b)
end
current = current.__typeInfo.parent
until current == nil
;
return false
end

View File

@@ -26,6 +26,9 @@ local AuraList = types.AuraList
---@class SquareIndicator
local SquareIndicator = types.SquareIndicator
---@class IconIndicator
local IconIndicator = types.IconIndicator
---@class SquareIndicator
local BorderIndicator = types.BorderIndicator
@@ -33,7 +36,6 @@ local BorderIndicator = types.BorderIndicator
local UnitFrame = types.CreateClass("UnitFrame")
types.UnitFrame = UnitFrame
local colors = {
hostile = { 0.5, 0.0, 0.0 },
neutral = { 0.7, 0.7, 0.0 },
@@ -50,7 +52,9 @@ local colors = {
immune = { 0.0, 0.2, 0.4 },
bomb = { 1.0, 0.7, 0.7 },
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 },
absorb = { 0.5, 0.4, 0.75 },
}
-- This trucates _codepoints_ not graphemes. If combination codepoints are
@@ -86,9 +90,25 @@ function UnitFrame:Init(unit, config)
self:SetMouseBinds(config.mouse)
self.hp = StatusBar:new(self, width, height, 0, true)
self.power = StatusBar:new(self, width, 6, 2, false)
local texture = "Interface\\Addons\\OmicronFrames\\media\\textures\\bar_subtle_diagonal"
local texture_top = "Interface\\Addons\\OmicronFrames\\media\\textures\\bar_subtle_diagonal_top"
local texture_bot = "Interface\\Addons\\OmicronFrames\\media\\textures\\bar_subtle_diagonal_bottom"
self.hp = StatusBar:new(self, width, height, 0, true, texture, texture)
self.power = StatusBar:new(self, width, 6, 2, false, texture, texture)
self.power:Hide()
self.absorb = StatusBar:new(self, width, height, 1, true, texture_top)
self.absorb:SetColor(unpack(colors.absorb))
self.absorb:SetFillStyle("REVERSE")
self.shield = StatusBar:new(self, width, height, 1, true, texture_bot)
self.shield:SetColor(unpack(colors.shield))
self.shield:SetRange(0, 1)
self.shield:SetValue(0.6)
self.shield:SetFillStyle("REVERSE")
self.auras = AuraList:new(self)
local overlays = CreateFrame("Frame", nil, secure)
@@ -129,12 +149,19 @@ function UnitFrame:CreateIndicator(indicator)
indicator.fadeTime,
indicator.flashTime
)
elseif kind == "BorderIndicator" then
return BorderIndicator:new(
elseif kind == "IconIndicator" then
return IconIndicator:new(
self,
indicator.thickness,
indicator.color
indicator.size,
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
error(string.format("Invalid Indicator kind `%s` requested", indicator.kind))
end
@@ -182,9 +209,9 @@ function UnitFrame:IsInRange()
-- Prefer to use configured spells
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
return IsSpellInRange(enemySpell, unit) == 1
return C_Spell.IsSpellInRange(enemySpell, unit) == true
end
-- Fall back to raid/party only range check
@@ -263,9 +290,15 @@ function UnitFrame:PrepareWheelBinds(bindings)
if bind.button == "wheel-up" or bind.button == "wheel-down" then
local prefix = ModifiersToPrefix(bind.mods)
local button = bind.button == "wheel-up" and "MOUSEWHEELUP" or "MOUSEWHEELDOWN"
table.insert(bindScript,
string.format([[self:SetBindingClick(false, "%s%s", "OmicronSecureFrame%s", "%s%s")]],
prefix, button, unit, prefix, bind.button
table.insert(
bindScript,
string.format(
[[self:SetBindingClick(false, "%s%s", "OmicronSecureFrame%s", "%s%s")]],
prefix,
button,
unit,
prefix,
bind.button
)
)
end
@@ -406,10 +439,13 @@ function UnitFrame:RegisterEvents()
end
end
secure:RegisterEvent("GROUP_ROSTER_UPDATE")
secure:RegisterUnitEvent("UNIT_AURA", unit)
secure:RegisterUnitEvent("UNIT_HEALTH", unit)
secure:RegisterUnitEvent("UNIT_MAXHEALTH", unit)
secure:RegisterUnitEvent("UNIT_NAME_UPDATE", unit)
secure:RegisterUnitEvent("UNIT_ABSORB_AMOUNT_CHANGED", unit)
secure:RegisterUnitEvent("UNIT_HEAL_ABSORB_AMOUNT_CHANGED", unit)
end
-- returns whether or not the unit guid has changed since the last call to this
@@ -430,6 +466,29 @@ function UnitFrame:HasUnitChanged()
end
end
function UnitFrame:UNIT_HEAL_ABSORB_AMOUNT_CHANGED()
if self:HasUnitChanged() then
self:UpdateAll(true)
return
end
self:UpdateAbsorb()
end
function UnitFrame:UNIT_ABSORB_AMOUNT_CHANGED()
if self:HasUnitChanged() then
self:UpdateAll(true)
return
end
self:UpdateShield()
end
function UnitFrame:GROUP_ROSTER_UPDATE()
if self:HasUnitChanged() then
self:UpdateAll(true)
return
end
end
function UnitFrame:UNIT_AURA(unit, info)
if self:HasUnitChanged() then
self:UpdateAll(true)
@@ -536,6 +595,8 @@ function UnitFrame:UpdateAll(unitChanged)
end
self:UpdateHealth()
self:UpdateHealthColor()
self:UpdateAbsorb()
self:UpdateShield()
self:UpdateRange()
self:UpdateName()
self:UpdateRole() -- Also calls UpdatePower if power is visible
@@ -550,6 +611,32 @@ function UnitFrame:UpdateHealth()
self.hp:SetValue(val)
end
function UnitFrame:UpdateShield()
local unit = self.unit
local val = UnitGetTotalAbsorbs(unit)
local max = UnitHealthMax(unit)
if val <= 0 then
self.shield:Hide()
else
self.shield:SetRange(0, max)
self.shield:SetValue(val)
self.shield:Show()
end
end
function UnitFrame:UpdateAbsorb()
local unit = self.unit
local val = UnitGetTotalHealAbsorbs(unit)
local max = UnitHealthMax(unit)
if val <= 0 then
self.absorb:Hide()
else
self.absorb:SetRange(0, max)
self.absorb:SetValue(val)
self.absorb:Show()
end
end
function UnitFrame:UpdateHealthColor()
local unit = self.unit
local val = UnitHealth(unit)