Heatsink = LibStub("AceAddon-3.0"):NewAddon("Heatsink", "AceEvent-3.0", "AceConsole-3.0", "AceTimer-3.0", "LibBars-1.0", "LibSink-2.0") local L = LibStub:GetLibrary("AceLocale-3.0"):GetLocale("Heatsink") local BZR = LibStub("LibBabble-Zone-3.0"):GetReverseLookupTable() local media = LibStub("LibSharedMedia-3.0") local AceConfig = LibStub("AceConfig-3.0") local AceConfigDialog = LibStub("AceConfigDialog-3.0") local Gratuity = LibStub("LibGratuity-3.0") local itemSlots = {"Trinket0Slot", "Trinket1Slot", "HeadSlot"} local playerGUID, playerClass local function spellname(spell) local name = GetSpellInfo(spell) return name end local function GetLSMIndex(t, value) for k, v in pairs(media:List(t)) do if v == value then return k end end return nil end local defaults = { profile = { sinkOptions = {}, min = 3, max = 3600, growth = true, texture = "default", scale = 1, scalefactor = 1.2, width = 200, height = 10, font = "ABF", fontsize = 10, school = true, showSpells = true, showPet = true, showItems = true, showBags = true, showInternal = true, emphasize = true, emphasizeTime = 5, flash = false, flashTime = 5, position = { cooldown = {}, emphasis = {}, }, }, } local groupedCooldowns = { [spellname(1499)] = L["Traps"], -- 1499 Freezing Trap [spellname(13795)] = L["Traps"], -- 13795 Immolation Trap [spellname(13809)] = L["Traps"], -- 13909 Immolation Trap [spellname(13813)] = L["Traps"], -- 13813 Explosive Trap [spellname(34600)] = L["Traps"], -- 34600 Snake Trap [spellname(8042)] = L["Shocks"], -- 8042 Earth Shock [spellname(8056)] = L["Shocks"], -- 8056 Frost Shock [spellname(8050)] = L["Shocks"], -- 8050 Flame Shock [spellname(20230)] = L["The Big Three"], -- 20230 Retaliation [spellname(871)] = L["The Big Three"], -- 871 Shield Wall [spellname(1719)] = L["The Big Three"], -- 1719 Recklessness [spellname(498)] = L["Bubble"], -- 498 Divine Protection [spellname(642)] = L["Bubble"], -- 642 Divine Shield [spellname(2878)] = L["Turn"], -- 2878 Turn Undead [spellname(10326)] = L["Turn"], -- 10326 Turn Evil } local groupedBagCooldowns = { [L["Mana Jade"]] = L["Stones"], [L["Mana Ruby"]] = L["Stones"], [L["Mana Citrine"]] = L["Stones"], [L["Mana Agate"]] = L["Stones"], [L["Mana Emerald"]] = L["Stones"], [L["Healthstone$"]] = L["Stones"], [L["Potion"]] = L["Potions"], [L["Blue Ogre Brew Special"]]= L["Potions"], --Ogri'la [L["Red Ogre Brew Special"]] = L["Potions"], --Ogri'la [L["Blue Ogre Brew"]]= L["Potions"], --Ogri'la [L["Red Ogre Brew"]] = L["Potions"], --Ogri'la [L["Bottled Nethergon Energy"]] = L["Potions"], --TempistKeep [L["Bottled Nethergon Vapor"]] = L["Potions"], --TempistKeep [L["Cenarion Mana Salve"]] = L["Potions"], --CC [L["Cenarion Healing Salve"]] = L["Potions"], --CC [L["Major Healing Draught"]] = L["Potions"], --PvP [L["Major Mana Draught"]] = L["Potions"], --PvP [L["Superior Mana Draught"]] = L["Potions"], --PvP [L["Superior Healing Draught"]] = L["Potions"], --PvP } local chainedCooldowns = { [spellname(11958)] = { -- 11958 Cold Snap spellname(120), -- 120 Cone of Cold spellname(122), -- 122 Frost Nova spellname(614), -- 6143 Frost Ward spellname(11426), -- 11426 Ice Barrier spellname(45438), -- 45438 Ice Block spellname(31687), -- 31687 Summon Water Elemental }, [spellname(14185)] = { -- 14185 Preparation spellname(14177), -- 14177 Cold Blood spellname(1725), -- 1725 Distract spellname(5277), -- 5277 Evasion spellname(1966), -- 1966 Feint spellname(1776), -- 1776 Gouge spellname(1766), -- 1766 Kick spellname(408), -- 408 Kidney Shot spellname(2983), -- 2983 Sprint spellname(1856), -- 1856 Vanish }, [spellname(23989)] = { -- 23989 Readiness spellname(19577), -- 19577 Intimidation spellname(34026), -- 34026 Kill Command spellname(1513), -- 1513 Scare Beast spellname(19434), -- 19434 Aimed Shot spellname(3044), -- 3044 Arcane Shot spellname(5116), -- 5116 Concussive Shot spellname(20736), -- 20736 Distracting Shot spellname(1534), -- 1534 Flare spellname(2643), -- 2643 Multi-Shot spellname(3045), -- 3045 Rapid Fire spellname(19801), -- 19801 Tranquilizing Shot spellname(3034), -- 3034 Viper Sting spellname(1510), -- 1510 Volley spellname(19306), -- 19306 Counterattack spellname(19263), -- 19263 Deterrence spellname(781), -- 781 Disengage spellname(13813), -- 13813 Explosive Trap spellname(5384), -- 5384 Feign Death spellname(1499), -- 1499 Freezing Trap spellname(13809), -- 13809 Frost Trap spellname(13795), -- 13795 Immolation Trap spellname(34477), -- 34477 Misdirection spellname(1495), -- 1495 Mongoose Bite spellname(2973), -- 2973 Raptor Strike spellname(34600), -- 34600 Snake Trap spellname(19386), -- 19386 Wyvern Sting }, } local schoolGroups = { ["WARLOCK"] = { [L["Shadow"]] = spellname(686), -- 686 Shadow Bolt [L["Fire"]] = spellname(5676), -- 5676 Searing Pain }, ["MAGE"] = { [L["Arcane"]] = spellname(5143), -- 5343 Arcane Missiles [L["Fire"]] = spellname(133), -- 133 Fireball [L["Frost"]] = spellname(116), -- 116 Frostbolt }, ["DRUID"] = { [L["Nature"]] = spellname(5185), -- 5185 Healing Touch }, ["SHAMAN"] = { [L["Physical"]] = spellname(3738), -- 3738 Wrath of Air Totem [L["Fire"]] = spellname(8227), -- 8227 Flametongue Totem [L["Frost"]] = spellname(5394), -- 5394 Healing Stream Totem [L["Nature"]] = spellname(403), -- 403 Lightning Bolt }, ["PRIEST"] = { [L["Shadow"]] = spellname(589), -- 589 Shadow Word: Pain }, ["PALADIN"] = { [L["Holy"]] = spellname(635), -- 635 Holy Light }, } -- Lists the spells that have a default cooldown, and the school they belong to local schoolSpells = { ["WARLOCK"] = { [spellname(6229)] = L["Shadow"], -- 6229 Shadow Ward [spellname(5484)] = L["Shadow"], -- 5484 Howl of Terror [spellname(17877)] = L["Shadow"], -- 17877 Shadowburn [spellname(18540)] = L["Shadow"], -- 18540 Ritual of Doom [spellname(603)] = L["Shadow"], -- 603 Curse of Doom [spellname(29833)] = L["Shadow"], -- 29893 Ritual of Souls [spellname(18288)] = L["Shadow"], -- 18288 Amplify Curse [spellname(6789)] = L["Shadow"], -- 6789 Death Coil [spellname(29858)] = L["Shadow"], -- 29858 Soulshatter [spellname(1122)] = L["Shadow"], -- 1122 Inferno [spellname(6353)] = L["Fire"], -- 6353 Soul Fire [spellname(18708)] = L["Shadow"], -- 18708 Fel Domination }, ["MAGE"] = { [spellname(122)] = L["Frost"], -- 122 Frost Nova [spellname(120)] = L["Frost"], -- 120 Cone of Cold [spellname(6143)] = L["Frost"], -- 6143 Frost Ward [spellname(11426)] = L["Frost"], -- 11426 Ice Barrier [spellname(45438)] = L["Frost"], -- 45438 Ice Block [spellname(31687)] = L["Frost"], -- 31687 Summon Water Elemental [spellname(31661)] = L["Fire"], -- 31661 Dragon's Breath [spellname(543)] = L["Fire"], -- 543 Fire Ward [spellname(2136)] = L["Fire"], -- 2136 Fire Blast [spellname(1831)] = L["Fire"], -- 1831 Blast Wave [spellname(10059)] = L["Arcane"], -- 10059 Portal: Stormwind [spellname(11416)] = L["Arcane"], -- 11416 Portal: Ironforge [spellname(11419)] = L["Arcane"], -- 11419 Portal: Darnassus [spellname(32266)] = L["Arcane"], -- 32266 Portal: Exodar [spellname(11417)] = L["Arcane"], -- 11417 Portal: Orgrimmar [spellname(32267)] = L["Arcane"], -- 32267 Portal: Silvermoon [spellname(11420)] = L["Arcane"], -- 11420 Portal: Thunder Bluff [spellname(11418)] = L["Arcane"], -- 11418 Portal: Undercity [spellname(33691)] = L["Arcane"], -- 33691 Portal: Shattrath [spellname(1953)] = L["Arcane"], -- 1953 Blink [spellname(2139)] = L["Arcane"], -- 2139 Counterspell [spellname(12051)] = L["Arcane"], -- 12051 Evocation [spellname(12042)] = L["Arcane"], -- 12042 Arcane Power [spellname(66)] = L["Arcane"], -- 66 Invisibility }, ["DRUID"] = { [spellname(29166)] = L["Nature"], -- 29166 Innervate [spellname(20484)] = L["Nature"], -- 20484 Rebirth [spellname(16857)] = L["Nature"], -- 16857 Faerie Fire (Feral) [spellname(740)] = L["Nature"], -- 740 Tranquility [spellname(22812)] = L["Nature"], -- 22812 Barkskin [spellname(16689)] = L["Nature"], -- 16689 Nature's Grasp [spellname(16914)] = L["Nature"], -- 16914 Hurricane }, ["SHAMAN"] = { [spellname(16188)] = L["Physical"], -- 16188 Nature's Swiftness [spellname(30823)] = L["Physical"], -- 30823 Shamanistic Rage [spellname(17364)] = L["Physical"], -- 17364 Stormstrike [spellname(2894)] = L["Fire"], -- 2894 Fire Elemental Totem [spellname(1535)] = L["Fire"], -- 1535 Fire Nova Totem [spellname(8050)] = L["Fire"], -- 8050 Flame Shock [spellname(8056)] = L["Frost"], -- 8056 Frost Shock [spellname(16190)] = L["Frost"], -- 16190 Mana Tide Totem [spellname(2062)] = L["Nature"], -- 2062 Earth Elemental Totem [spellname(8042)] = L["Nature"], -- 8042 Earth Shock [spellname(556)] = L["Nature"], -- 556 Astral Recall [spellname(2825)] = L["Nature"], -- 2825 Bloodlust [spellname(2484)] = L["Nature"], -- 2484 Earthbind Totem [spellname(16166)] = L["Nature"], -- 16166 Elemental Mastery [spellname(32182)] = L["Nature"], -- 32182 Heroism [spellname(8177)] = L["Nature"], -- 8177 Grounding Totem [spellname(20608)] = L["Nature"], -- 20608 Reincarnation [spellname(5730)] = L["Nature"], -- 5730 Stoneclaw Totem [spellname(421)] = L["Nature"], -- 421 Chain Lightning }, ["PRIEST"] = { [spellname(586)] = L["Shadow"], -- 586 Fade [spellname(589)] = L["Shadow"], -- 589 Shadow Word: Pain [spellname(32379)] = L["Shadow"], -- 32379 Shadow Word: Death [spellname(8092)] = L["Shadow"], -- 8092 Mind Blast [spellname(15407)] = L["Shadow"], -- 15407 Mind Flay [spellname(34433)] = L["Shadow"], -- 34433 Shadowfiend [spellname(15286)] = L["Shadow"], -- 15286 Vampiric Embrace }, ["PALADIN"] = { [spellname(20473)] = L["Holy"], -- 20473 Holy Shock [spellname(642)] = L["Holy"], -- 642 Divine Shield [spellname(498)] = L["Holy"], -- 498 Divine Protection [spellname(31842)] = L["Holy"], -- 31842 Divine Illumination [spellname(20216)] = L["Holy"], -- 20216 Divine Favor [spellname(26573)] = L["Holy"], -- 26573 Consecration [spellname(633)] = L["Holy"], -- 633 Lay on Hands [spellname(2812)] = L["Holy"], -- 2812 Holy Wrath [spellname(24275)] = L["Holy"], -- 24275 Hammer of Wrath [spellname(879)] = L["Holy"], -- 879 Exorcism [spellname(2878)] = L["Holy"], -- 2878 Turn Undead [spellname(10326)] = L["Holy"], -- 10326 Turn Evil [spellname(31789)] = L["Holy"], -- 31789 Righteous Defense [spellname(1022)] = L["Holy"], -- 1022 Blessing of Protection [spellname(1044)] = L["Holy"], -- 1044 Blessing of Freedom [spellname(31884)] = L["Holy"], -- 31884 Avenging Wrath [spellname(20271)] = L["Holy"], -- 20271 Judgement [spellname(2812)] = L["Holy"], -- 2812 Holy Wrath [spellname(853)] = L["Holy"], -- 853 Hammer of Justice [spellname(19752)] = L["Holy"], -- 19752 Divine Intervention [spellname(6940)] = L["Holy"], -- 6940 Blessing of Sacrifice }, } local internalCooldowns = { [33370] = { -- 33370 Spell Haste time = 18, item = 27683, -- 27683 Quagmirran's Eye }, [34321] = { -- 34321 Call of the Nexus time = 45, item = 27683, -- 27683 Shiffar's Nexus-Horn }, [34775] = { -- 34775 Haste time = 20, item = 28830, -- 28830 Dragonspine Trophy }, [34585] = { -- 34585 Love Struck time = 30, item = 28579, -- 28578 Masquerade Gown }, [33649] = { -- 33649 Rage of the Unraveller time = 45, item = 28034, -- 28034 Heatsink of the Unraveller }, [33370] = { -- 33370 Spell Haste time = 45, item = 28190, -- 28190 Scarab of the Infinite Cycle }, [37174] = { -- 37174 Perceived Weakness time = 30, item = 30450, -- 30450 Warp-Spring Coil }, [38348] = { -- 38348 Unstable Currents time = 45, item = 30626, -- 30626 Sextant of Unstable Currents }, [34597] = { -- 34597 Power of Arcanagos time = 45, item = 28602, -- 28602 Robe of the Elder Scribes }, [42084] = { -- 42084 Fury of the Crashing Waves time = 45, item = 30627, -- 30627 Tsunami Talisman }, [35078] = { -- 35078 Band of the Eternal Defender time = 45, item = 29297, -- 29297 Band of the Eternal Defender }, [35081] = { -- 35081 Band of the Eternal Champion time = 45, item = 29301, -- 29301 Band of the Eternal Champion }, [35084] = { -- 35084 Band of the Eternal Sage time = 45, item = 29305, -- 29305 Band of the Eternal Sage }, [35087] = { -- 35987 Band of the Eternal Restorer time = 45, item = 29309, -- 29309 Band of the Eternal Restorer }, [43837] = { -- 43837 Enlightenment, Crystalforge Raiment 4-set bonus time = 60, }, [41435] = { -- 41435 The Twin Blades of Azzinoth time = 45, }, [45478] = { -- 45478 Light's Salvation time = 45, item = 34677, -- 34677 Shattered Sun Pendant of Restoration (Aldor) }, [45479] = { -- 45479 Light's Wrath time = 45, item = 34678, -- 34679 Shattered Sun Pendant of Acumen (Aldor) }, [45480] = { -- 45480 Light's Strength time = 45, item = 34679, -- 34679 Shattered Sun Pendant of Might (Aldor) }, [45432] = { -- 45432 Light's Ward time = 45, item = 34680, -- 34680 Shattered Sun Pendant of Resolve (Aldor) }, [45478] = { -- 45430 Arcane Surge time = 45, item = 34677, -- 34677 Shattered Sun Pendant of Restoration (Scryer) }, [45429] = { -- 45429 Arcane Bolt time = 45, item = 34678, -- 34679 Shattered Sun Pendant of Acumen (Scryer) }, [45428] = { -- 45428 Arcane Strike time = 45, item = 34679, -- 34679 Shattered Sun Pendant of Might (Scryer) }, [45431] = { -- 45431 Arcane Insight time = 45, item = 34680, -- 34680 Shattered Sun Pendant of Resolve (Scryer) }, [45058] = { -- 45058 Evasive Maneuvers time = 30, item = 34473, -- 34473 Commendation of Kael'thas }, [37656] = { -- 37656 Wisdom time = 45, item = 32496, -- 32496 Memento of Tyrande }, [45053] = { -- 45053 Disdain time = 45, item = 34472, -- 34472 Shard of Contempt }, } local arenas = { [BZR["Blade's Edge Arena"]] = true, [BZR["Nagrand Arena"]] = true, [BZR["Ruins of Lordaeron"]] = true, } local options = { type = "group", args = { toggle = { type = "execute", name = L["Toggle anchors"], desc = L["Toggle the bar anchor frames"], func = function() Heatsink:ToggleAnchors() end, }, test = { type = "execute", name = L["Test"], desc = L["Test bars"], func = function() Heatsink:RunTest() end, }, output = Heatsink:GetSinkAce3OptionsDataTable(), shockandawe = { name = L["Shock and Awe"], desc = L["Emphasis/Flash settings"], type = "group", args = { emphasize = { type = "toggle", name = L["Emphasize Bars"], desc = L["Move bars to the emphasis anchor"], get = function() return Heatsink.db.profile.emphasize end, set = function(info,v) Heatsink.db.profile.emphasize = v end, order = 1, }, emphasizeTime = { type = "range", name = L["Emphasize Time"], desc = L["Time at which to emphasize bars"], get = function() return Heatsink.db.profile.emphasizeTime end, set = function(info,v) Heatsink.db.profile.emphasizeTime = v end, min = 1, max = 60, step = 1, isPercent = false, order = 2, }, flash = { type = "toggle", name = L["Flash Bars"], desc = L["Flash the cooldown bars"], get = function() return Heatsink.db.profile.flash end, set = function(info,v) Heatsink.db.profile.flash = v Heatsink:UpdateAnchors() end, order = 3, }, flashTime = { type = "range", name = L["Flash Time"], desc = L["Time at which to start flashing"], get = function() return Heatsink.db.profile.flashTime end, set = function(info,v) Heatsink.db.profile.flashTime = v Heatsink:UpdateAnchors() end, min = 1, max = 60, step = 1, isPercent = false, order = 4, }, }, }, duration = { name = "Duration settings", desc = "Duration settings", type = "group", args = { min = { type = "input", name = L["Minimum duration"], desc = L["Minimum cooldown duration to display"], pattern = "%d+", get = function() return tostring(Heatsink.db.profile.min) end, set = function(info, v) Heatsink.db.profile.min = tonumber(v) end, order = 1, }, max = { type = "input", name = L["Maximum duration"], desc = L["Maximum cooldown duration to display"], pattern = "%d+", get = function() return tostring(Heatsink.db.profile.max) end, set = function(info, v) Heatsink.db.profile.max = tonumber(v) end, order = 2, }, }, }, bars = { type = "group", name = L["Bar settings"], desc = L["Bar settings"], args = { growth = { type = "toggle", name = L["Grow upwards"], desc = L["Toggle bars grow upwards/downwards from anchor"], get = function () return Heatsink.db.profile.growth end, set = function (info, v) Heatsink.db.profile.growth = v Heatsink:UpdateAnchors() end, order = 1, }, width = { type = "range", name = L["Width"], desc = L["Set the width of the bars"], get = function() return Heatsink.db.profile.width end, set = function(info, v) Heatsink.db.profile.width = v Heatsink:UpdateAnchors() end, min = 50, max = 500, step = 5, isPercent = false, order = 2, }, height = { type = "range", name = L["Height"], desc = L["Set the height of the bars"], get = function() return Heatsink.db.profile.height end, set = function(info, v) Heatsink.db.profile.height = v Heatsink:UpdateAnchors() end, min = 10, max = 100, step = 1, isPercent = false, order = 3, }, scale = { type = "range", name = L["Scale"], desc = L["Set the scale of the bars"], get = function() return Heatsink.db.profile.scale end, set = function(info, v) Heatsink.db.profile.scale = v Heatsink:UpdateAnchors() end, min = 0.1, max = 5, step = 0.01, isPercent = true, order = 4, }, scalefactor = { type = "range", name = L["Emphasis scale factor"], desc = L["Set the scale multiple of the emphasis bars"], get = function() return Heatsink.db.profile.scalefactor end, set = function(info, v) Heatsink.db.profile.scalefactor = v Heatsink:UpdateAnchors() end, min = 0.1, max = 5, step = 0.01, isPercent = false, order = 5, }, texture = { type = "select", name = L["Texture"], desc = L["Set the texture for the timer bars"], values = media:List("statusbar"), get = function(info) return GetLSMIndex("statusbar", Heatsink.db.profile.texture) end, set = function(info, value) Heatsink.db.profile.texture = media:List("statusbar")[value] Heatsink:UpdateAnchors() end, order = 6, }, font = { type = "select", name = L["Font"], desc = L["Set the bar font"], values = media:List("font"), get = function(info) return GetLSMIndex("font", Heatsink.db.profile.font) end, set = function(info, value) Heatsink.db.profile.font = media:List("font")[value] Heatsink:UpdateAnchors() end, order = 7, }, fontszie = { type = "range", name = L["Font Size"], desc = L["Set the bar font size"], min = 7, max = 48, step = 1, get = function(info) return Heatsink.db.profile.fontsize end, set = function(info, value) Heatsink.db.profile.fontsize = value Heatsink:UpdateAnchors() end, order = 8, }, }, order = 10, }, show = { type = "group", name = L["Show cooldowns"], desc = L["Toggle showing cooldown types"], args = { spells = { type = "group", name = L["Spells"], desc = L["Player spells cooldown options"], args = { enable = { type = "toggle", name = L["Enable player spells"], desc = L["Toggle showing player spells cooldowns"], get = function () return Heatsink.db.profile.showSpells end, set = function (info, v) Heatsink.db.profile.showSpells = v Heatsink:SPELL_UPDATE_COOLDOWN() end, order = 10, }, school = { type = "toggle", name = L["Show school"], desc = L["Spawns single bar if a school is locked out"], get = function () return Heatsink.db.profile.school end, set = function (info, v) Heatsink.db.profile.school = v Heatsink:ScanSpells() end, disabled = function () return not Heatsink.db.profile.showSpells end, order = 100, }, }, order = 10, }, petspells = { type = "toggle", name = L["Pet spells"], desc = L["Toggle showing pet cooldowns"], get = function () return Heatsink.db.profile.showPet end, set = function (info, v) Heatsink.db.profile.showPet = v Heatsink:PET_BAR_UPDATE_COOLDOWN() end, order = 20, }, equipped = { type = "toggle", name = L["Equipped items"], desc = L["Toggle showing equipped items cooldowns"], get = function () return Heatsink.db.profile.showItems end, set = function (info, v) Heatsink.db.profile.showItems = v end, order = 30, }, bags = { type = "toggle", name = L["Inventory items"], desc = L["Toggle showing inventory items cooldowns"], get = function () return Heatsink.db.profile.showBags end, set = function (info, v) Heatsink.db.profile.showBags = v end, order = 40, }, internal = { type = "toggle", name = L["Internal Cooldowns"], desc = L["Toggle showing item internal proc cooldowns"], get = function () return Heatsink.db.profile.showInternal end, set = function (info, v) Heatsink.db.profile.showInternal = v end, order = 40, }, }, }, }, } AceConfig:RegisterOptionsTable("Heatsink", options) function Heatsink:OnInitialize() self.db = LibStub("AceDB-3.0"):New("HeatsinkDB", defaults, "Default") self:RegisterChatCommand("heatsink", function() LibStub("AceConfigDialog-3.0"):Open("Heatsink") end ) local optFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("Heatsink", "Heatsink") options.args.profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db) self:SetSinkStorage(self.db.profile.sinkOptions) self.db.RegisterCallback(self, "OnProfileChanged", "UpdateAnchors") self.db.RegisterCallback(self, "OnProfileCopied", "UpdateAnchors") self.db.RegisterCallback(self, "OnProfileReset", "UpdateAnchors") media.RegisterCallback(self, "LibSharedMedia_Registered", "UpdateAnchors") media.RegisterCallback(self, "LibSharedMedia_SetGlobal", "UpdateAnchors") self:CreateAnchor("cooldown") self:CreateAnchor("emphasis") self.active = {} self.spells = {} self.petspells = {} end function Heatsink:OnEnable() self:RegisterEvent("SPELLS_CHANGED") self:RegisterEvent("SPELL_UPDATE_COOLDOWN") self:RegisterEvent("PET_BAR_UPDATE_COOLDOWN") self:RegisterEvent("ZONE_CHANGED_NEW_AREA") self:RegisterEvent("BAG_UPDATE_COOLDOWN") self:RegisterEvent("UNIT_INVENTORY_CHANGED") self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED") self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") playerGUID = UnitGUID("player") playerClass = select(2, UnitClass("player")) self:ScanSpells() self:ScanPetSpells() if self.db.profile.showItems then self:ScanItems() end if self.db.profile.showBags then self:ScanBags() end end function Heatsink:OnDisable() self:UnregisterAllEvents() end function Heatsink:CreateAnchor(anchor) self[anchor] = self:NewBarGroup(anchor, nil, self.db.profile.width, self.db.profile.height) self[anchor]:ClearAllPoints() self[anchor].id = anchor self[anchor].RegisterCallback(self, "AnchorClicked") self[anchor].RegisterCallback(self, "TimerFinished") self[anchor]:HideAnchor() self[anchor]:SetSortFunction(Heatsink.SortBars) self:UpdateAnchor(anchor) end function Heatsink:RunTest() local icon = select(3, GetSpellInfo(34712)) or nil local start = GetTime() self:StartBar("Heatsink 5", start, 5, icon, false) self:StartBar("Heatsink 10", start, 10, icon, false) self:StartBar("Heatsink 15", start, 15, icon, false) end function Heatsink:AnchorClicked(callback, group, button) self:SavePosition(group.id) if button == "RightButton" then group:HideAnchor() group:Lock() end end function Heatsink:TimerFinished(callback, group, bar) for k,v in pairs(self.active) do if v == bar.name then table.remove(self.active, k) end end self:Pour(bar.name.." "..L["Ready!"], 0.2, 0.7, 0.9, nil, nil, nil, nil, nil, nil) end function Heatsink:ToggleAnchors() if self.cooldown.button:IsVisible() then self.cooldown:HideAnchor() self.cooldown:Lock() else self.cooldown:ShowAnchor() self.cooldown:Unlock() end if self.emphasis.button:IsVisible() then self.emphasis:HideAnchor() self.emphasis:Lock() else self.emphasis:ShowAnchor() self.emphasis:Unlock() end end function Heatsink:SavePosition(anchor) local p,_,pr,px,py = self[anchor]:GetPoint() self.db.profile.position[anchor].p = p self.db.profile.position[anchor].pr = pr self.db.profile.position[anchor].px = px self.db.profile.position[anchor].py = py end function Heatsink:UpdateAnchor(anchor) self[anchor]:SetTexture(media:Fetch("statusbar", self.db.profile.texture)) self[anchor]:SetFont(media:Fetch("font", self.db.profile.font), self.db.profile.fontsize) self[anchor]:SetWidth(self.db.profile.width) self[anchor]:SetHeight(self.db.profile.height) self[anchor]:ReverseGrowth(self.db.profile.growth) self[anchor]:SetFlashPeriod(0) if self.db.profile.flash then self[anchor]:SetFlashPeriod(self.db.profile.flashTime) end if not self.db.profile.position[anchor] then self.db.profile.position[anchor] = {} end if self.db.profile.position[anchor].p and self.db.profile.position[anchor].pr and self.db.profile.position[anchor].px and self.db.profile.position[anchor].py then self[anchor]:ClearAllPoints() self[anchor]:SetPoint(self.db.profile.position[anchor].p, UIParent, self.db.profile.position[anchor].pr, self.db.profile.position[anchor].px, self.db.profile.position[anchor].py) else self[anchor]:SetPoint("CENTER", UIParent, "CENTER", 0, 0) end if anchor == "emphasis" then self[anchor]:SetScale(self.db.profile.scalefactor) self[anchor]:SetColorAt(1.00, 1.0, 0.0, 0.0, 1) else self[anchor]:SetScale(self.db.profile.scale) self[anchor]:SetColorAt(1.00, 0.0, 1.0, 0.0, 1) self[anchor]:SetColorAt(0.50, 1.0, 0.7, 0.0, 1) self[anchor]:SetColorAt(0.00, 1.0, 0.0, 0.0, 1) end end function Heatsink:UpdateAnchors() self:UpdateAnchor("cooldown") self:UpdateAnchor("emphasis") end function Heatsink.Emphasize(bar) local duration = bar.maxValue - (GetTime() - bar.startTime) Heatsink.emphasis:NewTimerBar(bar.name, bar.name, duration, duration, bar.icon:GetTexture()) bar:Fade() end function Heatsink.SortBars(a, b) if a.isTimer ~= b.isTimer then return a.isTimer end if a.maxValue == b.maxValue then if a.value == b.value then return a.name > b.name else return a.value > b.value end else return a.maxValue > b.maxValue end end function Heatsink:StartBar(name, start, duration, icon, track) for k,v in pairs(self.active) do if v == name then return end end if track then table.insert(self.active, name) end local remaining = duration - (GetTime() - (start or 0)) self.cooldown:NewTimerBar(name, name, remaining, duration, icon) local bar = self.cooldown:GetBar(name) if self.db.profile.emphasize then if bar then bar:RegisterTimeLeftTrigger((self.db.profile.emphasizeTime or 5), Heatsink.Emphasize) end end end function Heatsink:SPELLS_CHANGED() self:ScanSpells() self:ScanPetSpells() end function Heatsink:SPELL_UPDATE_COOLDOWN() if not self.db.profile.showSpells then return end local schoolCooldown = {} if self.db.profile.school and schoolGroups[playerClass] then for school, spell in pairs(schoolGroups[playerClass]) do local data = self.spells[spell] if data then local start, duration, active = GetSpellCooldown(data.id, BOOKTYPE_SPELL) if active == 1 and duration > self.db.profile.min and duration <= self.db.profile.max then schoolCooldown[school] = true self:StartBar(school.." "..L["School"], start, duration, true) end end end end for name, spell in pairs(self.spells) do local start, duration, active = GetSpellCooldown(spell.id, BOOKTYPE_SPELL) if active == 1 and duration > self.db.profile.min and duration <= self.db.profile.max then if not self.db.profile.school or not (spell.school and schoolCooldown[spell.school]) then self:StartBar(name, start, duration, spell.icon, true) end end end end function Heatsink:PET_BAR_UPDATE_COOLDOWN() if not self.db.profile.showPet then return end for name, spell in pairs(self.petspells) do local start, duration, active = GetSpellCooldown(spell.id, BOOKTYPE_PET) if active == 1 and duration > self.db.profile.min and duration <= self.db.profile.max then self:StartBar(name, start, duration, spell.icon, true) end end end function Heatsink:UNIT_SPELLCAST_SUCCEEDED(event, unit, spellName) if unit == "player" and chainedCooldowns[spellName] then for _, chainedSpellName in pairs(chainedCooldowns[spellName]) do -- local id = self:GetBarId(chainedSpellName) local bar = self.cooldown:GetBar(chainedSpellName) if bar then for k,v in pairs(self.active) do if v == bar.name then table.remove(self.active, k) end end bar:Fade() end end end end function Heatsink:ZONE_CHANGED_NEW_AREA() local zone = GetRealZoneText() if not zone then self:ScheduleTimer(self.ZONE_CHANGED_NEW_AREA, 5, self) elseif arenas[zone] then local bars = self.cooldown:GetBars() if bars then for id in pairs(bars) do local bar = self.cooldown:GetBar(id) if bar then for k,v in pairs(self.active) do if v == bar.name then table.remove(self.active, k) end end bar:Fade() end end end end end function Heatsink:UNIT_INVENTORY_CHANGED(unit) if unit == "player" then if self.db.profile.showItems then self:ScanItems() end if self.db.profile.showBags then self:ScanBags() end end end function Heatsink:BAG_UPDATE_COOLDOWN(container) if self.db.profile.showItems then self:ScanItems() end if self.db.profile.showBags then self:ScanBags() end end function Heatsink:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, eventType, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, spellID, spellName, spellFlags, ...) if not self.db.profile.showInternal or dstGUID ~= playerGUID then return end if (eventType ~= "SPELL_AURA_APPLIED" and eventType ~= "SPELL_CAST_SUCESS") then return end if not internalCooldowns[spellID] then return end local duration = internalCooldowns[spellID].time if not duration then return end local start = GetTime() local name, icon if internalCooldowns[spellID].item then local itemName, _, _, _, _, _, _, _, _, itemIcon = GetItemInfo(internalCooldowns[spellID].item) name = itemName icon = itemIcon else name = spellName icon = select(3, GetSpellInfo(spellID)) end self:StartBar(name, start, duration, icon, true) end function Heatsink:ScanSpells() spells = {} local i = 1 local spellName = GetSpellInfo(i, BOOKTYPE_SPELL) local previousSpellName local schoolEnabled = self.db.profile.school while spellName do if spellName ~= previousSpellName then for k, v in pairs(groupedCooldowns) do if spellName == k then spellName = v break end end Gratuity:SetSpell(i, BOOKTYPE_SPELL) if Gratuity:Find("cooldown", 2, 3, true) then self.spells[spellName] = { id = i, icon = select(3, GetSpellInfo(i, BOOKTYPE_SPELL)), startTime = 0, } if schoolEnabled and schoolSpells[playerClass] and schoolSpells[playerClass][spellName] then self.spells[spellName]["school"] = schoolSpells[playerClass][spellName] end else if schoolEnabled and schoolGroups[playerClass] then for school, spell in pairs(schoolGroups[playerClass]) do if (spellName == spell) then self.spells[spellName] = { id = i, icon = select(3, GetSpellInfo(i, BOOKTYPE_SPELL)), startTime = 0, school = school, } break end end end end end i = i + 1 previousSpellName = spellName spellName = GetSpellInfo(i, BOOKTYPE_SPELL) end self:SPELL_UPDATE_COOLDOWN() end function Heatsink:ScanPetSpells() local i = 1 local spellName = GetSpellInfo(i, BOOKTYPE_PET) local previousSpellName while spellName do if spellName ~= previousSpellName then Gratuity:SetSpell(i, BOOKTYPE_PET) if Gratuity:Find("cooldown", 2, 3, true) then self.petspells[spellName] = { id = i, icon = select(3, GetSpellInfo(i, BOOKTYPE_PET)), startTime = 0, } end end i = i + 1 previousSpellName = spellName spellName = GetSpellInfo(i, BOOKTYPE_PET) end self:PET_BAR_UPDATE_COOLDOWN() end function Heatsink:ScanItems() for _, slotName in pairs(itemSlots) do local slotId = GetInventorySlotInfo(slotName) local start, duration, active = GetInventoryItemCooldown("player", slotId) if active == 1 and duration > self.db.profile.min and duration <= self.db.profile.max then local name = select(3, GetInventoryItemLink("player", slotId):find("Hitem[^|]+|h%[([^[]+)%]")) local icon = GetInventoryItemTexture("player", slotId) self:StartBar(name, start, duration, icon, nil) end end end function Heatsink:ScanBags() for bag = 0,4 do local slots = GetContainerNumSlots(bag) for slot = 1,slots do local start, duration, active = GetContainerItemCooldown(bag,slot) if active == 1 and duration > self.db.profile.min and duration <= self.db.profile.max then local name = select(3, GetContainerItemLink(bag,slot):find("Hitem[^|]+|h%[([^[]+)%]")) local icon = GetContainerItemInfo(bag, slot) for pattern, replacement in pairs(groupedBagCooldowns) do if (string.find(name, pattern)) then name = replacement break end end self:StartBar(name, start, duration, icon, nil) end end end end