WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   AddOn Help/Support (https://www.wowinterface.com/forums/forumdisplay.php?f=3)
-   -   Edit omnibar (https://www.wowinterface.com/forums/showthread.php?t=53410)

hasnogaems 05-05-16 10:24 PM

Edit omnibar
 
I want to edit widely known addon http://mods.curse.com/addons/wow/omnibar to make it ability icon fade out only when it's on cooldown and have full opacity when it off cooldown. Currently there is reversed behavior. Im pretty sure it's can be done, but i can't find in code where this opacity settings is. Can anyone help me please?

hasnogaems 05-06-16 01:57 PM

Im also gonna post main OmniBar.lua code if it pertinent.
Code:

-- OmniBar by Jordon

OmniBar = LibStub("AceAddon-3.0"):NewAddon("OmniBar", "AceEvent-3.0", "AceHook-3.0")

OmniBar.cooldowns = {
        [47476]  = { default = true,  duration = 60,  class = "DEATHKNIGHT" },                                  -- Strangulate
        [47481]  = { default = false, duration = 60,  class = "DEATHKNIGHT", specID = { 252 } },                -- Gnaw (Ghoul)
        [47482]  = { default = false, duration = 30,  class = "DEATHKNIGHT", specID = { 252 } },                -- Leap (Ghoul)
        [47528]  = { default = true,  duration = 15,  class = "DEATHKNIGHT" },                                  -- Mind Freeze
        [48707]  = { default = false, duration = 45,  class = "DEATHKNIGHT" },                                  -- Anti-Magic Shell
        [48743]  = { default = false, duration = 120, class = "DEATHKNIGHT" },                                  -- Death Pact
        [48792]  = { default = false, duration = 180, class = "DEATHKNIGHT" },                                  -- Icebound Fortitude
        [49028]  = { default = false, duration = 90,  class = "DEATHKNIGHT", specID = { 250 } },                -- Dancing Rune Weapon
        [49039]  = { default = false, duration = 120, class = "DEATHKNIGHT" },                                  -- Lichborne
        [49576]  = { default = false, duration = 25,  class = "DEATHKNIGHT" },                                  -- Death Grip
        [51052]  = { default = false, duration = 120, class = "DEATHKNIGHT" },                                  -- Anti-Magic Zone
        [51271]  = { default = false, duration = 60,  class = "DEATHKNIGHT", specID = { 251 } },                -- Pillar of Frost
        [55233]  = { default = false, duration = 60,  class = "DEATHKNIGHT", specID = { 250 } },                -- Vampiric Blood
        [77606]  = { default = false, duration = 30,  class = "DEATHKNIGHT" },                                  -- Dark Simulacrum
        [91802]  = { default = true,  duration = 30,  class = "DEATHKNIGHT", specID = { 252 } },                -- Shambling Rush
        [96268]  = { default = false, duration = 30,  class = "DEATHKNIGHT" },                                  -- Death's Advance
        [108194] = { default = false, duration = 30,  class = "DEATHKNIGHT" },                                  -- Asphyxiate
        [108201] = { default = false, duration = 120, class = "DEATHKNIGHT" },                                  -- Desecrated Ground
        [152279] = { default = false, duration = 120, class = "DEATHKNIGHT" },                                  -- Breath of Sindragosa
        [498]    = { default = false, duration = 30,  class = "PALADIN" },                                      -- Divine Protection
        [642]    = { default = false, duration = 150, class = "PALADIN" },                                      -- Divine Shield
        [853]    = { default = false, duration = 60,  class = "PALADIN" },                                      -- Hammer of Justice
            [105593] = { parent = 853, duration = 30 },                                                          -- Fist of Justice
        [1022]  = { default = false, duration = 300, class = "PALADIN", charges = 2 },                          -- Hand of Protection
        [1044]  = { default = false, duration = 25,  class = "PALADIN", charges = 2 },                          -- Hand of Freedom
        [6940]  = { default = false, duration = { default = 90, [65] = 110 }, class = "PALADIN", charges = 2 }, -- Hand of Sacrifice
        [20066]  = { default = false, duration = 15,  class = "PALADIN" },                                      -- Repentance
        [31821]  = { default = false, duration = 180, class = "PALADIN", specID = { 65 } },                      -- Devotion Aura
        [31884]  = { default = false, duration = 120, class = "PALADIN" },                                      -- Avenging Wrath
        [96231]  = { default = true,  duration = 15,  class = "PALADIN" },                                      -- Rebuke
        [114039] = { default = false, duration = 30,  class = "PALADIN" },                                      -- Hand of Purity
        [114157] = { default = false, duration = 60,  class = "PALADIN" },                                      -- Execution Sentence
        [115750] = { default = false, duration = 120, class = "PALADIN" },                                      -- Blinding Light
        [871]    = { default = false, duration = 180, class = "WARRIOR", specID = { 73 } },                      -- Shield Wall
        [1719]  = { default = false, duration = 180, class = "WARRIOR", specID = { 71, 72 } },                  -- Recklessness
        [3411]  = { default = false, duration = 30,  class = "WARRIOR" },                                      -- Intervene
        [5246]  = { default = false, duration = 90,  class = "WARRIOR" },                                      -- Intimidating Shout
        [6544]  = { default = false, duration = 45,  class = "WARRIOR" },                                      -- Heroic Leap
        [6552]  = { default = true,  duration = 15,  class = "WARRIOR" },                                      -- Pummel
        [18499]  = { default = false, duration = 30,  class = "WARRIOR" },                                      -- Berserker Rage
        [23920]  = { default = false, duration = 25,  class = "WARRIOR" },                                      -- Spell Reflection
                [114028] = { parent = 23920, duration = 30 },                                                        -- Mass Spell Reflection
        [46968]  = { default = false, duration = 20,  class = "WARRIOR" },                                      -- Shockwave
        [107570] = { default = false, duration = 30,  class = "WARRIOR" },                                      -- Storm Bolt
        [107574] = { default = false, duration = 90,  class = "WARRIOR" },                                      -- Avatar
        [114029] = { default = false, duration = 30,  class = "WARRIOR" },                                      -- Safeguard
        [118000] = { default = false, duration = 60,  class = "WARRIOR" },                                      -- Dragon Roar
        [118038] = { default = false, duration = 120, class = "WARRIOR", specID = { 71, 72 } },                  -- Die by the Sword
        [99]    = { default = false, duration = 30,  class = "DRUID" },                                        -- Disorienting Roar
        [740]    = { default = false, duration = 180, class = "DRUID" },                                        -- Tranquility
        [5211]  = { default = false, duration = 50,  class = "DRUID" },                                        -- Bash
        [22812]  = { default = false, duration = 60,  class = "DRUID", specID = { 102, 104, 105 } },            -- Barkskin
        [33891]  = { default = false, duration = 180, class = "DRUID", specID = { 105 } },                      -- Incarnation: Tree of Life
        [50334]  = { default = false, duration = 180, class = "DRUID", specID = { 103, 104 } },                  -- Berserk
        [61336]  = { default = false, duration = 180, class = "DRUID", specID = { 103, 104 }, charges = 2 },    -- Survival Instincts
        [78675]  = { default = true,  duration = 60,  class = "DRUID", specID = { 102 } },                      -- Solar Beam
        [102280] = { default = false, duration = 30,  class = "DRUID" },                                        -- Displacer Beast
        [102342] = { default = false, duration = 60,  class = "DRUID", specID = { 105 } },                      -- Ironbark
        [102359] = { default = false, duration = 30,  class = "DRUID" },                                        -- Mass Entanglement
        [102543] = { default = false, duration = 180, class = "DRUID", specID = { 103 } },                      -- Incarnation: King of the Jungle
        [102560] = { default = false, duration = 180, class = "DRUID", specID = { 102 } },                      -- Incarnation: Chosen of Elune
        [106839] = { default = true,  duration = 15,  class = "DRUID", specID = { 103, 104 } },                  -- Skull Bash
        [108291] = { default = false, duration = 360, class = "DRUID" },                                        -- Heart of the Wild (Balance)
                [108292] = { parent = 108291 },                                                                      -- Heart of the Wild (Feral)
                [108293] = { parent = 108291 },                                                                      -- Heart of the Wild (Guardian)
                [108294] = { parent = 108291 },                                                                      -- Heart of the Wild (Resto)
        [112071] = { default = false, duration = 180, class = "DRUID", specID = { 102 } },                      -- Celestial Alignment
        [124974] = { default = false, duration = 90,  class = "DRUID" },                                        -- Nature's Vigil
        [132158] = { default = false, duration = 60,  class = "DRUID", specID = { 105 } },                      -- Nature's Swiftness
        [132469] = { default = false, duration = 30,  class = "DRUID" },                                        -- Typhoon
        [159630] = { default = false, duration = 90,  class = "PRIEST", specID = { 256, 257 } },                -- Shadow Magic
        [8122]  = { default = false, duration = 30,  class = "PRIEST" },                                        -- Psychic Scream
        [15487]  = { default = true,  duration = 45,  class = "PRIEST", specID = { 256, 258 } },                -- Silence
        [33206]  = { default = false, duration = 120, class = "PRIEST", specID = { 256 } },                      -- Pain Suppression
        [47585]  = { default = false, duration = 120, class = "PRIEST", specID = { 258 } },                      -- Dispersion
        [47788]  = { default = false, duration = 180, class = "PRIEST", specID = { 257 } },                      -- Guardian Spirit
        [64044]  = { default = false, duration = 45,  class = "PRIEST", specID = { 258 } },                      -- Psychic Horror
        [73325]  = { default = false, duration = 90,  class = "PRIEST" },                                        -- Leap of Faith
        [5484]  = { default = false, duration = 40,  class = "WARLOCK" },                                      -- Howl of Terror
        [6360]  = { default = false, duration = 25,  class = "WARLOCK" },                                      -- Whiplash
        [6789]  = { default = false, duration = 45,  class = "WARLOCK" },                                      -- Mortal Coil
        [19505]  = { default = false, duration = 15,  class = "WARLOCK" },                                      -- Devour Magic (Felhunter)
        [30283]  = { default = false, duration = 30,  class = "WARLOCK" },                                      -- Shadowfury
        [48020]  = { default = false, duration = 26,  class = "WARLOCK" },                                      -- Demonic Portal
        [89766]  = { default = false, duration = 30,  class = "WARLOCK" },                                      -- Axe Toss
        [108482] = { default = false, duration = 120, class = "WARLOCK" },                                      -- Unbound Will
        [119910] = { default = true,  duration = 24,  class = "WARLOCK" },                                      -- Spell Lock (Command Demon)
            [19647]  = { parent = 119910 },                                                                      -- Spell Lock (Felhunter)
            [119911] = { parent = 119910 },                                                                      -- Optical Blast (Command Demon)
            [115781] = { parent = 119910 },                                                                      -- Optical Blast (Observer)
            [132409] = { parent = 119910 },                                                                      -- Spell Lock (Grimoire of Sacrifice)
            [171138] = { parent = 119910 },                                                                      -- Shadow Lock (Doomguard)
            [171139] = { parent = 119910 },                                                                      -- Shadow Lock (Grimoire of Sacrifice)
            [171140] = { parent = 119910 },                                                                      -- Shadow Lock (Command Demon)
        [111859] = { default = false, duration = 120, class = "WARLOCK" },                                      -- Grimoire: Imp
        [111896] = { default = false, duration = 120, class = "WARLOCK" },                                      -- Grimoire: Succubus
        [111897] = { default = true,  duration = 120, class = "WARLOCK" },                                      -- Grimoire: Felhunter
        [77801]  = { default = false, duration = 120, class = "WARLOCK", charges = 2 },                          -- Dark Soul
                [113858] = { parent = 77801 },                                                                      -- Dark Soul: Instability
                [113860] = { parent = 77801 },                                                                      -- Dark Soul: Misery
                [113861] = { parent = 77801 },                                                                      -- Dark Soul: Knowledge
        [115284] = { default = false, duration = 15,  class = "WARLOCK" },                                      -- Clone Magic (Observer)
        [115770] = { default = false, duration = 25,  class = "WARLOCK" },                                      -- Fellash
        [8143]  = { default = false, duration = 60,  class = "SHAMAN" },                                        -- Tremor Totem
        [8177]  = { default = false, duration = 25,  class = "SHAMAN" },                                        -- Grounding Totem
        [30823]  = { default = false, duration = 60,  class = "SHAMAN", specID = { 262, 263 } },                -- Shamanistic Rage
        [51490]  = { default = false, duration = 45,  class = "SHAMAN", specID = { 262, 263 } },                -- Thunderstorm
        [51514]  = { default = false, duration = 45,  class = "SHAMAN" },                                        -- Hex
        [57994]  = { default = true,  duration = 12,  class = "SHAMAN" },                                        -- Wind Shear
        [98008]  = { default = false, duration = 180, class = "SHAMAN" },                                        -- Spirit Link Totem
        [108269] = { default = false, duration = 45,  class = "SHAMAN" },                                        -- Capacitor Totem
        [108271] = { default = false, duration = 90,  class = "SHAMAN" },                                        -- Astral Shift
        [108273] = { default = false, duration = 60,  class = "SHAMAN" },                                        -- Windwalk Totem
        [108285] = { default = false, duration = 180, class = "SHAMAN" },                                        -- Call of the Elements
        [114049] = { default = false, duration = 180, class = "SHAMAN" },                                        -- Ascendance
                [114050] = { parent = 114049 },                                                                      -- Ascendance (Elemental)
                [114051] = { parent = 114049 },                                                                      -- Ascendance (Enhancement)
                [114052] = { parent = 114049 },                                                                      -- Ascendance (Restoration)
        [1499]  = { default = false, duration = { default = 20, [253] = 30, [254] = 30 }, class = "HUNTER" },  -- Freezing Trap
            [60192] = { parent = 1499 },                                                                        -- Freezing Trap (Trap Launcher)
        [13813]  = { default = false, duration = { default = 20, [253] = 30, [254] = 30 }, class = "HUNTER" },  -- Explosive Trap
            [82939] = { parent = 13813 },                                                                        -- Explosive Trap (Trap Launcher)
        [19263]  = { default = false, duration = 180, class = "HUNTER", charges = 2 },                          -- Deterrence
        [19386]  = { default = false, duration = 45,  class = "HUNTER" },                                        -- Wyvern Sting
        [19574]  = { default = false, duration = 60,  class = "HUNTER", specID = { 253 } },                      -- Bestial Wrath
        [53480]  = { default = false, duration = 60,  class = "HUNTER" },                                        -- Roar of Sacrifice
        [131894] = { default = false, duration = 60,  class = "HUNTER" },                                        -- A Murder of Crows
        [147362] = { default = true,  duration = 24,  class = "HUNTER" },                                        -- Counter Shot
        [66]    = { default = false, duration = 300, class = "MAGE" },                                          -- Invisibility
        [1953]  = { default = false, duration = 15,  class = "MAGE" },                                          -- Blink
        [2139]  = { default = true,  duration = 24,  class = "MAGE" },                                          -- Counterspell
        [11129]  = { default = false, duration = 45,  class = "MAGE", specID = { 63 } },                        -- Combustion
        [11958]  = { default = false, duration = 180, class = "MAGE" },                                          -- Cold Snap
        [12043]  = { default = false, duration = 90,  class = "MAGE", specID = { 62 } },                        -- Presence of Mind
        [12472]  = { default = false, duration = 180, class = "MAGE", specID = { 64 } },                        -- Icy Veins
        [31661]  = { default = false, duration = 20,  class = "MAGE", specID = { 63 } },                        -- Dragon's Breath
        [44572]  = { default = false, duration = 30,  class = "MAGE", specID = { 64 } },                        -- Deep Freeze
        [45438]  = { default = false, duration = 300, class = "MAGE" },                                          -- Ice Block
        [84714]  = { default = false, duration = 60,  class = "MAGE", specID = { 64 } },                        -- Frozen Orb
        [102051] = { default = false, duration = 20,  class = "MAGE" },                                          -- Frostjaw
        [113724] = { default = false, duration = 45,  class = "MAGE" },                                          -- Ring of Frost
        [157997] = { default = false, duration = 25,  class = "MAGE", specID = { 64 }, charges = 2 },            -- Ice Nova
        [408]    = { default = false, duration = 20,  class = "ROGUE" },                                        -- Kidney Shot
        [1766]  = { default = true,  duration = 15,  class = "ROGUE" },                                        -- Kick
        [1856]  = { default = false, duration = { default = 60, [261] = 120 }, class = "ROGUE" },              -- Vanish
        [2094]  = { default = false, duration = 120, class = "ROGUE" },                                        -- Blind
        [2983]  = { default = false, duration = 60,  class = "ROGUE" },                                        -- Sprint
        [5277]  = { default = false, duration = 180, class = "ROGUE" },                                        -- Evasion
        [13750]  = { default = false, duration = 180, class = "ROGUE", specID = { 260 } },                      -- Adrenaline Rush
        [14185]  = { default = false, duration = 300, class = "ROGUE" },                                        -- Preparation
        [31224]  = { default = false, duration = 60,  class = "ROGUE" },                                        -- Cloak of Shadows
        [36554]  = { default = false, duration = 20,  class = "ROGUE" },                                        -- Shadow Step
        [51690]  = { default = false, duration = 120, class = "ROGUE", specID = { 260} },                        -- Killing Spree
        [51713]  = { default = false, duration = 60,  class = "ROGUE", specID = { 261 } },                      -- Shadow Dance
        [74001]  = { default = false, duration = 120, class = "ROGUE" },                                        -- Combat Readiness
        [76577]  = { default = false, duration = 180, class = "ROGUE" },                                        -- Smoke Bomb
        [113656] = { default = false, duration = 25,  class = "MONK", specID = { 269 } },                        -- Fists of Fury
        [115176] = { default = false, duration = 180, class = "MONK", specID = { 268, 269 } },                  -- Zen Meditation
        [115203] = { default = false, duration = 180, class = "MONK" },                                          -- Fortifying Brew
        [115310] = { default = false, duration = 180, class = "MONK" },                                          -- Revival
        [116705] = { default = true,  duration = 15,  class = "MONK" },                                          -- Spear Hand Strike
        [116844] = { default = false, duration = 45,  class = "MONK" },                                          -- Ring of Peace
        [116849] = { default = false, duration = 55,  class = "MONK", specID = { 270 } },                        -- Life Cocoon
        [119381] = { default = false, duration = 45,  class = "MONK" },                                          -- Leg Sweep
        [119996] = { default = false, duration = 25,  class = "MONK" },                                          -- Transcendence: Transfer
        [122470] = { default = false, duration = 90,  class = "MONK", specID = { 269 } },                        -- Touch of Karma
        [122783] = { default = false, duration = 90,  class = "MONK" },                                          -- Diffuse Magic
        [137562] = { default = false, duration = 120, class = "MONK" },                                          -- Nimble Brew
}

local cooldowns = OmniBar.cooldowns

local order = {
        ["DEATHKNIGHT"] = 1,
        ["PALADIN"] = 2,
        ["WARRIOR"] = 3,
        ["DRUID"] = 4,
        ["PRIEST"] = 5,
        ["WARLOCK"] = 6,
        ["SHAMAN"] = 7,
        ["HUNTER"] = 8,
        ["MAGE"] = 9,
        ["ROGUE"] = 10,
        ["MONK"] = 11,
}

local resets = {
        --[[ Summon Felhunter
            - Spell Lock
          ]]
        [691] = { 119910 },

        --[[ Cold Snap
            - Ice Block
            - Presence of Mind
            - Dragon's Breath
          ]]
        [11958] = { 45438, 12043, 31661 },

        --[[ Preparation
            - Sprint
            - Vanish
            - Evasion
          ]]
        [14185] = { 2983, 1856, 5277 },

        --[[ Call of the Elements
            - Tremor Totem
            - Grounding Totem
            - Capacitor Totem
            - Windwalk Totem
          ]]
        [108285] = { 8143, 8177, 108269, 108273 },
}

-- Defaults
local defaults = {
        size                = 40,
        columns              = 8,
        padding              = 2,
        locked              = false,
        center              = false,
        border              = true,
        highlightTarget      = true,
        highlightFocus      = false,
        growUpward          = true,
        showUnused          = false,
        adaptive            = false,
        unusedAlpha          = 0.45,
        swipeAlpha          = 0.65,
        cooldownCount        = true,
        arena                = true,
        ratedBattleground    = true,
        battleground        = true,
        world                = true,
        ashran              = true,
        multiple            = true,
        glow                = true,
        tooltips            = true,
        names                = false,
        maxIcons            = 500,
        align                = "CENTER",
}

local SETTINGS_VERSION = 3

local MAX_DUPLICATE_ICONS = 5

local BASE_ICON_SIZE = 36

local ASHRAN_MAP_ID = 978

local _

OmniBar.index = 1

OmniBar.bars = {}

function OmniBar:OnEnable()
        if not OmniBarDB or not OmniBarDB.version or OmniBarDB.version < SETTINGS_VERSION then OmniBarDB = { version = SETTINGS_VERSION } end

        self.db = LibStub("AceDB-3.0"):New("OmniBarDB", { profile = { bars = {} } }, true)
       
        self.index = 1

        for i = #self.bars, 1, -1 do
                OmniBar:Delete(self.bars[i].key, true)
                table.remove(self.bars, i)
        end

        for key,_ in pairs(self.db.profile.bars) do
                self:Initialize(key)
                self.index = self.index + 1
        end

        -- Create a default bar if none exist
        if self.index == 1 then
                self:Initialize("OmniBar1", "OmniBar")
                self.index = 2
        end

        if not self.registered then
                self.db.RegisterCallback(self, "OnProfileChanged", "OnEnable")
                self.db.RegisterCallback(self, "OnProfileCopied", "OnEnable")
                self.db.RegisterCallback(self, "OnProfileReset", "OnEnable")

                self:SetupOptions()
                self.registered = true
        end

        for key,_ in pairs(self.db.profile.bars) do
                self:AddBarToOptions(key)
        end

        self:Refresh(true)
end

function OmniBar:Delete(key, keepProfile)
        local bar = _G[key]
        if not bar then return end
        bar:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
        bar:UnregisterEvent("PLAYER_ENTERING_WORLD")
        bar:UnregisterEvent("ZONE_CHANGED_NEW_AREA")
        bar:UnregisterEvent("PLAYER_TARGET_CHANGED")
        bar:UnregisterEvent("PLAYER_FOCUS_CHANGED")
        bar:UnregisterEvent("PLAYER_REGEN_DISABLED")
        bar:UnregisterEvent("ARENA_OPPONENT_UPDATE")
        bar:UnregisterEvent("ARENA_PREP_OPPONENT_SPECIALIZATIONS")
        bar:UnregisterEvent("UPDATE_BATTLEFIELD_SCORE")
        bar:UnregisterEvent("UPDATE_BATTLEFIELD_STATUS")
        bar:Hide()
        if not keepProfile then self.db.profile.bars[key] = nil end
        self.options.args.bars.args[key] = nil
        LibStub("AceConfigRegistry-3.0"):NotifyChange("OmniBar")
end

function OmniBar:Initialize(key, name)
        if not self.db.profile.bars[key] then
                self.db.profile.bars[key] = { name = name }
                for a,b in pairs(defaults) do
                        self.db.profile.bars[key][a] = b
                end
        end

        local f = _G[key] or CreateFrame("Frame", key, UIParent, "OmniBarTemplate")
        f:Show()
        f.settings = self.db.profile.bars[key]
        f.settings.align = f.settings.align or "CENTER"
        f.settings.maxIcons = f.settings.maxIcons or 500
        f.key = key
        f.icons = {}
        f.active = {}
        f.cooldowns = cooldowns
        f.detected = {}
        f.specs = {}
        f.BASE_ICON_SIZE = BASE_ICON_SIZE
        f.numIcons = 0
        f:RegisterForDrag("LeftButton")

        local name = f.settings.name
        f.anchor.text:SetText(name)

        -- Load the settings
        OmniBar_LoadSettings(f)

        -- Create the icons
        for spellID,_ in pairs(cooldowns) do
                if OmniBar_IsSpellEnabled(f, spellID) then
                        OmniBar_CreateIcon(f)
                end
        end

        -- Create the duplicate icons
        for i = 1, MAX_DUPLICATE_ICONS do
                OmniBar_CreateIcon(f)
        end

        OmniBar_ShowAnchor(f)
        OmniBar_RefreshIcons(f)
        OmniBar_UpdateIcons(f)
        OmniBar_Center(f)

        f:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
        f:RegisterEvent("PLAYER_ENTERING_WORLD")
        f:RegisterEvent("ZONE_CHANGED_NEW_AREA")
        f:RegisterEvent("PLAYER_TARGET_CHANGED")
        f:RegisterEvent("PLAYER_FOCUS_CHANGED")
        f:RegisterEvent("PLAYER_REGEN_DISABLED")
        f:RegisterEvent("ARENA_OPPONENT_UPDATE")
        f:RegisterEvent("ARENA_PREP_OPPONENT_SPECIALIZATIONS")
        f:RegisterEvent("UPDATE_BATTLEFIELD_SCORE")
        f:RegisterEvent("UPDATE_BATTLEFIELD_STATUS")

        table.insert(self.bars, f)
end


function OmniBar:Create()

        local key

        while true do
                key = "OmniBar"..self.index
                self.index = self.index + 1
                if not self.db.profile.bars[key] then
                        self:Initialize(key, "OmniBar "..self.index - 1)
                        self:AddBarToOptions(key, true)
                        return
                end
        end
       
end

function OmniBar:Refresh(full)
        for key,_ in pairs(self.db.profile.bars) do
                local f = _G[key]
                if f then
                        f.container:SetScale(f.settings.size/BASE_ICON_SIZE)
                        if full then
                                OmniBar_OnEvent(f, "PLAYER_ENTERING_WORLD")
                        else
                                OmniBar_LoadPosition(f)
                                OmniBar_UpdateIcons(f)
                                OmniBar_Center(f)
                        end
                end
        end
end

local Masque = LibStub and LibStub("Masque", true)

for spellID,_ in pairs(cooldowns) do
        local name, _, icon = GetSpellInfo(spellID)
        cooldowns[spellID].icon = icon
        cooldowns[spellID].name = name
end

-- create a lookup table to translate spec names into IDs
local specNames = {}
for classID = 1, MAX_CLASSES do
        local _, classToken = GetClassInfoByID(classID)
        specNames[classToken] = {}
        for i = 1, GetNumSpecializationsForClassID(classID) do
                local id, name = GetSpecializationInfoForClassID(classID, i)
                specNames[classToken][name] = id
        end
end

local function IsHostilePlayer(unit)
        if not unit then return end
        local reaction = UnitReaction("player", unit)
        if not reaction then return end -- out of range
        return UnitIsPlayer(unit) and reaction < 4 and not UnitIsPossessed(unit)
end

function OmniBar_ShowAnchor(self)
        if self.disabled or self.settings.locked or #self.active > 0 then
                self.anchor:Hide()
        else
                local width = self.anchor.text:GetWidth() + 29
                self.anchor:SetSize(width, 30)
                self.anchor:Show()
        end
end

function OmniBar_CreateIcon(self)
        if InCombatLockdown() then return end
        self.numIcons = self.numIcons + 1
        local name = self:GetName()
        local key = name.."Icon"..self.numIcons
        local f = _G[key] or CreateFrame("Button", key, _G[name.."Icons"], "OmniBarButtonTemplate")
        table.insert(self.icons, f)
end

local function SpellBelongsToSpec(spellID, specID)
        if not specID then return true end
        if not cooldowns[spellID].specID then return true end
        for i = 1, #cooldowns[spellID].specID do
                if cooldowns[spellID].specID[i] == specID then return true end
        end
        return false
end

function OmniBar_AddIconsByClass(self, class, sourceGUID, specID)
        for spellID, spell in pairs(cooldowns) do
                if OmniBar_IsSpellEnabled(self, spellID) and spell.class == class and SpellBelongsToSpec(spellID, specID) then
                        OmniBar_AddIcon(self, spellID, sourceGUID, nil, true, nil, specID)
                end
        end
end

local function IconIsSource(iconGUID, guid)
        if not guid then return end
        if string.len(iconGUID) == 1 then
                -- arena target
                return UnitGUID("arena"..iconGUID) == guid
        end
        return iconGUID == guid
end

function OmniBar_UpdateBorders(self)
        for i = 1, #self.active do
                local border
                local guid = self.active[i].sourceGUID
                if guid then
                        if self.settings.highlightFocus and IconIsSource(guid, UnitGUID("focus")) then
                                self.active[i].FocusTexture:SetAlpha(1)
                                border = true
                        else
                                self.active[i].FocusTexture:SetAlpha(0)
                        end
                        if self.settings.highlightTarget and IconIsSource(guid, UnitGUID("target")) then
                                self.active[i].FocusTexture:SetAlpha(0)
                                self.active[i].TargetTexture:SetAlpha(1)
                                border = true
                        else
                                self.active[i].TargetTexture:SetAlpha(0)
                        end
                else
                        local class = select(2, UnitClass("focus"))
                        if self.settings.highlightFocus and class and IsHostilePlayer("focus") and class == self.active[i].class then
                                self.active[i].FocusTexture:SetAlpha(1)
                                border = true
                        else
                                self.active[i].FocusTexture:SetAlpha(0)
                        end
                        class = select(2, UnitClass("target"))
                        if self.settings.highlightTarget and class and IsHostilePlayer("target") and class == self.active[i].class then
                                self.active[i].FocusTexture:SetAlpha(0)
                                self.active[i].TargetTexture:SetAlpha(1)
                                border = true
                        else
                                self.active[i].TargetTexture:SetAlpha(0)
                        end
                end

                -- Set dim
                self.active[i]:SetAlpha(self.settings.unusedAlpha and self.active[i].cooldown:GetCooldownTimes() == 0 and not border and
                        self.settings.unusedAlpha or 1)
        end
end

function OmniBar_UpdateArenaSpecs(self)
        if self.zone ~= "arena" then return end
        for i = 1, 5 do
                local specID = GetArenaOpponentSpec(i)
                if specID and specID > 0 then
                        local name = GetUnitName("arena"..i, true)
                        if name then self.specs[name] = specID end
                end
        end
end

function OmniBar_SetZone(self, refresh)
        local disabled = self.disabled
        local _, zone = IsInInstance()
        if zone == "none" then
                SetMapToCurrentZone()
                zone = GetCurrentMapAreaID()
        end

        self.zone = zone
        local rated = IsRatedBattleground()
        self.disabled = (zone == "arena" and not self.settings.arena) or
                (rated and not self.settings.ratedBattleground) or
                (zone == "pvp" and not self.settings.battleground and not rated) or
                (zone == ASHRAN_MAP_ID and not self.settings.ashran) or
                (zone ~= "arena" and zone ~= "pvp" and zone ~= ASHRAN_MAP_ID and not self.settings.world)

        if refresh or disabled ~= self.disabled then
                OmniBar_LoadPosition(self)
                OmniBar_RefreshIcons(self)
                OmniBar_UpdateIcons(self)
                OmniBar_ShowAnchor(self)
                if zone == "arena" and not self.disabled then
                        wipe(self.detected)
                        wipe(self.specs)
                        OmniBar_OnEvent(self, "ARENA_OPPONENT_UPDATE")
                end
        end
       
end

function OmniBar_OnEvent(self, event, ...)
        if event == "COMBAT_LOG_EVENT_UNFILTERED" then
                local _, event, _, sourceGUID, sourceName, sourceFlags, _,_,_,_,_, spellID = ...
                if self.disabled then return end
                if event == "SPELL_CAST_SUCCESS" and bit.band(sourceFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) ~= 0 then
                        if cooldowns[spellID] then
                                OmniBar_UpdateArenaSpecs(self)
                                OmniBar_AddIcon(self, spellID, sourceGUID, sourceName)
                        end

                        -- Check if we need to reset any cooldowns
                        if resets[spellID] then
                                for i = 1, #self.active do
                                        if self.active[i] and self.active[i].spellID and self.active[i].sourceGUID and self.active[i].sourceGUID == sourceGUID and self.active[i].cooldown:IsVisible() then
                                                -- cooldown belongs to this source
                                                for j = 1, #resets[spellID] do
                                                        if resets[spellID][j] == self.active[i].spellID then
                                                                self.active[i].cooldown:Hide()
                                                                OmniBar_CooldownFinish(self.active[i].cooldown, true)
                                                                return
                                                        end
                                                end
                                        end
                                end
                        end
                end

        elseif event == "PLAYER_ENTERING_WORLD" then
                OmniBar_SetZone(self, true)
               
        elseif event == "ZONE_CHANGED_NEW_AREA" then
                OmniBar_SetZone(self, true)

        elseif event == "UPDATE_BATTLEFIELD_STATUS" then -- IsRatedBattleground() doesn't return valid response until this event
                OmniBar_SetZone(self)

        elseif event == "UPDATE_BATTLEFIELD_SCORE" then
                for i = 1, GetNumBattlefieldScores() do
                        local name, _,_,_,_,_,_,_, classToken, _,_,_,_,_,_, talentSpec = GetBattlefieldScore(i)
                        if name and specNames[classToken] and specNames[classToken][talentSpec] then
                                self.specs[name] = specNames[classToken][talentSpec]
                        end
                end

        elseif event == "ARENA_PREP_OPPONENT_SPECIALIZATIONS" or event == "ARENA_OPPONENT_UPDATE" then
                if self.disabled or not self.settings.adaptive then return end

                for i = 1, 5 do
                        local specID = GetArenaOpponentSpec(i)
                        if specID and specID > 0 then
                                -- only add icons if show unused is checked
                                if not self.settings.showUnused then return end
                                if not self.detected[i] then
                                        local class = select(7, GetSpecializationInfoByID(specID))
                                        OmniBar_AddIconsByClass(self, class, i, specID)
                                        self.detected[i] = class
                                end
                        end
                end

        elseif event == "PLAYER_TARGET_CHANGED" or event == "PLAYER_FOCUS_CHANGED" or event == "PLAYER_REGEN_DISABLED" then
                if self.disabled then return end

                -- update icon borders
                OmniBar_UpdateBorders(self)

                -- we don't need to add in arena
                if self.zone == "arena" then return end

                -- only add icons if show adaptive is checked
                if not self.settings.showUnused or not self.settings.adaptive then return end

                -- only add icons when we're in combat
                --if event == "PLAYER_TARGET_CHANGED" and not InCombatLockdown() then return end

                local unit = "playertarget"
                if IsHostilePlayer(unit) then
                        local guid = UnitGUID(unit)
                        local _, class = UnitClass(unit)
                        if class then
                                if self.detected[guid] then return end
                                self.detected[guid] = class
                                OmniBar_AddIconsByClass(self, class)
                        end
                end
        end
end

function OmniBar_LoadSettings(self)
       
        -- Set the scale
        self.container:SetScale(self.settings.size/BASE_ICON_SIZE)

        OmniBar_LoadPosition(self)
        OmniBar_RefreshIcons(self)
        OmniBar_UpdateIcons(self)
        OmniBar_Center(self)
end

function OmniBar_SavePosition(self, set)
        local point, relativeTo, relativePoint, xOfs, yOfs = self:GetPoint()
        local frameStrata = self:GetFrameStrata()
        relativeTo = relativeTo and relativeTo:GetName() or "UIParent"
        if set then
                if set.point then point = set.point end
                if set.relativeTo then relativeTo = set.relativeTo end
                if set.relativePoint then relativePoint = set.relativePoint end
                if set.xOfs then xOfs = set.xOfs end
                if set.yOfs then yOfs = set.yOfs end
                if set.frameStrata then frameStrata = set.frameStrata end
        end

        if not self.settings.position then
                self.settings.position = {}
        end
        self.settings.position.point = point
        self.settings.position.relativeTo = relativeTo
        self.settings.position.relativePoint = relativePoint
        self.settings.position.xOfs = xOfs
        self.settings.position.yOfs = yOfs
        self.settings.position.frameStrata = frameStrata
end

function OmniBar_LoadPosition(self)
        self:ClearAllPoints()
        if self.settings.position then
                local relativeTo = self.settings.position.relativeTo and self.settings.position.relativeTo or "UIParent"
                self:SetPoint(self.settings.position.point, self.settings.position.relativeTo, self.settings.position.relativePoint,
                        self.settings.position.xOfs, self.settings.position.yOfs)
                if not self.settings.position.frameStrata then self.settings.position.frameStrata = "MEDIUM" end
                self:SetFrameStrata(self.settings.position.frameStrata)
        else
                self:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
                OmniBar_SavePosition(self)
        end
end

function OmniBar_IsSpellEnabled(self, spellID)
        if not spellID then return end
        -- Check for an explicit rule
        local key = "spell"..spellID
        if type(self.settings[key]) == "boolean" then
                if self.settings[key] then
                        return true
                end
        elseif not self.settings.noDefault and cooldowns[spellID].default then
                -- Not user-set, but a default cooldown
                return true
        end
end

function OmniBar_Center(self)
        local parentWidth = UIParent:GetWidth()
        local clamp = self.settings.center and (1 - parentWidth)/2 or 0
        self:SetClampRectInsets(clamp, -clamp, 0, 0)
        clamp = self.settings.center and (self.anchor:GetWidth() - parentWidth)/2 or 0
        self.anchor:SetClampRectInsets(clamp, -clamp, 0, 0)
end

function OmniBar_CooldownFinish(self, force)
        local icon = self:GetParent()
        if icon.cooldown and icon.cooldown:GetCooldownTimes() > 0 and not force then return end -- not complete
        local charges = icon.charges
        if charges then
                charges = charges - 1
                if charges > 0 then
                        -- remove a charge
                        icon.charges = charges
                        icon.Count:SetText(charges)
                        OmniBar_StartCooldown(icon:GetParent():GetParent(), icon, GetTime())
                        return
                end
        end

        local bar = icon:GetParent():GetParent()

        local flash = icon.flashAnim
        local newItemGlowAnim = icon.newitemglowAnim

        if flash:IsPlaying() or newItemGlowAnim:IsPlaying() then
                flash:Stop()
                newItemGlowAnim:Stop()
        end

        if not bar.settings.showUnused then
                icon:Hide()
        else
                if icon.TargetTexture:GetAlpha() == 0 and
                        icon.FocusTexture:GetAlpha() == 0 and
                        bar.settings.unusedAlpha then
                                icon:SetAlpha(bar.settings.unusedAlpha)
                end
        end
        bar:StopMovingOrSizing()
        OmniBar_Position(bar)
end

function OmniBar_RefreshIcons(self)
        -- Hide all the icons
        for i = 1, self.numIcons do
                if self.icons[i].MasqueGroup then
                        --self.icons[i].MasqueGroup:Delete()
                        self.icons[i].MasqueGroup = nil
                end
                self.icons[i].TargetTexture:SetAlpha(0)
                self.icons[i].FocusTexture:SetAlpha(0)
                self.icons[i].flash:SetAlpha(0)
                self.icons[i].NewItemTexture:SetAlpha(0)
                self.icons[i].cooldown:SetCooldown(0, 0)
                self.icons[i].cooldown:Hide()
                self.icons[i]:Hide()
        end
        wipe(self.active)

        if self.disabled then return end

        if self.settings.showUnused and not self.settings.adaptive then
                for spellID,_ in pairs(cooldowns) do
                        if OmniBar_IsSpellEnabled(self, spellID) then
                                OmniBar_AddIcon(self, spellID, nil, nil, true)
                        end
                end
        end
        OmniBar_Position(self)
end

function OmniBar_StartCooldown(self, icon, start)
        icon.cooldown:SetCooldown(start, icon.duration)
        icon.cooldown.finish = start + icon.duration
        icon.cooldown:SetSwipeColor(0, 0, 0, self.settings.swipeAlpha or 0.65)
        icon:SetAlpha(1)
end


function OmniBar_AddIcon(self, spellID, sourceGUID, sourceName, init, test, specID)
        -- Check for parent spellID
        local originalSpellID = spellID
        if cooldowns[spellID].parent then spellID = cooldowns[spellID].parent end

        if not OmniBar_IsSpellEnabled(self, spellID) then return end

        local icon, duplicate

        -- Try to reuse a visible frame
        for i = 1, #self.active do
                if self.active[i].spellID == spellID then
                        duplicate = true
                        -- check if we can use this icon, but not when initializing arena opponents
                        if not init or self.zone ~= "arena" then
                                -- use icon if not bound to a sourceGUID
                                if not self.active[i].sourceGUID then
                                        duplicate = nil
                                        icon = self.active[i]
                                        break
                                end

                                -- if it's the same source, reuse the icon
                                if sourceGUID and IconIsSource(self.active[i].sourceGUID, sourceGUID) then
                                        duplicate = nil
                                        icon = self.active[i]
                                        break
                                end

                        end
                end
        end

        -- We couldn't find a visible frame to reuse, try to find an unused
        if not icon then
                if #self.active >= self.settings.maxIcons then return end
                if not self.settings.multiple and duplicate then return end
                for i = 1, #self.icons do
                        if not self.icons[i]:IsVisible() then
                                icon = self.icons[i]
                                icon.specID = nil
                                break
                        end
                end
        end

        -- We couldn't find a frame to use
        if not icon then return end

        local now = GetTime()

        if specID then
                icon.specID = specID
        else
                if sourceName and sourceName ~= COMBATLOG_FILTER_STRING_UNKNOWN_UNITS and self.specs[sourceName] then
                        icon.specID = self.specs[sourceName]
                end
        end

        icon.class = cooldowns[spellID].class
        icon.sourceGUID = sourceGUID
        icon.icon:SetTexture(cooldowns[spellID].icon)
        icon.spellID = spellID
        icon.added = now

        if icon.charges and cooldowns[originalSpellID].charges and icon:IsVisible() then
                local start, duration = icon.cooldown:GetCooldownTimes()
                if icon.cooldown.finish and icon.cooldown.finish - GetTime() > 1 then
                        -- add a charge
                        local charges = icon.charges + 1
                        icon.charges = charges
                        icon.Count:SetText(charges)
                        if self.settings.glow then
                                icon.flashAnim:Play()
                                icon.newitemglowAnim:Play()
                        end
                        return icon
                end
        elseif cooldowns[originalSpellID].charges then
                icon.charges = 1
                icon.Count:SetText("1")
        else
                icon.charges = nil
                icon.Count:SetText(nil)
        end

        local name = self.settings.names and sourceGUID and select(6, GetPlayerInfoByGUID(sourceGUID))
        if test and self.settings.names then name = "Name" end
        icon.Name:SetText(name)

        if cooldowns[originalSpellID].duration then
                if type(cooldowns[originalSpellID].duration) == "table" then
                        if icon.specID and cooldowns[originalSpellID].duration[icon.specID] then
                                icon.duration = cooldowns[originalSpellID].duration[icon.specID]
                        else
                                icon.duration = cooldowns[originalSpellID].duration.default
                        end
                else
                        icon.duration = cooldowns[originalSpellID].duration
                end
        else -- child doesn't have a custom duration, use parent
                if type(cooldowns[spellID].duration) == "table" then
                        if icon.specID and cooldowns[spellID].duration[icon.specID] then
                                icon.duration = cooldowns[spellID].duration[icon.specID]
                        else
                                icon.duration = cooldowns[spellID].duration.default
                        end
                else
                        icon.duration = cooldowns[spellID].duration
                end
        end

        -- We don't want duration to be too long if we're just testing
        if test then icon.duration = math.random(5,30) end

        -- Masque
        if Masque then
                icon.MasqueGroup = Masque:Group("OmniBar", cooldowns[spellID].name)
                icon.MasqueGroup:AddButton(icon, {
                        FloatingBG = false,
                        Icon = icon.icon,
                        Cooldown = icon.cooldown,
                        Flash = false,
                        Pushed = false,
                        Normal = icon:GetNormalTexture(),
                        Disabled = false,
                        Checked = false,
                        Border = _G[icon:GetName().."Border"],
                        AutoCastable = false,
                        Highlight = false,
                        Hotkey = false,
                        Count = false,
                        Name = false,
                        Duration = false,
                        AutoCast = false,
                })
        end

        icon:Show()

        if not init then
                OmniBar_StartCooldown(self, icon, now)
                if self.settings.glow then
                        icon.flashAnim:Play()
                        icon.newitemglowAnim:Play()
                end
        end

        return icon
end

function OmniBar_UpdateIcons(self)
        for i = 1, self.numIcons do
                -- Set show text
                self.icons[i].cooldown:SetHideCountdownNumbers(not self.settings.cooldownCount and true or false)
                self.icons[i].cooldown.noCooldownCount = self.settings.cooldownCount and false

                -- Set swipe alpha
                self.icons[i].cooldown:SetSwipeColor(0, 0, 0, self.settings.swipeAlpha or 0.65)

                -- Set border
                if self.settings.border then
                        self.icons[i].icon:SetTexCoord(0, 0, 0, 1, 1, 0, 1, 1)
                else
                        self.icons[i].icon:SetTexCoord(0.07, 0.9, 0.07, 0.9)
                end

                -- Set dim
                self.icons[i]:SetAlpha(self.settings.unusedAlpha and self.icons[i].cooldown:GetCooldownTimes() == 0 and
                        self.settings.unusedAlpha or 1)

                -- Masque
                if self.icons[i].MasqueGroup then self.icons[i].MasqueGroup:ReSkin() end

        end
end

function OmniBar_Test(self)
        self.disabled = nil
        OmniBar_RefreshIcons(self)
        for k,v in pairs(cooldowns) do
                OmniBar_AddIcon(self, k, nil, nil, nil, true)
        end
end

local function ExtractDigits(str)
        if not str then return 0 end
        if type(str) == "number" then return str end
        local num = str:gsub("%D", "")
        return tonumber(num) or 0
end

function OmniBar_Position(self)
        local numActive = #self.active
        if numActive == 0 then
                -- Show the anchor if needed
                OmniBar_ShowAnchor(self)
                return
        end

        -- Keep cooldowns together by class
        if self.settings.showUnused then
                table.sort(self.active, function(a, b)
                        local x, y = ExtractDigits(a.sourceGUID), ExtractDigits(b.sourceGUID)
                        if a.class == b.class then
                                if x < y then return true end
                                if x == y then return a.spellID < b.spellID end
                        end
                        return order[a.class] < order[b.class]
                end)
        else
                -- if we aren't showing unused, just sort by added time
                table.sort(self.active, function(a, b) return a.added == b.added and a.spellID < b.spellID or a.added < b.added end)
        end

        local count, rows = 0, 1
        local grow = self.settings.growUpward and 1 or -1
        local padding = self.settings.padding and self.settings.padding or 0
        for i = 1, numActive do
                if self.settings.locked then
                        self.active[i]:EnableMouse(false)
                else
                        self.active[i]:EnableMouse(true)
                end
                self.active[i]:ClearAllPoints()
                local columns = self.settings.columns and self.settings.columns > 0 and self.settings.columns < numActive and
                        self.settings.columns or numActive
                if i > 1 then
                        count = count + 1
                        if count >= columns then
                                if self.settings.align == "CENTER" then
                                        self.active[i]:SetPoint("CENTER", self.anchor, "CENTER", (-BASE_ICON_SIZE-padding)*(columns-1)/2, (BASE_ICON_SIZE+padding)*rows*grow)
                                else
                                        self.active[i]:SetPoint(self.settings.align, self.anchor, self.settings.align, 0, (BASE_ICON_SIZE+padding)*rows*grow)
                                end
                               
                                count = 0
                                rows = rows + 1
                        else
                                if self.settings.align == "RIGHT" then
                                        self.active[i]:SetPoint("TOPRIGHT", self.active[i-1], "TOPLEFT", -1 * padding, 0)
                                else
                                        self.active[i]:SetPoint("TOPLEFT", self.active[i-1], "TOPRIGHT", padding, 0)
                                end
                        end
                       
                else
                        if self.settings.align == "CENTER" then
                                self.active[i]:SetPoint("CENTER", self.anchor, "CENTER", (-BASE_ICON_SIZE-padding)*(columns-1)/2, 0)
                        else
                                self.active[i]:SetPoint(self.settings.align, self.anchor, self.settings.align, 0, 0)
                        end
                end
        end
        OmniBar_ShowAnchor(self)
end

function OmniBar:Test()
        for key,_ in pairs(self.db.profile.bars) do
                OmniBar_Test(_G[key])
        end
end

SLASH_OmniBar1 = "/ob"
SLASH_OmniBar2 = "/omnibar"
SlashCmdList.OmniBar = function()
        InterfaceOptionsFrame_OpenToCategory("OmniBar")
        InterfaceOptionsFrame_OpenToCategory("OmniBar")
end


hasnogaems 05-12-16 02:38 PM

Come on guys! It's can't be so hard!

Resike 05-12-16 02:43 PM

Quote:

Originally Posted by hasnogaems (Post 314698)
Come on guys! It's can't be so hard!

It's not, swap all SetAlpha(1) to SetAlpha(0) and SetAlpha(0) to SetAlpha(1).

hasnogaems 06-02-16 03:28 AM

I did it, and it's basically broke now. Unused ability icons look pretty weird http://imgur.com/M6CS6uo and cooldown icon just stop showing now. Unused icon currently just dissapear and cooldown icond doesnt show up when ability was used. Thank you for answer anyway, sincerely, and sorry for my messy english. Still looking for help with my issue doe.

hasnogaems 11-18-19 10:23 PM

I can assume this addon will do https://www.wowinterface.com/downloa...o.php?id=22826
Did not tried it yet.


All times are GMT -6. The time now is 12:20 AM.

vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI