WoWInterface SVN NightWatch

[/] [trunk/] [NightWatch.lua] - Rev 40

Compare with Previous | Blame | View Log

--
-- NightWatch
--
-- A custom spell watcher/reporter by Nightyniight of Frostmourne-US.
--
-- Inspired by "tricks message" by Intermission.
--
--

local addon = LibStub("AceAddon-3.0"):NewAddon("NightWatch", "AceConsole-3.0", "AceEvent-3.0")
local ACD = LibStub("AceConfigDialog-3.0")

-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded.
-- Listed here for Mikk's FindGlobals script.
-- GLOBALS: LibStub

local _G = getfenv(0)
local pairs = _G.pairs
local print = _G.print
local next = _G.next
local format = _G.string.format
local string_upper = _G.string.upper
local string_gsub = _G.string.gsub
local type = _G.type

local wipe = _G.wipe
local SendChatMessage = _G.SendChatMessage
local UnitInRaid = _G.UnitInRaid
local UnitInParty = _G.UnitInParty
local GetNumRaidMembers = _G.GetNumRaidMembers
local GetNumPartyMembers = _G.GetNumPartyMembers
local InCombatLockdown = _G.InCombatLockdown
local GetSpellInfo = _G.GetSpellInfo
local UnitName = _G.UnitName


local addonVersion = GetAddOnMetadata("NightWatch", "Version")
local dbVers = 8

local playerName = UnitName("player")
local firstEnable = true

local db
local clipboard = {}
local sink = {}
local i

for i = 1, 4 do
        sink[i] = addon:NewModule("Sink" .. i , "LibSink-2.0")
end

local function Print(msg)
        print(format("|cffffff7fnw|r: %s",msg))
end

function addon:defaultMsgFormat()
        return {
                chat =                  "<%C> %f %s%t",
                chatTarget =    " on <<%t>>",
                whisper =               "<%C> %f %s%t",
                whisperTarget = " on YOU",
        }
end

local Default_Profile = {
        profile = {
                enabled = true,
                spells = {},
                sinkStorage = {
                        [1] = { sink20OutputSink = "ChatFrame", },
                        [2] = { sink20OutputSink = "None", },
                        [3] = { sink20OutputSink = "None", },
                        [4] = { sink20OutputSink = "None", },
                },
                customMsgFormat = addon:defaultMsgFormat(),
                -- flagMap is used as a translation lookup and to help generate the default flag options for each watched spell.
                flagMap = {
                        SPELL_CAST_START = "starts casting",
                        SPELL_CAST_SUCCESS = "cast",
                        SPELL_CAST_FAILED = "failed casting",
                        SPELL_RESURRECT = "resurrected",
                        SPELL_CREATE = "creates",
                        SPELL_MISSED = "missed with",
                },
        },
}

function addon:flagDefaults()
        local result = {}
        for k,_ in pairs(db.flagMap) do
                result[k] = false
        end
        return result
end

function addon:wsDefaults()
        return {
                watched = true,
                selfOnly = false,
                groupOnly = false,
                whisperTarget = false,
                output1 = true,
                output2 = false,
                output3 = false,
                output4 = false,
                combatOnly = false,
                anyFlag = true,
                flags = addon:flagDefaults(),
                msgFormat = addon:defaultMsgFormat(),
        }
end

local wantIconLinks = {
        ["ChatFrame"] = true,
        ["Default"] = true,
        ["Blizzard Error Frame"] = true,
        ["Raid Warning"] = true,
}

local icons = {
        star = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_1:0|t",
        circle = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_2:0|t",
        diamond = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_3:0|t",
        triangle = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_4:0|t",
        moon = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_5:0|t",
        square = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_6:0|t",
        cross = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_7:0|t",
        skull = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_8:0|t",
}

-- Borrowed from SmartRes2
local resSpells = {
        [(GetSpellInfo(2006))]  = true, -- Resurrection
        [(GetSpellInfo(2008))]  = true, -- Ancestral Spirit
        [(GetSpellInfo(7328))]  = true, -- Redemption
        [(GetSpellInfo(50769))] = true, -- Revive
        [(GetSpellInfo(20484))] = true, -- Rebirth
        [(GetSpellInfo(8342))]  = true, -- Defibrillate (Goblin Jumper Cables)
        [(GetSpellInfo(22999))] = true, -- Defibrillate (Goblin Jumper Cables XL)
        [(GetSpellInfo(54732))] = true, -- Defibrillate (Gnomish Army Knife)
}

