Compare commits

...

21 Commits

Author SHA1 Message Date
763bf0bdae Remove old unused textures for squares 2025-08-24 12:37:22 +02:00
f181c0022c Apply stylua to the entire codebase 2025-08-24 12:28:06 +02:00
0cced419a6 Make devloop do an initial install on first run 2025-08-24 12:28:06 +02:00
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
6f954b9d15 Add more immunity and burn spells to auralist 2025-08-24 12:28:06 +02:00
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
dc62b031a9 Update UnitAuraSlots API for TWW 2025-08-24 12:28:06 +02:00
ec7e2c74ff Update range check API to for TWW 2025-08-24 12:28:06 +02:00
b1dca1639d Properly update unit frames on GROUP_ROSTER_UPDATE 2025-08-24 12:28:06 +02:00
47542c7147 Expand config with more auras 2025-08-24 12:28:06 +02:00
1fce6017bd Add bar overlays for absorb and heal absorb 2025-08-24 12:27:56 +02:00
ff9d8b2289 Add/change monk and priest keybinds and auras 2023-11-05 19:20:16 +01:00
24c571658c Apply auto formatting to all source files 2023-09-10 12:37:25 +02:00
e20887d46b Implement fade and flash behavior for square indicators 2023-07-28 14:15:50 +02:00
877560cb58 Rework profiler for 10.1.0
Since GetFunctionCPUUsage was removed in 10.1.0 the entire profler
stopped working. This has been replaced by wrapping the functions in a
closure that runs GetTimePreciseSec before and after and keeps track of
the total runtime per function.
2023-07-27 15:29:08 +02:00
b201ef789a Partially fix unicode name truncation.
This fix ensures that name truncation always happens on codepoint
boundaries and that a name has exactly 5 visible codepoints. This should
cover all possible cases unless names can have grapheme clusters, afaik
this is not the case.
2023-07-26 03:15:13 +02:00
30d8248832 Expand spell ids for default AuraList statuses 2023-05-12 16:51:39 +02:00
5668a5b4db Fix a bug where AuraList wasn't processing a full update
This fixes issue #8
2023-05-12 15:50:34 +02:00
263b3f1330 Update default configuration indicators/keybinds/layout 2023-05-12 15:45:54 +02:00
45129a8635 Square- and BorderIndicator now use trigger data for changing color
If the field color is supplied from trigger data, these indicators will
now change their color. Currently this change is permanent and not bound to the
trigger.
2023-04-14 15:40:39 +02:00
1f95899781 Make Trigger responsible for default data
Trigger now has a new method Trigger:SetData and two new fields
Trigger.defaultData and Trigger.data

Whenever SetData is used it ensures that all the keys and values from
defaultData are present in the new data.

The data argument from Trigger:SetState has been removed and it will now
always use the Trigger.Data field for notifying indicators
2023-04-14 15:37:57 +02:00
25 changed files with 1332 additions and 334 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

@@ -17,29 +17,36 @@
local omif = select(2, ...)
local colors = {
cyan = {0.0, 0.8, 0.8},
white = {1.0, 1.0, 1.0},
orange = {1.0, 0.5 , 0},
violet = {1.0, 0, 1.0},
cyan = { 0.0, 0.8, 0.8 },
white = { 1.0, 1.0, 1.0 },
orange = { 1.0, 0.5, 0 },
violet = { 1.0, 0, 1.0 },
magic = { 0.4, 0.4, 1.0 },
disease = { 0.4, 0.2, 0.0 },
poison = { 0.0, 0.7, 0.7 },
curse = { 0.7, 0.0, 0.7 },
red = { 1, 0, 0 },
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
@@ -49,142 +56,618 @@ local function MouseConfig()
if class == "SHAMAN" then
return {
-- No modifier
{button="mouse1", mods={}, kind="target"},
{button="mouse2", mods={}, kind="macro", 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"},
{button="wheel-up", mods={}, kind="macro", data="/use [@UNIT,help]Healing Surge"},
{button="wheel-down", mods={}, kind="macro", data="/use [@UNIT,help]Riptide"},
{ button = "mouse1", mods = {}, kind = "target" },
{
button = "mouse2",
mods = {},
kind = "macro",
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",
},
{
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",
},
}
elseif class == "PRIEST" then
return {
-- No modifier
{button="mouse1", mods={}, kind="target"},
{button="mouse2", mods={}, kind="macro", 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"},
{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 = "mouse1", mods = {}, kind = "target" },
{
button = "mouse2",
mods = {},
kind = "macro",
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",
},
{ 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="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="wheel-up", mods={shift=true}, kind="macro", data="/cast [@UNIT,help]Levitate;\n/stopspelltarget"},
{button="wheel-down", mods={shift=true}, kind="macro", data="/cast [@UNIT,help]Leap of Faith;\n/stopspelltarget"},
{ button = "mouse2", mods = { shift = true }, kind = "togglemenu" },
{
button = "wheel-up",
mods = { shift = true },
kind = "macro",
data = "/cast [@UNIT,help]Levitate;\n/stopspelltarget",
},
{
button = "wheel-down",
mods = { shift = true },
kind = "macro",
data = "/cast [@UNIT,help]Leap of Faith;\n/stopspelltarget",
},
}
elseif class == "MONK" then
return {
-- No modifier
{ button = "mouse1", mods = {}, kind = "target" },
{
button = "mouse2",
mods = {},
kind = "macro",
data = (
"/use [@UNIT,known:Reawaken,dead,help]Reawaken; "
.. "[@UNIT,dead,help]Resuscitate; "
.. "[@UNIT,help]Tiger's Lust"
),
},
{
button = "mouse3",
mods = {},
kind = "macro",
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",
},
-- 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" },
-- Shift
{ button = "mouse2", mods = { shift = true }, kind = "togglemenu" },
{
button = "wheel-up",
mods = { shift = true },
kind = "macro",
data = "/cast [@UNIT,help]Levitate;\n/stopspelltarget",
},
{
button = "wheel-down",
mods = { shift = true },
kind = "macro",
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="mouse2", mods={shift=true}, kind="togglemenu"},
{ button = "mouse1", mods = {}, kind = "target" },
{ button = "mouse2", mods = { shift = true }, kind = "togglemenu" },
}
end
end
local function TriggerConfig()
local function TriggerClassConfig()
local _, class = UnitClass("player")
if class == "SHAMAN" then
return {
{
kind="AuraTrigger", spellId=383648, own=true, -- Second Earth Shield on yourself
indicator={
kind="SquareIndicator",
size=14,
point="TOPLEFT",
x=2, y=-2,
color=colors.white,
}
kind = "AuraTrigger",
spellId = 383648,
own = true, -- Second Earth Shield on yourself
indicator = {
kind = "SquareIndicator",
size = 17,
point = "TOPLEFT",
x = 3,
y = -3,
color = colors.white,
showStacks = true,
},
},
{
kind="AuraTrigger", spellId=974, own=true, -- Second Earth Shield on yourself
indicator={
kind="SquareIndicator",
size=14,
point="TOPLEFT",
x=2, y=-2,
color=colors.white,
}
kind = "AuraTrigger",
spellId = 974,
own = true, -- Second Earth Shield on yourself
indicator = {
kind = "SquareIndicator",
size = 17,
point = "TOPLEFT",
x = 3,
y = -3,
color = colors.white,
showStacks = true,
},
},
{
kind="AuraTrigger", spellId=61295, own=true, -- Riptide
indicator={
kind="SquareIndicator",
size=14,
point="BOTTOMLEFT",
x=2, y=2,
color=colors.cyan,
}
kind = "AuraTrigger",
spellId = 61295,
own = true, -- Riptide
indicator = {
kind = "IconIndicator",
size = 17,
point = "BOTTOMLEFT",
x = 3,
y = 3,
fadeTime = 10.0,
},
},
{
kind = "StatusTrigger",
status = "Burn",
invert = false,
indicator = {
kind = "BorderIndicator",
thickness = 3.0,
color = colors.red,
level = 0,
},
},
}
elseif class == "PRIEST" then
return {
{
kind="AuraTrigger", spellId=17, own=true, -- Power Word: Shield
indicator={
kind="SquareIndicator",
size=14,
point="TOPLEFT",
x=2, y=-2,
color=colors.white,
}
kind = "AuraTrigger",
spellId = 17,
own = true, -- Power Word: Shield
indicator = {
kind = "SquareIndicator",
size = 17,
point = "TOPLEFT",
x = 3,
y = -3,
color = colors.white,
fadeTime = 10.0,
},
},
{
kind="AuraTrigger", spellId=33206, own=true, -- Pain Suppression
indicator={
kind="SquareIndicator",
size=14,
point="TOPLEFT",
x=18, y=-2,
color=colors.orange,
}
kind = "AuraTrigger",
spellId = 33206,
own = true, -- Pain Suppression
indicator = {
kind = "SquareIndicator",
size = 17,
point = "TOPLEFT",
x = 22,
y = -3,
color = colors.orange,
fadeTime = 10.0,
flashTime = 1.3,
},
},
{
kind="AuraTrigger", spellId=139, own=true, -- Renew
indicator={
kind="SquareIndicator",
size=14,
point="BOTTOMLEFT",
x=2, y=2,
color=colors.cyan,
}
kind = "AuraTrigger",
spellId = 41635,
own = true, -- Prayer of Mending
indicator = {
kind = "SquareIndicator",
size = 17,
point = "TOPLEFT",
x = 41,
y = -3,
color = { 0.7, 0.9, 1 },
showStacks = true,
},
},
{
kind="AuraTrigger", spellId=194384, own=true, -- Renew
indicator={
kind="SquareIndicator",
size=14,
point="BOTTOMLEFT",
x=18, y=2,
color=colors.white,
}
kind = "AuraTrigger",
spellId = 139,
own = true, -- Renew
indicator = {
kind = "SquareIndicator",
size = 17,
point = "BOTTOMLEFT",
x = 3,
y = 3,
color = colors.cyan,
fadeTime = 10.0,
},
},
{
kind="AuraTrigger", spellId=10060, own=true, -- Renew
indicator={
kind="SquareIndicator",
size=14,
point="BOTTOMLEFT",
x=34, y=2,
color=colors.violet,
kind = "AuraTrigger",
spellId = 194384,
own = true, -- Atonement
indicator = {
kind = "SquareIndicator",
size = 17,
point = "BOTTOMLEFT",
x = 22,
y = 3,
color = colors.white,
fadeTime = 10.0,
flashTime = 1.3,
},
},
{
kind = "AuraTrigger",
spellId = 10060,
own = true, -- Power Infusion
indicator = {
kind = "SquareIndicator",
size = 17,
point = "BOTTOMLEFT",
x = 41,
y = 3,
color = colors.violet,
fadeTime = 10.0,
},
},
{
kind = "StatusTrigger",
status = "Burn",
invert = false,
indicator = {
kind = "BorderIndicator",
thickness = 3.0,
color = colors.red,
level = 0,
},
},
}
elseif class == "PALADIN" then
return {
{
kind = "AuraTrigger",
spellId = 287280, -- Glimmer of Light
own = true,
indicator = {
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
-- Quick helper function to create a config for debuff triggers
local function MakeDebuffTrigger(slot, spellid, color, stacks)
if stacks == nil then
stacks = false
end
return {
kind = "AuraTrigger",
spellId = spellid,
indicator = {
kind = "SquareIndicator",
size = 15,
point = "TOPRIGHT",
x = -17 * slot,
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
{ 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)
{ 3, 407313, colors.violet, true }, -- Infused Strikes
-- Uldaman: Legacy of Tyr
{ 1, 377510, colors.cyan, true }, -- Stolen Time
-- Brackenhide Hollow
{ 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
-- 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
local types = omif.GetModule("types")
local UnitFrame = types.UnitFrame
local UnitGroup = types.UnitGroup
@@ -198,7 +681,7 @@ function HideBlizzardFrames()
TargetFrame,
FocusFrame,
PartyFrame,
CompactRaidFrameContainer
CompactRaidFrameContainer,
}
for _, frame in ipairs(blizzardFrames) do
@@ -212,9 +695,9 @@ function CreateRaidFrames(left, top, config)
local width = config.size.width
local height = config.size.height
local group = UnitGroup:new(left, top, width, height)
for i=0,5 do
for j=0,4 do
local num = i*5 + j + 1
for i = 0, 5 do
for j = 0, 4 do
local num = i * 5 + j + 1
local frame = UnitFrame:new("raid" .. num, config)
group:AddUnitFrame(frame)
end
@@ -227,7 +710,7 @@ function CreatePartyFrames(left, top, config)
local group = UnitGroup:new(left, top, width, height)
local player = UnitFrame:new("player", config)
group:AddUnitFrame(player)
for i=1,4 do
for i = 1, 4 do
local frame = UnitFrame:new("party" .. i, config)
group:AddUnitFrame(frame)
end
@@ -241,16 +724,16 @@ function CreateTargetFrames(left, top, config)
focus:SetPosition(left, top)
local target = UnitFrame:new("target", config)
target:SetPosition(left, top - height)
for i=1,4 do
for i = 1, 4 do
local boss = UnitFrame:new("boss" .. i, config)
boss:SetPosition(left, top-(i+1)*height)
boss:SetPosition(left, top - (i + 1) * height)
end
end
function CreateFrames()
config = {
local config = {
size = {
width = 110,
width = 111,
height = 45,
},
range = RangeConfig(),
@@ -259,10 +742,11 @@ function CreateFrames()
triggers = TriggerConfig(),
}
CreatePartyFrames(0, -290, config)
CreatePartyFrames(-0.05, -290.05, config)
config.hideInRaid = false
CreateRaidFrames(0, -290, config)
CreateTargetFrames(110*3+50, -245, config)
CreateRaidFrames(-0.05, -290.05, config)
CreateTargetFrames(110 * 3 + 50 - 0.05, -245.05, config)
HideBlizzardFrames()
end
omif.SetEventHandler("OMICRON_LOADING", CreateFrames)

View File

@@ -19,7 +19,7 @@ omif.events = {} -- event (str) to list of handlers
omif.db = nil
OmicronFrames = {}
omif.modules = {public=OmicronFrames}
omif.modules = { public = OmicronFrames }
-- Simple function that creates or returns a local module table
function omif.GetModule(name)
if omif.modules[name] == nil then
@@ -66,6 +66,7 @@ function omif.OnAddonLoaded(name)
omif.MainEventHandler(nil, "OMICRON_LOADING")
print("Loaded", AddonName)
end
omif.SetEventHandler("ADDON_LOADED", omif.OnAddonLoaded)
-- Fire custom event OMICRON_FULLY_LOADED. This event is fired when the player
@@ -75,6 +76,7 @@ function omif.FireFullyLoadedEvent(initialLogin, reloadUI)
omif.MainEventHandler(nil, "OMICRON_FULLY_LOADED")
end
end
omif.SetEventHandler("PLAYER_ENTERING_WORLD", omif.FireFullyLoadedEvent)
function omif.SlashCommand(args)

View File

@@ -26,56 +26,117 @@ local Printf = Commander.Printf
local types = omi.GetModule("types")
local data = {}
local startTime
local stopTime
local functionTimers = {}
local functionNames = {}
Commander.RegisterCommand("omi-pstart", {
description="Start/reset the OmicronFrames profiler",
command=function()
local profiler = C_CVar.GetCVar("scriptProfile")
if profiler ~= "1" then
PrintLn("scriptProfiler is off")
PrintLn("set it to on with `/console scriptProfile 1`, then reload the UI.")
-- Permanently wrap all functions to do profiling
local function EnableFunctionProfiler()
if #functionTimers ~= 0 then
return
end
PrintLn("OmicronFrames: start profiling")
ResetCPUUsage()
local GetTimePreciseSec = GetTimePreciseSec
local counter = 1
for typeName, T in pairs(types) do
if type(T) == "table" then
PrintLn("Type:", typeName, T)
for fnName, fn in pairs(T) do
if type(fn) == "function" then
local fnIdx = counter
counter = counter + 1
local name = string.format("%s:%s", typeName, fnName)
functionTimers[fnIdx] = 0
functionNames[fnIdx] = name
local wrapped = function(...)
local start = GetTimePreciseSec()
local a, b, c, d, e, f, g, h = fn(...)
local elapsed = GetTimePreciseSec() - start
functionTimers[fnIdx] = functionTimers[fnIdx] + elapsed
return a, b, c, d, e, f, g, h
end
T[fnName] = wrapped
end
end
end
end
end
-- Set all function timers back to 0
local function ResetFunctionProfiler()
for i, _ in ipairs(functionTimers) do
functionTimers[i] = 0
end
end
Commander.RegisterCommand("omi-pstart", {
description = "Start/reset the OmicronFrames profiler",
command = function()
local profiler = C_CVar.GetCVar("scriptProfile")
PrintLn("OmicronFrames: start profiling")
EnableFunctionProfiler()
ResetFunctionProfiler()
startTime = GetTimePreciseSec()
stopTime = nil
if profiler == "1" then
ResetCPUUsage()
else
PrintLn("WoW scriptProfiler is off. Addon totals will not be shown,")
PrintLn("only individual functions.The profiler will not show total")
PrintLn("addon time, only function profiler.")
PrintLn("")
PrintLn("To enable the script profiler run `/console scriptProfile 1`, then reload the UI.")
return
end
end,
})
Commander.RegisterCommand("omi-pstop", {
description="Stop the OmicronFrames profiler",
command=function()
description = "Stop the OmicronFrames profiler",
command = function()
if not startTime then
PrintLn("Profiling not started. Run omi-pstart first.")
return
end
PrintLn("OmicronFrames: stop profiling")
local profiler = C_CVar.GetCVar("scriptProfile") == "1"
data = {}
stopTime = GetTimePreciseSec()
if profiler then
UpdateAddOnCPUUsage()
local total = GetAddOnCPUUsage(addonName)
data = {{
table.insert(data, {
name = "Total OmicronFrames time",
time = total,
pct = 1.0
}}
for typeName, T in pairs(types) do
if type(T) == "table" then
for fnName, fn in pairs(T) do
if type(fn) == "function" then
local time = GetFunctionCPUUsage(fn, true)
table.insert(data, {
name = string.format("%s:%s", typeName, fnName),
time = time,
pct = time/total
})
end
for i, time in ipairs(functionTimers) do
local name = functionNames[i]
table.insert(data, {
name = name,
time = time * 1000,
})
end
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,
})
Commander.RegisterCommand("omi-pprint", {
description="Print the collected OmicronFrames profiling data",
command=function()
description = "Print the collected OmicronFrames profiling data",
command = function()
if not stopTime then
PrintLn("Profiling data not collected yet. Run omi-pstop first.")
return
end
local top = data[#data].time
local total = stopTime - startTime
for _, item in ipairs(data) do
Printf("% 5.1f%% % 5fms %s\n", item.pct*100, item.time, item.name)
end
local time = item.time
local pct = time / top * 100
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,
})

View File

@@ -25,26 +25,118 @@ types.AuraList = AuraList
local statusLists = {
Immune = {
[642] = true, -- Divine Shield, Paladin
[186265] = true -- Aspect of the Turtle, Hunter
[186265] = true, -- Aspect of the Turtle, Hunter
[45438] = true, -- Ice Block
[31224] = true, -- Cloak of Shadows
[196555] = true, -- Netherwalk
},
Bomb = {
[642] = true, -- Divine Shield, Paladin
[186265] = true, -- Aspect of the Turtle, Hunter
[381615] = true, -- Raszageth, Static Charge
[377467] = true -- Raszageth, Fulminating Charge
[377467] = true, -- Raszageth, Fulminating Charge
[399713] = true, -- Raszageth, Magnetic Charge
[397797] = true, -- Corrupted Vortex, Jade Serpent Temple
[387843] = true, -- Astral Bomb, Algeth'ar Academy
[386181] = true, -- Mana Bomb, Algeth'ar Academy
[401330] = true, -- Sarkareth, Burning Claws (p1 tank)
[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 = {
[642] = true, -- Divine Shield, Paladin
[186265] = true -- Aspect of the Turtle, Hunter
}
-- Jade Serpent Temple
[114803] = true, -- Throw Torch
[397914] = true, -- Defiling Mist
[106114] = true, -- Touch of Nothingness
-- Shadow moon Burial Ground
[152819] = true, -- Shadow Word: Frailty
[153524] = true, -- Plague Spit
-- Court of Stars
[397907] = true, -- Impending Doom
[209516] = true, -- Mana Fang
[207980] = true, -- Disintegration Beam
[211464] = true, -- Fel Detonation
-- Algeth'ar Academy
[376997] = true, -- Savage Peck, Algeth'ar Academy
[388912] = true, -- Severing Slash, Algeth'ar Academy
[388866] = true, -- Mana Void, Algeth'ar Academy
-- Vault of the Incarnates
[390911] = true, -- Raszageth, Lingering Charge
-- 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
-- Halls of Infusion
-- Uldaman: Legacy of Tyr
[372718] = true, -- Earthen Shards
-- Brackenhide Hollow
[367521] = true, -- Bone Bolt
[367484] = true, -- Vicious Clawmangle
[378020] = true, -- Gash Frenzy
[385058] = true, -- Withering Poison
[384575] = true, -- Crippling Bite
-- Neltharus
[373735] = true, -- Dragon Strike
[372224] = true, -- Draogonbone Axe
[377018] = true, -- Molten Gold
[372570] = true, -- Bold Ambush
-- Neltharion's Lair
-- Freehold
[413131] = true, -- Whirling Dagger
-- The Underrot
[265019] = true, -- Savage Cleave
[265568] = true, -- Dark Omen
[273226] = true, -- Decaying Spores
-- Vortex Pinnacle
-- 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
},
}
-- helper function that goes over several aura slots for a unit. Runs a
-- callback function with UnitAuraInfo table as argument
local function ForEachAuraSlots(unit, fn, continuationToken, ...)
local GetAuraDataBySlot = C_UnitAuras.GetAuraDataBySlot
local n = select('#', ...)
for i=1, n do
local n = select("#", ...)
for i = 1, n do
local slot = select(i, ...)
fn(GetAuraDataBySlot(unit, slot))
end
@@ -53,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
@@ -67,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
@@ -84,7 +175,6 @@ function AuraList:Init(unitframe)
self.statusCount = {}
self.auras = {} -- map AuraInstanceID to UnitAuraInfo table
self.triggers = {}
self.triggersBySpell = {}
self.triggersByStatus = {}
@@ -213,8 +303,15 @@ function AuraList:RemoveAura(iid)
return statusChanged
end
-- FIXME: param wrong
---@param info UnitAuraInfo
function AuraList:Update(info)
if info.isFullUpdate then
self:Reset()
-- FIXME: return value might not reflect truth but true is the more prudent choice
return true
end
local GetAuraDataByAuraInstanceID = C_UnitAuras.GetAuraDataByAuraInstanceID
local statusChanged = false
for _, aura in ipairs(info.addedAuras or {}) do

View File

@@ -70,17 +70,26 @@ end
--- Show the square indicator.
---@param data table
function BorderIndicator:Show(data)
if data.color then
self.color = data.color
end
local frame, stacks = self:GetFrame()
frame:Show()
end
function BorderIndicator:Update(data)
if data.color then
self:SetColor(data.color)
end
end
function BorderIndicator:GetFrame()
local frame, new = borderPool:Acquire()
if new then
frame.backdropInfo = {
edgeFile = "Interface\\Addons\\OmicronFrames\\media\\textures\\pixel_edge",
edgeSize = 1,
insets = {left=0, right=0, top=0, bottom=0},
insets = { left = 0, right = 0, top = 0, bottom = 0 },
}
end
@@ -99,4 +108,3 @@ function BorderIndicator:Hide()
self.frame = nil
borderPool:Release(frame)
end

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,14 +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

@@ -45,7 +45,9 @@ end
---@param y number
---@param color number[]
---@param showStacks boolean
function SquareIndicator:Init(unitframe, size, point, x, y, color, showStacks)
---@param doFade nil|number
---@param doFlash nil|number
function SquareIndicator:Init(unitframe, size, point, x, y, color, showStacks, fadeTime, flashTime)
self.unitframe = unitframe
self.frameParent = unitframe.overlays
self.size = size
@@ -54,6 +56,8 @@ function SquareIndicator:Init(unitframe, size, point, x, y, color, showStacks)
self.y = y
self.color = color
self.showStacks = showStacks
self.fadeTime = fadeTime
self.flashTime = flashTime
end
--- Set the color of the Square Indicator
@@ -77,7 +81,18 @@ function SquareIndicator:Show(data)
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.color then
self:SetColor(data.color)
else
frame:SetBackdropColor(unpack(self.color))
end
frame:Show()
end
@@ -89,7 +104,7 @@ function SquareIndicator:GetFrame()
bgFile = "Interface\\Addons\\OmicronFrames\\media\\textures\\square_white",
edgeFile = "Interface\\Addons\\OmicronFrames\\media\\textures\\pixel_edge",
edgeSize = 2,
insets = {left=0, right=0, top=0, bottom=0},
insets = { left = 0, right = 0, top = 0, bottom = 0 },
}
frame:ApplyBackdrop()
@@ -104,17 +119,45 @@ function SquareIndicator:GetFrame()
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 SquareIndicator:Update(data)
if data.color then
self:SetColor(data.color)
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 SquareIndicator: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.
@@ -123,5 +166,6 @@ function SquareIndicator:Hide()
self.stacks:Hide()
self.frame = nil
self.stacks = nil
frame:SetScript("OnUpdate", nil)
squarePool:Release(frame)
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
if bgtexture ~= nil then
local bg = bar:CreateTexture(nil, "BACKGROUND")
bg:SetTexture("Interface\\Addons\\OmicronFrames\\media\\textures\\bar_subtle_diagonal")
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
@@ -70,13 +78,15 @@ function StatusBar:Hide()
end
function StatusBar:SetInterpolatedColor(start, stop, progress)
local r = start[1] + (stop[1] - start[1])*progress
local g = start[2] + (stop[2] - start[2])*progress
local b = start[3] + (stop[3] - start[3])*progress
local r = start[1] + (stop[1] - start[1]) * progress
local g = start[2] + (stop[2] - start[2]) * progress
local b = start[3] + (stop[3] - start[3]) * progress
self:SetColor(r, g, b)
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,19 +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
)
local trigger = AuraTrigger:new(config.spellId, config.own, config.requiredCount, config.invert, config.defaultData)
unit.auras:AddTrigger(trigger)
return trigger
end
@@ -57,8 +51,9 @@ end
---@param own boolean
---@param requiredCount number | nil
---@param invert boolean | nil
function AuraTrigger:Init(spellId, own, requiredCount, invert)
Trigger.Init(self, invert)
---@param defaultData table | nil
function AuraTrigger:Init(spellId, own, requiredCount, invert, defaultData)
Trigger.Init(self, invert, defaultData)
self.spellId = spellId
self.requiredCount = requiredCount or 1
self.own = own
@@ -68,7 +63,7 @@ end
--- Reset the AuraTrigger to the default state
function AuraTrigger:Reset()
self.count = 0
self:SetState(self.count >= self.requiredCount, nil, false)
self:SetState(self.count >= self.requiredCount, false)
end
---Check if a given aura matches this AuraTrigger
@@ -90,15 +85,27 @@ function AuraTrigger:AddAura(aura)
return
end
self.count = self.count + 1
self:SetState(self.count >= self.requiredCount, {stacks=aura.applications}, false)
self:SetData({
stacks = aura.applications,
duration = aura.duration,
expirationTime = aura.expirationTime,
icon = aura.icon,
})
self:SetState(self.count >= self.requiredCount, false)
end
---Inform the trigger about an updated aura
---@param before UnitAuraInfo
---@param after UnitAuraInfo
function AuraTrigger:UpdateAura(before, after)
self:SetData({
stacks = after.applications,
duration = after.duration,
expirationTime = after.expirationTime,
icon = after.icon,
})
if self.active then
self.indicator:Update({stacks=after.applications})
self.indicator:Update(self.data)
end
end
@@ -109,5 +116,6 @@ function AuraTrigger:RemoveAura(aura)
return
end
self.count = self.count - 1
self:SetState(self.count >= self.requiredCount, nil, false)
self:SetData({})
self:SetState(self.count >= self.requiredCount, false)
end

View File

@@ -73,7 +73,7 @@ function MultiTrigger:AddTrigger(trigger)
end,
Hide = function(_, data)
self:OnChildDeactivate(idx, trigger, data)
end
end,
})
end
@@ -92,7 +92,8 @@ function MultiTrigger:OnChildActivate(idx, trigger, data)
table.sort(activeChildren)
-- The highest priority active trigger has changed
if activeChild ~= activeChildren[1] then
self:SetState(true, data)
self:SetData(data)
self:SetState(true, true)
end
end
@@ -113,8 +114,15 @@ function MultiTrigger:OnChildDeactivate(idx, trigger, data)
end
end
table.remove(activeChildren, found)
if activeChild ~= activeChildren[1] then
self:SetState(activeChildren[1] ~= nil, childrenData[1], true)
local newActiveChild = activeChildren[1]
if activeChild ~= newActiveChild then
if activeChildren[1] ~= nil then
self:SetData(childrenData[newActiveChild])
self:SetState(true, true)
else
self:SetData({})
self:SetState(false, false)
end
end
end
@@ -133,4 +141,3 @@ end
function MultiTrigger:Reset()
self:SetState(false)
end

View File

@@ -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)
local trigger = StatusTrigger:new(config.status, config.requiredCount, config.invert, config.defaultData)
unit.auras:AddTrigger(trigger)
return trigger
end
@@ -47,8 +46,8 @@ end
---@param status string The kind of status to trigger on
---@param requiredCount number | nil
---@param invert boolean | nil
function StatusTrigger:Init(status, requiredCount, invert)
Trigger.Init(self, invert)
function StatusTrigger:Init(status, requiredCount, invert, defaultData)
Trigger.Init(self, invert, defaultData)
self.status = status
self.requiredCount = requiredCount or 1
self.invert = invert or false
@@ -59,5 +58,5 @@ end
-- status:
-- Must be a valid UnitAuraInfo
function StatusTrigger:UpdateStatus(count)
self:SetState(count >= self.requiredCount, nil, false)
self:SetState(count >= self.requiredCount, false)
end

View File

@@ -23,10 +23,11 @@ local types = omi.GetModule("types")
---@class Trigger: Object
---@field active 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
@@ -36,10 +37,25 @@ end
--- Initialize a new Trigger object
---@param invert boolean|nil
function Trigger:Init(invert)
---@param defaultData table|nil default data that is always present when this trigger updates an indicator
function Trigger:Init(invert, defaultData)
invert = invert or false
self.invert = invert
self.active = invert
self.defaultData = defaultData
self:SetData({})
end
---Save new data from the datasource into the trigger. This method will make
---sure the default data is always present
---@param data table
function Trigger:SetData(data)
if self.defaultData then
for k, v in pairs(self.defaultData) do
data[k] = v
end
end
self.data = data
end
--- Set the target of the Trigger.
@@ -49,30 +65,28 @@ end
function Trigger:SetTarget(target)
self.indicator = target
if self.active then
target:Show()
target:Show(self.data)
end
end
---Set the state of the trigger. Will activate the indicator if needed.
---@param data table<string,any> | nil
---@param state boolean
function Trigger:SetState(state, data, doUpdate)
function Trigger:SetState(state, doUpdate)
state = state ~= self.invert -- state xor inverted
if self.active ~= state then
if state then
self.indicator:Show(data)
self.indicator:Show(self.data)
else
self.indicator:Hide()
end
self.active = state
elseif doUpdate and self.active then
self.indicator:Update(data)
self.indicator:Update(self.data)
end
end
-- Reset the trigger to the default state. If this changes the activation of the
-- trigger then the target will be notified.
function Trigger:Reset()
self:SetState(false, nil, false)
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
@@ -48,6 +48,6 @@ function types.IsDerivedFrom(a, b)
return true
end
current = current.__typeInfo.parent
until current == nil;
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,26 +36,42 @@ 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},
healthy = {0, 0.1, 0},
high = {0, 1.0, 0},
mid = {1.0, 1.0, 0},
low = {1.0, 0.0, 0},
offline = {0.7, 0.7, 0.7},
dead = {0.7, 0.7, 0.7},
magic = {0.4, 0.4, 1.0},
disease = {0.4, 0.2, 0.0},
poison = {0.0, 0.7, 0.7},
curse = {0.7, 0.0, 0.7},
immune = {0.0, 0.2, 0.4},
bomb = {1.0, 0.7, 0.7},
cyan = {0.0, 0.8, 0.8},
white = {1.0, 1.0, 1.0}
hostile = { 0.5, 0.0, 0.0 },
neutral = { 0.7, 0.7, 0.0 },
healthy = { 0, 0.1, 0 },
high = { 0, 1.0, 0 },
mid = { 1.0, 1.0, 0 },
low = { 1.0, 0.0, 0 },
offline = { 0.7, 0.7, 0.7 },
dead = { 0.7, 0.7, 0.7 },
magic = { 0.4, 0.4, 1.0 },
disease = { 0.4, 0.2, 0.0 },
poison = { 0.0, 0.7, 0.7 },
curse = { 0.7, 0.0, 0.7 },
immune = { 0.0, 0.2, 0.4 },
bomb = { 1.0, 0.7, 0.7 },
cyan = { 0.0, 0.8, 0.8 },
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
-- contained in the string, it will not properly truncate and may return
-- incorrect graphemes
local function utf8_truncate(s, n)
local init = 0
for _ = 1, n do
local _, last = string.find(s, ".[\128-\191]*", init + 1)
if not last then
return s
end
init = last
end
return string.sub(s, 1, init)
end
---@param unit string
---@param config table
function UnitFrame:Init(unit, config)
@@ -71,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)
@@ -110,14 +145,23 @@ function UnitFrame:CreateIndicator(indicator)
indicator.x,
indicator.y,
indicator.color,
indicator.showStacks
indicator.showStacks,
indicator.fadeTime,
indicator.flashTime
)
elseif kind == "IconIndicator" then
return IconIndicator:new(
self,
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
)
return BorderIndicator:new(self, indicator.thickness, indicator.color)
else
error(string.format("Invalid Indicator kind `%s` requested", indicator.kind))
end
@@ -127,7 +171,7 @@ function UnitFrame:StartRangeTicker()
if self.rangeTicker then
return
end
local delta = 0.45 + (fastrandom(0, 100)/1000)
local delta = 0.45 + (fastrandom(0, 100) / 1000)
self.rangeTicker = C_Timer.NewTicker(delta, function()
self:UpdateRange()
end)
@@ -165,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
@@ -246,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
@@ -389,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
@@ -413,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)
@@ -519,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
@@ -533,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)
@@ -563,7 +667,7 @@ function UnitFrame:UpdateHealthColor()
elseif isFriend and pct >= 0.90 then
self.hp:SetColor(unpack(colors.healthy))
elseif isFriend and pct >= 0.75 then
local progress = (pct - 0.75) / (0.90-0.75)
local progress = (pct - 0.75) / (0.90 - 0.75)
self.hp:SetInterpolatedColor(colors.mid, colors.high, progress)
else
local progress = pct / 0.75
@@ -580,25 +684,10 @@ function UnitFrame:UpdateName()
-- TODO: UnitClass can return Unknown and the color can be nil. Having
-- the unit exist but not fully loaded is possibly a state we want to
-- handle more generally
local color = RAID_CLASS_COLORS[class] or {r=1, g=1, b=1}
local color = RAID_CLASS_COLORS[class] or { r = 1, g = 1, b = 1 }
self.name:SetTextColor(color.r, color.g, color.b)
else
self.name:SetTextColor(1, 1, 1)
end
self.name:SetText(UnitName(self.unit):sub(1, 5))
self.name:SetText(utf8_truncate((UnitName(self.unit)), 5))
end
--[[
-- UNIT_AURA
-- UNIT_CLASSIFICATION_CHANGED
-- UNIT_COMBAT
-- UNIT_CONNECTION
-- UNIT_DISPLAYPOWER
-- UNIT_FACTION
-- UNIT_FLAGS
-- UNIT_LEVEL
-- UNIT_MANA
-- UNIT_HEALTH_PREDICTION
-- UNIT_PHASE
]]--

View File

@@ -121,14 +121,14 @@ function UnitGroup:Sort()
table.sort(self.units, UnitFrameDefaultCompare)
local left, top, width, height = self.left, self.top, self.width, self.height
local maxFrameIndex = #self.units
for y=0,5 do
for x=0,4 do
local num = y*5 + x + 1
for y = 0, 5 do
for x = 0, 4 do
local num = y * 5 + x + 1
if num > maxFrameIndex then
return
end
local frame = self.units[num]
frame:SetPosition(left + (x-2)*width, top - y*height)
frame:SetPosition(left + (x - 2) * width, top - y * height)
end
end
end