diff --git a/.stylua.toml b/.stylua.toml new file mode 100644 index 0000000..394e884 --- /dev/null +++ b/.stylua.toml @@ -0,0 +1 @@ +indent_type = "Spaces" diff --git a/devloop b/devloop index e60727c..d4e7bb0 100755 --- a/devloop +++ b/devloop @@ -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 diff --git a/media/textures/bar_subtle_diagonal_bottom.tga b/media/textures/bar_subtle_diagonal_bottom.tga new file mode 100644 index 0000000..910382a Binary files /dev/null and b/media/textures/bar_subtle_diagonal_bottom.tga differ diff --git a/media/textures/bar_subtle_diagonal_top.tga b/media/textures/bar_subtle_diagonal_top.tga new file mode 100644 index 0000000..778c98c Binary files /dev/null and b/media/textures/bar_subtle_diagonal_top.tga differ diff --git a/media/textures/square_a.tga b/media/textures/square_a.tga deleted file mode 100644 index 7a1b635..0000000 Binary files a/media/textures/square_a.tga and /dev/null differ diff --git a/media/textures/square_b.tga b/media/textures/square_b.tga deleted file mode 100644 index 473ce1c..0000000 Binary files a/media/textures/square_b.tga and /dev/null differ diff --git a/src/OmicronFrames.toc b/src/OmicronFrames.toc index 4ea0dcc..7940f13 100644 --- a/src/OmicronFrames.toc +++ b/src/OmicronFrames.toc @@ -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 diff --git a/src/frames.lua b/src/frames.lua index b9336ea..45b1713 100644 --- a/src/frames.lua +++ b/src/frames.lua @@ -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 diff --git a/src/profiler.lua b/src/profiler.lua index 99e9657..581f84c 100644 --- a/src/profiler.lua +++ b/src/profiler.lua @@ -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, }) diff --git a/src/types/auralist.lua b/src/types/auralist.lua index 11daab4..558a486 100644 --- a/src/types/auralist.lua +++ b/src/types/auralist.lua @@ -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 = {} diff --git a/src/types/indicators/iconindicator.lua b/src/types/indicators/iconindicator.lua new file mode 100644 index 0000000..042f1b2 --- /dev/null +++ b/src/types/indicators/iconindicator.lua @@ -0,0 +1,173 @@ +-- Copyright 2025 +-- +-- 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 . +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 diff --git a/src/types/indicators/indicator.lua b/src/types/indicators/indicator.lua index 48badbc..a99fc6f 100644 --- a/src/types/indicators/indicator.lua +++ b/src/types/indicators/indicator.lua @@ -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 diff --git a/src/types/statusbar.lua b/src/types/statusbar.lua index e9f15e2..a221e69 100644 --- a/src/types/statusbar.lua +++ b/src/types/statusbar.lua @@ -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 diff --git a/src/types/triggers/auratrigger.lua b/src/types/triggers/auratrigger.lua index e5636c8..cef6135 100644 --- a/src/types/triggers/auratrigger.lua +++ b/src/types/triggers/auratrigger.lua @@ -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 diff --git a/src/types/triggers/multitrigger.lua b/src/types/triggers/multitrigger.lua index 71909e5..f065bb7 100644 --- a/src/types/triggers/multitrigger.lua +++ b/src/types/triggers/multitrigger.lua @@ -1,7 +1,7 @@ -- Copyright 2023 -- -- 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 . +-- Omicron Frames. If not, see . 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 - diff --git a/src/types/triggers/statustrigger.lua b/src/types/triggers/statustrigger.lua index 520d13a..c28ab32 100644 --- a/src/types/triggers/statustrigger.lua +++ b/src/types/triggers/statustrigger.lua @@ -1,7 +1,7 @@ -- Copyright 2023 -- -- 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 . +-- Omicron Frames. If not, see . 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) diff --git a/src/types/triggers/trigger.lua b/src/types/triggers/trigger.lua index 6a80bc8..dfc306b 100644 --- a/src/types/triggers/trigger.lua +++ b/src/types/triggers/trigger.lua @@ -1,7 +1,7 @@ -- Copyright 2023 -- -- 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 . +-- Omicron Frames. If not, see . 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 - diff --git a/src/types/types.lua b/src/types/types.lua index b3fec41..a036170 100644 --- a/src/types/types.lua +++ b/src/types/types.lua @@ -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 diff --git a/src/types/unitframe.lua b/src/types/unitframe.lua index 9c388b6..1d4cf6b 100644 --- a/src/types/unitframe.lua +++ b/src/types/unitframe.lua @@ -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)