function addon:makeOptions()
        local opts = {
                name = "NightWatch " .. addonVersion,
                type = "group",
                handler = addon,
                -- inline = false,
                childGroups = "tab",
                args = {
                        test = {
                                name = "test",
                                order = 0, guiHidden = true, cmdHidden = true,
                                type = "execute",
                                func = "test",
                        },
                        enable = {
                                name = "Enable addon",
                                order = 10, guiHidden = true,
                                type = "execute",
                                func = function(info) addon:Enable() end,
                        },
                        enabled = {
                                name = "Enabled",
                                order = 20, cmdHidden = true,
                                type = "toggle",
                                get = function(info) return db[info[#info]] end,
                                set = function(info, value)
                                        if value then
                                                addon:Enable()
                                        else
                                                addon:Disable()
                                        end
                                end,
                        },
                        reset = {
                                name = "Reset Profile",
                                desc = "Reset *all* profile settings to default values.",
                                order = 30, -- width = "half",
                                type = "execute",
                                func = "resetSettings",
                                confirm = true,
                                confirmText = "This action is permanent.  Are you sure?",
                        },
                        disable = {
                                name = "Disable addon",
                                order = 40, guiHidden = true,
                                type = "execute",
                                func = function(info) addon:Disable() end,
                        },
                        config = {
                                name = "Toggle config",
                                order = 50, guiHidden = true,
                                type = "execute",
                                func = "openConfig"
                        },
                },
        }

        opts.args.spell = {
                name = "Spell Options",
                order = 10,
                type = "group",
                handler = addon,
                cmdHidden = true,
                childGroups = "tab",
                args = {
                        selectedSpell = {
                                name = "Selected Spell",
                                order = 10,
                                type = "select",
                                values = function() return addon:convertLookup(db.spells) end,
                                get = function() return db.selSpell end,
                                set = function(_, value) db.selSpell = value end,
                        },
                        deleteSpell = {
                                name = "Delete",
                                desc = "Delete selected spell.",
                                order = 20, width = "half",  cmdHidden = true,
                                type = "execute",
                                func =  "deleteSpell",
                                confirm = true,
                                confirmText = "This action is permanent.  Are you sure?",
                        },
                        addSpell = {
                                name = "Add New Spell",
                                desc = "Add a new spell.",
                                order = 30, cmdHidden = true, -- width = "half",
                                type = "input",
                                get = function() return nil end,
                                set = "addSpell",
                                validate = function(info, value)
                                        if value:len() < 3 then
                                                return "Must be at least 3 characters long."
                                        end
                                        return true
                                end,
                        },
                },
        }

        opts.args.spell.args.general = {
                name = "General",
                order = 10,
                type = "group",
                cmdHidden = true, width = "half",
                handler = addon,
                get = "getterFunc",
                set = "setterFunc",
                disabled = function() return not db.selSpell end,
                args = {
                        watched = {
                                name = "Watch",
                                desc = "Watch this spell.",
                                order = 10,
                                type = "toggle",
                        },
                        head1 = {
                                name = "",
                                order = 20,
                                type = "header",
                        },
                        selfOnly = {
                                name = "Self Only",
                                desc = "Only announce when YOU cast this spell.",
                                order = 30,
                                type = "toggle",
                        },
                        groupOnly = {
                                name = "Group Only",
                                desc = "Only announce spells cast by persons in your raid/party.",
                                order = 40,
                                type = "toggle",
                        },
                        combatOnly = {
                                name = "Combat Only",
                                desc = "Only announce when you are in combat.",
                                order = 50,
                                type = "toggle",
                        },
                        whisperTarget = {
                                name = "Whisper Target",
                                desc = "Whisper the target of spell, if applicable.",
                                order = 60,
                                type = "toggle",
                        },
                        flags = {
                                name = "Combatlog Flags",
                                order = 90,
                                type = "multiselect",
                                values = function() return addon:convertLookup(db.flagMap) end,
                                get = "getFlag",
                                set = "setFlag",
                                disabled = function() return (not db.selSpell) or addon:isAnyFlag() end,
                        },
                        all = {
                                name = "All",
                                desc = "Set all flags.",
                                order = 100, width = "half",
                                type = "execute",
                                func =  function(info,key) addon:setAllFlagsTo(info, true) end,
                                disabled = "isAnyFlag",
                        },
                        none = {
                                name = "None",
                                desc = "Clear all flags.",
                                order = 110, width = "half",
                                type = "execute",
                                func =  function(info,key) addon:setAllFlagsTo(info, false) end,
                                disabled = "isAnyFlag",
                        },
                        anyFlag = {
                                name = "Any flag",
                                desc = "Announce on ANY flag (not just the ones listed).",
                                order = 120,
                                type = "toggle",
                        },
                },
        }

        for i = 1,4 do
                opts.args.spell.args.general.args["output" .. i] = {
                                name = "Output " .. i,
                                desc = "Send output here.",
                                order = 70 + 1,
                                type = "toggle",
                        }
        end

        opts.args.spell.args.msgFormat = {
                name = "Message Format",
                type = "group",
                order = 20,
                cmdHidden = true,
                handler = addon,
                get = "mfGetterFunc",
                set = "mfSetterFunc",
                disabled = function() return not db.selSpell end,
                args = {
                        chat = {
                                name = "Chat Format",
                                desc = "Define chat format.",
                                order = 10, width = "double",
                                type = "input",
                        },
                        chatTarget = {
                                name = "Chat Target Format",
                                desc = "If set, this will replace %t if there is a target.",
                                order = 20,
                                type = "input",
                        },
                        chatTest = {
                                name = function() return addon:chatTest() end,
                                order = 25,
                                type = "description",
                        },
                        whisper = {
                                name = "Whisper Format",
                                desc = "Define whisper format.",
                                order = 30, width = "double",
                                type = "input",
                        },
                        whisperTarget = {
                                name = "Whisper Target Format",
                                desc = "If set, this will replace %t if there is a target.",
                                order = 40,
                                type = "input",
                        },
                        whisperTest = {
                                name = function() return addon:whisperTest() end,
                                order = 60,
                                type = "description",
                        },
                        -- head1 = {
                                -- name = '',
                                -- order = 70,
                                -- type = "header",
                        -- },
                        setDefaultFmt = {
                                name = "Set As Default",
                                desc = "Make these formats the default for new spells.",
                                order = 100, -- width = "half",
                                type = "execute",
                                func = function()
                                        if not db.selSpell then return end
                                        db.customMsgFormat.chat = db.spells[db.selSpell].msgFormat.chat
                                        db.customMsgFormat.chatTarget = db.spells[db.selSpell].msgFormat.chatTarget
                                end,
                        },
                        copyFmt = {
                                name = "Copy",
                                desc = "Copy these formats.",
                                order = 110, width = "half",
                                type = "execute",
                                func = function() addon:copyFmt(db.selSpell) end,
                        },
                        pasteFmt = {
                                name = "Paste",
                                desc = "Paste formats.",
                                order = 120, width = "half",
                                type = "execute",
                                func = function() addon:pasteFmt(db.selSpell) end,
                        },
                        setFmtToAll = {
                                name = "Set All",
                                desc = "Sets these formats for all spells",
                                order = 130, width = "half",
                                type = "execute",
                                func = function() addon:setFmtToAll(db.selSpell) end,
                        },
                        resetFmtToDefault = {
                                name = "Reset",
                                desc = "Resets these formats to program defaults.",
                                order = 140, width = "half",
                                type = "execute",
                                func = function() addon:resetFmtToDefault(db.selSpell) end,
                        },
                        formatHelp = {
                                name =  "\n** Format Syntax **\n" ..
                                                "%c = Caster\n" ..
                                                "%s = Spell\n" ..
                                                "%t = Target\n" ..
                                                "%f = Flags\n" ..
                                                "For UPPERCASE text, use an uppercase placeholder.",
                                order = 150,
                                type = "description",
                        },
                },
        }

        opts.args.output = {
                name = "Outputs",
                desc = "Configure output options.",
                type = "group",
                order = 30,
                cmdHidden = true,
                args = {}
        }

        local sinkOpts = {}
        for i = 1, #sink do
                sinkOpts[i] = sink[i]:GetSinkAce3OptionsDataTable()
                sinkOpts[i].name = "Output " .. i
                sinkOpts[i].desc = "Where to send this output."
                sinkOpts[i].order = 10 + i
                -- sinkOpts[i].inline = true
                opts.args.output.args["output" .. i] = sinkOpts[i]
        end

        opts.args.flagOptions = {
                name = "Flag Options",
                desc = "Configure flag options.",
                type = "group",
                order = 700,
                cmdHidden = true,
                args = {
                        selectedFlag = {
                                name = "Selected Flag",
                                order = 10,
                                type = "select",
                                values = function() return addon:convertLookup(db.flagMap) end,
                                get = function() return db.selFlag end,
                                set = function(_, value) db.selFlag = value end,
                        },
                        translation = {
                                name = "Translation",
                                desc = "Friendly text for this flag.",
                                order = 20,
                                type = "input",
                                get = function() return db.flagMap[db.selFlag] or '' end,
                                set = function(_, value) db.flagMap[db.selFlag] = value end,
                        },
                        head1 = {
                                name = "",
                                order = 40,
                                type = "header",
                        },
                        deleteFlag = {
                                name = "Delete Flag",
                                desc = "Delete selected flag.",
                                order = 75, -- width = "half",
                                type = "execute",
                                func =  function()
                                        db.flagMap[db.selFlag] = nil
                                        LibStub("AceConfigRegistry-3.0"):NotifyChange("NightWatch")
                                        end,
                                confirm = true,
                                confirmText = "This action is permanent.  Are you sure?",
                        },
                        addFlag = {
                                name = "Add New Flag",
                                desc = "Add a new flag.",
                                order = 80,
                                type = "input",
                                get = function() return nil end,
                                set = function(_, value)
                                        db.flagMap[value] = value
                                        LibStub("AceConfigRegistry-3.0"):NotifyChange("NightWatch")
                                end,
                                validate = function(info, value)
                                        if value:len() < 3 then
                                                return "Must be at least 3 characters long."
                                        end
                                        return true
                                end,
                        },
                },
        }


        opts.args.profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(addon.db)
        opts.args.profile.order = -10

        return opts
end

function addon:OnInitialize()
    addon.db = LibStub("AceDB-3.0"):New("NightWatchDB", Default_Profile, "Default")
        db = addon.db.profile

--      addon.db.RegisterCallback( self, "OnNewProfile", "profileChanged" )             -- OnProfileChanged seems to cover new profiles too.
        addon.db.RegisterCallback( self, "OnProfileReset", "profileChanged" )
        addon.db.RegisterCallback( self, "OnProfileChanged", "profileChanged" )
        addon.db.RegisterCallback( self, "OnProfileCopied", "profileChanged" )

        LibStub("AceConfig-3.0"):RegisterOptionsTable("NightWatch", addon:makeOptions())
        LibStub("AceConfigDialog-3.0"):AddToBlizOptions("NightWatch", "NightWatch")
    addon:RegisterChatCommand("nw", "chatCommand")
    addon:RegisterChatCommand("nightwatch", "chatCommand")

        addon:profileChanged()
end


function addon:OnEnable()
        addon:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED", "combatLogEvent")
        db.enabled = true
        if not firstEnable then
                Print("enabled")
        else
                firstEnable = false
        end
end

function addon:OnDisable()
        addon:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
        db.enabled = false
        Print("disabled")
end

function addon:chatCommand(input)
    if not input or input:trim() == "" then
        addon:openConfig()
                return
        end
    if input == "help" then
                input = ""
        end
        LibStub("AceConfigCmd-3.0").HandleCommand(addon, "nw", "NightWatch", input)
end

-------------------------------------------------------------------------------
-- 0  1          2      3           4        5        6         7          8        9        10        11         12       13         14           15
-- _, timestamp, event, hideCaster, srcGUID, srcName, srcFlags, srcFlags2, dstGUID, dstName, dstFlags, dstFlags2, spellID, spellName, spellSchool, missType, ...)
--
local flag, source, target, spell
local msg, whisperTarget
function addon:combatLogEvent(_, _, flag, _, _, source, _, _, _, target, _, _, _, spell, ...)
--                            0  1  2     3  4  5       6  7  8  9       10 11 12 13  

        if not db.spells[spell] then return end                                                                 -- Spell not in database, quit

        local ws = db.spells[spell]

        if not ws.watched then return end                                                                               -- Not being watched, quit

        if not (ws.anyFlag or ws.flags[flag]) then return end                                   -- Doesn't match flags, quit

        if ws.selfOnly and not (source == playerName) then return end                   -- Check for self-cast only

        if ws.combatOnly and not InCombatLockdown() then return end                             -- Check for self in combat

        if ws.groupOnly and not (UnitInRaid(source) or UnitInParty(source)) then return end     -- Check for source in raid/party

        if db.flagMap[flag] then                                                                                                -- Convert flag to more friendly version, if available.
                flag = db.flagMap[flag]
        end

        if resSpells[spell] and not target then                         -- Partial fix for res spell target not showing in combatlog.  gg bliz.
                target = UnitName(source .. "-target") or nil
        end

        if target then
                if ws.whisperTarget then                                                                                                -- Whisper target
                        if ws.msgFormat.whisperTarget then
                                whisperTarget = addon:formatMsg(nil, nil, target, nil, ws.msgFormat.whisperTarget)
                        end
                        SendChatMessage(addon:formatMsg(source, spell, whisperTarget, flag, ws.msgFormat.whisper), "WHISPER", nil, target)
                end
                target = addon:formatMsg(nil, nil, target, nil, ws.msgFormat.chatTarget)
        end

        msg = addon:formatMsg(source, spell, target, flag, ws.msgFormat.chat)           -- Format message

        for i = 1, #sink do
                if ws["output" .. i] then                                                                                               -- Send message to output(s)
                        if wantIconLinks[db.sinkStorage[i].sink20OutputSink] then
                                msg = addon:mkIconLinks(msg)
                        end
                        addon:pour(sink[i], msg)
                end
        end

end

-------------------------------------------------------------------------------

function addon:profileChanged()
        db = addon.db.profile
        for i = 1, #sink do
                sink[i]:SetSinkStorage(db.sinkStorage[i])
        end     
        addon:dbUpgrade()
        addon:resetSelected()
end

function addon:dbUpgrade()
        if db.dbVers and db.dbVers > 7 then return end                                                  -- DB Already upgraded

        -- Upgrade data format from previous addon versions (=<0.6)
        -- (Renamed db.spell to db.spells just to torture myself.
        -- Also moved away from AceDB's magic ['*'] feature since it was 
        -- auto-creating spells everty time I checked the spell was in the db.  
        -- Sigh.)
        if db.spell then
                Print("Upgrading database")
                db.spells = addon:deepcopy(db.spell)                                                                    -- Quick and dirty ...
                db.spell = nil
                db.selected = nil
                Print("Upgrade complete")
        end

        -- Remove obsolete sink data from previous addon versions (=<0.7)
        for i = 1, 4 do
                if db["sinkStorage" .. i] then
                        db.sinkStorage[i] = db["sinkStorage" .. i]
                        db["sinkStorage" .. i] = nil
                end
        end

        db.dbVers = dbVers
end

function addon:resetSelected()
        if not db.selSpell or not db.spells[db.selSpell] or db.spells[db.selSpell] == "" then
                db.selSpell = (next(db.spells) or nil)
        end
end

function addon:openConfig()
        ACD[ACD.OpenFrames.NightWatch and "Close" or "Open"](ACD,"NightWatch")
end

function addon:resetSettings()
        addon.db:ResetProfile()
        Print("All settings in this profile have been reset to default values.")
end

function addon:getterFunc(info)
        if not db.selSpell then return nil end
        return db.spells[db.selSpell][info[#info]]
end

function addon:setterFunc(info, value)
        if not db.selSpell then return end
        db.spells[db.selSpell][info[#info]] = value
end

function addon:test()
        Print("Adding test spells.")
        addon:addSpell(nil, "Hearthstone")
        addon:addSpell(nil, "Using Direbrew's Remote")
        addon:addSpell(nil, "Toy Train Set")
        addon:addSpell(nil, "Rebirth")
        addon:addSpell(nil, "Tricks of the Trade")
        addon:addSpell(nil, "Misdirection")
        LibStub("AceConfigRegistry-3.0"):NotifyChange("NightWatch")
end

function addon:addSpell(info, value)
        db.spells[value] = addon:wsDefaults()
        -- db.spells[value].watched = true
        db.spells[value].msgFormat = addon:deepcopy(db.customMsgFormat)
        db.selSpell = value
end

function addon:deleteSpell()
        if not db.selSpell then return end
        wipe(db.spells[db.selSpell])
        db.spells[db.selSpell] = nil
        addon:resetSelected()
end

function addon:getFlag(info, key)
        if not db.selSpell then return nil end
        return db.spells[db.selSpell].flags[key]
end

function addon:setFlag(info, key, value)
        if not db.selSpell then return nil end
        db.spells[db.selSpell].flags[key] = value
end

function addon:isAnyFlag()
        if not db.selSpell then return nil end
        return db.spells[db.selSpell].anyFlag
end

function addon:setAllFlagsTo(info, value)
        if not db.selSpell then return end
        for k in pairs(db.spells[db.selSpell].flags) do
                db.spells[db.selSpell].flags[k] = value
        end
end

function addon:mfGetterFunc(info)
        if not db.selSpell then return nil end
        return db.spells[db.selSpell][info[#info-1]][info[#info]]
end

function addon:mfSetterFunc(info, value)
        if not db.selSpell then return nil end
        db.spells[db.selSpell][info[#info-1]][info[#info]] = value
end

function addon:chatTest()
        if not db.selSpell then return nil end
        return addon:testMsg(
                        db.spells[db.selSpell].msgFormat.chat,
                        db.spells[db.selSpell].msgFormat.chatTarget
                )
end

function addon:whisperTest()
        if not db.selSpell then return nil end
        return addon:testMsg(
                        db.spells[db.selSpell].msgFormat.whisper,
                        db.spells[db.selSpell].msgFormat.whisperTarget
                )
end

 function addon:testMsg(formatString, targetFormatString)
        local source, spell, target, flag = "Caster", db.selSpell or "Spell", "Target", "Flag"
        if not (targetFormatString == "" or targetFormatString == nil) then
                target = addon:formatMsg(nil, nil, target, nil, targetFormatString)
        end
        return addon:mkIconLinks(addon:formatMsg(source, spell, target, flag, formatString))
end

function addon:formatMsg(caster, spell, target, flag, formatString)
        if not formatString then return "" end
        local msg = formatString
        if not caster then caster = '' end
        if not spell then spell = '' end
        if not target then target = '' end
        if not flag then flag = '' end
        msg = string_gsub(msg, "%%c", caster)
        msg = string_gsub(msg, "%%C", string_upper(caster))
        msg = string_gsub(msg, "%%t", target)
        msg = string_gsub(msg, "%%T", string_upper(target))
        msg = string_gsub(msg, "%%s", spell)
        msg = string_gsub(msg, "%%S", string_upper(spell))
        msg = string_gsub(msg, "%%f", flag)
        msg = string_gsub(msg, "%%F", string_upper(flag))
        return msg
end

function addon:mkIconLinks(msg)
        if not msg then return "" end
        for k,v in pairs(icons) do
                msg = string_gsub(msg, "{" .. k .."}", v)
        end
        return msg
end

function addon:copyFmt(value)
        if not value then return end
        clipboard.fmt = addon:deepcopy(db.spells[value].msgFormat)
end

function addon:pasteFmt(value)
        if not value then return end
        db.spells[value].msgFormat = addon:deepcopy(clipboard.fmt)
end

function addon:setFmtToAll(value)
        if not value then return end
        addon:copyFmt(value)
        for k,_ in pairs(db.spells) do
                addon:pasteFmt(k)
        end
end

function addon:resetFmtToDefault(value)
        if not value then return end
        db.spells[value].msgFormat = addon:defaultMsgFormat()
end

-- From Recount
function addon:deepcopy(object)
        local lookup_table = {}
        local function _copy(object)
                if type(object) ~= "table" then
                        return object
                elseif lookup_table[object] then
                        return lookup_table[object]
                end
                local new_table = {}
                lookup_table[object] = new_table
                for index, value in pairs(object) do
                        new_table[_copy(index)] = _copy(value)
                end
                return setmetatable(new_table, getmetatable(object))
        end
        return _copy(object)
end

function addon:pour(dest, text, color, icon)
        text = format("|cffffff7fnw|r: %s", text)
        color = color or {r=1,g=1,b=1}
        icon = icon or nil
        dest:Pour(text,color.r,color.g,color.b,nil,nil,nil,nil,nil,icon)
end

function addon:convertLookup(data)
        local result = {}
        for k,_ in pairs(data) do
                result[k] = k
        end
        return result
end

Compare with Previous | Blame