Quantcast
--[[
##############################################################################
_____/\\\\\\\\\\\____/\\\________/\\\__/\\\________/\\\__/\\\\\\\\\\\_       #
 ___/\\\/////////\\\_\/\\\_______\/\\\_\/\\\_______\/\\\_\/////\\\///__      #
  __\//\\\______\///__\//\\\______/\\\__\/\\\_______\/\\\_____\/\\\_____     #
   ___\////\\\__________\//\\\____/\\\___\/\\\_______\/\\\_____\/\\\_____    #
    ______\////\\\________\//\\\__/\\\____\/\\\_______\/\\\_____\/\\\_____   #
     _________\////\\\______\//\\\/\\\_____\/\\\_______\/\\\_____\/\\\_____  #
      __/\\\______\//\\\______\//\\\\\______\//\\\______/\\\______\/\\\_____ #
       _\///\\\\\\\\\\\/________\//\\\________\///\\\\\\\\\/____/\\\\\\\\\\\_#
        ___\///////////___________\///___________\/////////_____\///////////_#
##############################################################################
S U P E R - V I L L A I N - U I   By: Munglunch                              #
##############################################################################
##########################################################
LOCALIZED LUA FUNCTIONS
##########################################################
]]--
--[[ GLOBALS ]]--
local _G = _G;
local unpack    = _G.unpack;
local select    = _G.select;
local pairs     = _G.pairs;
local type      = _G.type;
local tostring  = _G.tostring;
local tonumber  = _G.tonumber;
local string    = _G.string;
local math      = _G.math;
--[[ STRING METHODS ]]--
local match, format = string.match, string.format;
local gmatch, gsub = string.gmatch, string.gsub;
--[[ MATH METHODS ]]--
local floor, modf = math.floor, math.modf;
local iLevelFilter = ITEM_LEVEL:gsub( "%%d", "(%%d+)" )
--[[
##########################################################
GET ADDON DATA
##########################################################
]]--
local SuperVillain, L = unpack(select(2, ...))
--[[
##########################################################
MISC UTILITY FUNCTIONS
##########################################################
]]--
function SuperVillain:ColorGradient(perc, ...)
    if perc >= 1 then
        return select(select('#', ...) - 2, ...)
    elseif perc <= 0 then
        return ...
    end
    local num = select('#', ...) / 3
    local segment, relperc = modf(perc*(num-1))
    local r1, g1, b1, r2, g2, b2 = select((segment*3)+1, ...)
    return r1 + (r2-r1)*relperc, g1 + (g2-g1)*relperc, b1 + (b2-b1)*relperc
end;
--[[
##########################################################
POSITIONING UTILITY FUNCTIONS
##########################################################
]]--
SuperVillain.PointIndexes = {
    ["TOP"] = "TOP",
    ["BOTTOM"] = "BOTTOM",
    ["LEFT"] = "LEFT",
    ["RIGHT"] = "RIGHT",
    ["TOPRIGHT"] = "UP AND RIGHT",
    ["TOPLEFT"] = "UP AND LEFT",
    ["BOTTOMRIGHT"] = "DOWN AND RIGHT",
    ["BOTTOMLEFT"] = "DOWN AND LEFT",
    ["CENTER"] = "CENTER",
    ["RIGHTTOP"] = "RIGHT AND UP",
    ["LEFTTOP"] = "LEFT AND UP",
    ["RIGHTBOTTOM"] = "RIGHT AND DOWN",
    ["LEFTBOTTOM"] = "LEFT AND DOWN",
    ["INNERRIGHT"] = "INNER RIGHT",
    ["INNERLEFT"] = "INNER LEFT",
    ["INNERTOPRIGHT"] = "INNER TOP RIGHT",
    ["INNERTOPLEFT"] = "INNER TOP LEFT",
    ["INNERBOTTOMRIGHT"] = "INNER BOTTOM RIGHT",
    ["INNERBOTTOMLEFT"] = "INNER BOTTOM LEFT",
}

do
    local _inverted = {
        TOP = "BOTTOM",
        BOTTOM = "TOP",
        LEFT = "RIGHT",
        RIGHT = "LEFT",
        TOPRIGHT = "BOTTOMRIGHT",
        TOPLEFT = "BOTTOMLEFT",
        BOTTOMRIGHT = "TOPRIGHT",
        BOTTOMLEFT = "TOPLEFT",
        CENTER = "CENTER",
        RIGHTTOP = "TOPLEFT",
        LEFTTOP = "TOPRIGHT",
        RIGHTBOTTOM = "BOTTOMLEFT",
        LEFTBOTTOM = "BOTTOMRIGHT",
        INNERRIGHT = "RIGHT",
        INNERLEFT = "LEFT",
        INNERTOPRIGHT = "TOPRIGHT",
        INNERTOPLEFT = "TOPLEFT",
        INNERBOTTOMRIGHT = "BOTTOMRIGHT",
        INNERBOTTOMLEFT = "BOTTOMLEFT",
    }
    setmetatable(_inverted, { __index = function(t, k)
        return "CENTER"
    end})

    local _translated = {
        TOP = "TOP",
        BOTTOM = "BOTTOM",
        LEFT = "LEFT",
        RIGHT = "RIGHT",
        TOPRIGHT = "TOPRIGHT",
        TOPLEFT = "TOPLEFT",
        BOTTOMRIGHT = "BOTTOMRIGHT",
        BOTTOMLEFT = "BOTTOMLEFT",
        CENTER = "CENTER",
        RIGHTTOP = "TOPRIGHT",
        LEFTTOP = "TOPLEFT",
        RIGHTBOTTOM = "BOTTOMRIGHT",
        LEFTBOTTOM = "BOTTOMLEFT",
        INNERRIGHT = "RIGHT",
        INNERLEFT = "LEFT",
        INNERTOPRIGHT = "TOPRIGHT",
        INNERTOPLEFT = "TOPLEFT",
        INNERBOTTOMRIGHT = "BOTTOMRIGHT",
        INNERBOTTOMLEFT = "BOTTOMLEFT",
    }
    setmetatable(_translated, { __index = function(t, k)
        return "CENTER"
    end})

    function SuperVillain:ReversePoint(frame, point, target, x, y)
        if((not frame) or (not point)) then return; end
        local anchor = _inverted[point];
        local relative = _translated[point];
        x = x or 0;
        y = y or 0;
        target = target or frame:GetParent()
        frame:SetPoint(anchor, target, relative, x, y)
        --[[ auto-set specific properties to save on logic ]]--
        frame.initialAnchor = anchor;
    end
end;
--[[
##########################################################
ITEM UTILITY FUNCTIONS
##########################################################
]]--
do
    local _failsafe = {0}

    local _upgrades = {
        [  1] = {8, 1, 1},  [373] = {4, 1, 2},  [374] = {8, 2, 2},  [375] = {4, 1, 3}, [376] = {4, 2, 3},
        [377] = {4, 3, 3},  [378] = {7, 0, 0},  [379] = {4, 1, 2},  [380] = {4, 2, 2}, [445] = {0, 0, 2},
        [446] = {4, 1, 2},  [447] = {8, 2, 2},  [451] = {0, 0, 1},  [452] = {8, 1, 1}, [453] = {0, 0, 2},
        [454] = {4, 1, 2},  [455] = {8, 2, 2},  [456] = {0, 0, 1},  [457] = {8, 1, 1}, [458] = {0, 0, 4},
        [459] = {4, 1, 4},  [460] = {8, 2, 4},  [461] = {12, 3, 4}, [462] = {16, 4, 4},
        [465] = {0, 0, 2},  [466] = {4, 1, 2},  [467] = {8, 2, 2},  [468] = {0, 0, 4},
        [469] = {4, 1, 4},  [470] = {8, 2, 4},  [471] = {12, 3, 4}, [472] = {16, 4, 4},
        [491] = {0, 0, 4},  [492] = {4, 1, 4},  [493] = {8, 2, 4},  [494] = {0, 0, 6},
        [495] = {4, 1, 6},  [496] = {8, 2, 6},  [497] = {12, 3, 6}, [498] = {16, 4, 6},
        [504] = {12, 3, 4}, [505] = {16, 4, 4}, [506] = {20, 5, 6}, [507] = {24, 6, 6}
    }

    local _heirlooms = {
        44102,42944,44096,42943,42950,48677,42946,42948,42947,42992,50255,44103,
        44107,44095,44098,44097,44105,42951,48683,48685,42949,48687,42984,44100,
        44101,44092,48718,44091,42952,48689,44099,42991,42985,48691,44094,44093,
        42945,48716
    }

    -- DEPRECATED
    -- local _heirloom_regex = "|?c?f?f?(%x*)|?H?([^:]*):?(%d+):?(%d*):?(%d*):?(%d*):?(%d*):?(%d*):?(%-?%d*):?(%-?%d*):?(%d*):?(%d*)|?h?%[?([^%[%]]*)%]?|?h?|?r?";

    local _slots = {
        ["HeadSlot"] = {true, true},            ["NeckSlot"] = {true, false},
        ["ShoulderSlot"] = {true, true},        ["BackSlot"] = {true, false},
        ["ChestSlot"] = {true, true},           ["WristSlot"] = {true, true},
        ["MainHandSlot"] = {true, true, true},  ["SecondaryHandSlot"] = {true, true},
        ["HandsSlot"] = {true, true, true},     ["WaistSlot"] = {true, true, true},
        ["LegsSlot"] = {true, true, true},      ["FeetSlot"] = {true, true, true},
        ["Finger0Slot"] = {true, false, true},  ["Finger1Slot"] = {true, false, true},
        ["Trinket0Slot"] = {true, false, true}, ["Trinket1Slot"] = {true, false, true}
    }

    setmetatable(_upgrades, { __index = function(t, k)
        return _failsafe
    end})

    local function _justthetip()
        for i=1, #GameTooltip.shoppingTooltips do
            if(not GameTooltip.shoppingTooltips[i]:IsShown()) then
                return GameTooltip.shoppingTooltips[i]
            end
        end
    end

    local function _getHeirloomLevel(unit, itemID)
        if(not itemID) then return; end
        local baseLevel = UnitLevel(unit)
        if baseLevel > 85 then baseLevel = 85 end;
        if baseLevel > 80 then
            for i=1, #_heirlooms do
                if(_heirlooms[i] == itemID) then
                    baseLevel = 80;
                end
            end
            if baseLevel > 80 then
                return (((baseLevel - 81) * 12.2) + 272)
            end
        elseif baseLevel > 67 then
            return (((baseLevel - 68) * 6) + 130)
        elseif baseLevel > 59 then
            return (((baseLevel - 60) * 3) + 85)
        end
        return baseLevel
    end

    local function _getItemInfo(itemString)
        local itemId = tonumber(itemString:match("item:%d+:%d+:%d+:%d+:%d+:%d+:%-?%d+:%-?%d+:%d+:%d+:(%d+)"))
        if itemId then
            local lvl = _upgrades[itemId][1]
            local cur = _upgrades[itemId][2]
            local max = _upgrades[itemId][3]
            return cur, max, lvl
        end
        return nil
    end

    local function _getItemLevel(unit, itemString)
        local name, link, quality, iLevel = GetItemInfo(itemString)
        local itemId = tonumber(itemString:match("item:%d+:%d+:%d+:%d+:%d+:%d+:%-?%d+:%-?%d+:%d+:%d+:(%d+)"))
        if iLevel and itemId then
            if(quality == 7) then
                iLevel = _getHeirloomLevel(unit, itemId)
            else
                iLevel = iLevel + _upgrades[itemId][1]
            end
        end
        return iLevel
    end

    local function _scanItemLevel(unit, itemString)
        local tooltip = _justthetip();
        if(not tooltip) then return _getItemLevel(unit, itemString) end
        tooltip:SetOwner(UIParent, "ANCHOR_NONE");
        tooltip:SetHyperlink(itemString);
        tooltip:Show();

        local iLevel = 0;
        local tname = tooltip:GetName().."TextLeft%s";
        for i = 2, tooltip:NumLines() do
            local text = _G[tname:format(i)]:GetText();
            if(text and text ~= "") then
                local value = tonumber(text:match(iLevelFilter));
                if(value) then
                    iLevel = value;
                end
            end
        end

        tooltip:Hide();
        return iLevel
    end

    function SuperVillain:ParseGearSlots(unit, inspecting, firstCallback, secondCallback)
        local category = (inspecting) and "Inspect" or "Character";
        local averageLevel,totalSlots,upgradeAdjust,globalName = 0,0,0;
        for slotName,flags in pairs(_slots) do
            globalName = ("%s%s"):format(category, slotName)
            local slotId = GetInventorySlotInfo(slotName)
            local iLink = GetInventoryItemLink(unit, slotId)
            if(iLink and type(iLink) == "string") then
                local iLevel = _scanItemLevel(unit, iLink)
                if(iLevel and iLevel > 0) then
                    totalSlots = totalSlots + 1;
                    averageLevel = averageLevel + iLevel
                    if(flags[1] and firstCallback and type(firstCallback) == "function") then
                        firstCallback(globalName, iLevel)
                    end
                end
            end
            if(slotId ~= nil) then
                if(not inspecting and flags[2] and secondCallback and type(secondCallback) == "function") then
                    secondCallback(globalName, slotId)
                end
            end
        end;
        if(averageLevel < 1 or totalSlots < 15) then
            return
        end;
        return floor(averageLevel / totalSlots)
    end
end;
--[[
##########################################################
CHAT LOG PARSING FUNCTIONS (from LibDeformat  by:ckknight)
##########################################################
]]--
do
    local FORMAT_SEQUENCES = {
        ["s"] = ".+",
        ["c"] = ".",
        ["%d*d"] = "%%-?%%d+",
        ["[fg]"] = "%%-?%%d+%%.?%%d*",
        ["%%%.%d[fg]"] = "%%-?%%d+%%.?%%d*",
    }

    local STRING_BASED_SEQUENCES = {
        ["s"] = true,
        ["c"] = true,
    }

    local cache = setmetatable({}, {__mode='k'})

    local function _deformat(pattern)
        local func = cache[pattern]
        if func then return func end
        local unpattern = '^' .. pattern:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") .. '$'
        local number_indexes = {}
        local index_translation = nil
        local highest_index
        if not pattern:find("%%1%$") then
            local i = 0
            while true do
                i = i + 1
                local first_index
                local first_sequence
                for sequence in pairs(FORMAT_SEQUENCES) do
                    local index = unpattern:find("%%%%" .. sequence)
                    if index and (not first_index or index < first_index) then
                        first_index = index
                        first_sequence = sequence
                    end
                end
                if not first_index then
                    break
                end
                unpattern = unpattern:gsub("%%%%" .. first_sequence, "(" .. FORMAT_SEQUENCES[first_sequence] .. ")", 1)
                number_indexes[i] = not STRING_BASED_SEQUENCES[first_sequence]
            end
            highest_index = i - 1
        else
            local i = 0
            while true do
                i = i + 1
                local found_sequence
                for sequence in pairs(FORMAT_SEQUENCES) do
                    if unpattern:find("%%%%" .. i .. "%%%$" .. sequence) then
                        found_sequence = sequence
                        break
                    end
                end
                if not found_sequence then
                    break
                end
                unpattern = unpattern:gsub("%%%%" .. i .. "%%%$" .. found_sequence, "(" .. FORMAT_SEQUENCES[found_sequence] .. ")", 1)
                number_indexes[i] = not STRING_BASED_SEQUENCES[found_sequence]
            end
            highest_index = i - 1
            i = 0
            index_translation = {}
            pattern:gsub("%%(%d)%$", function(w)
                i = i + 1
                index_translation[i] = tonumber(w)
            end)
        end
        if highest_index == 0 then
            cache[pattern] = SuperVillain.fubar
        else
            local t = {}
            t[#t+1] = [=[
                return function(text)
                    local ]=]
            for i = 1, highest_index do
                if i ~= 1 then
                    t[#t+1] = ", "
                end
                t[#t+1] = "a"
                if not index_translation then
                    t[#t+1] = i
                else
                    t[#t+1] = index_translation[i]
                end
            end
            t[#t+1] = [=[ = text:match(]=]
            t[#t+1] = ("%q"):format(unpattern)
            t[#t+1] = [=[)
                if not a1 then
                    return ]=]
            for i = 1, highest_index do
                if i ~= 1 then
                    t[#t+1] = ", "
                end
                t[#t+1] = "nil"
            end
            t[#t+1] = "\n"
            t[#t+1] = [=[
                end
            ]=]
            t[#t+1] = "return "
            for i = 1, highest_index do
                if i ~= 1 then
                    t[#t+1] = ", "
                end
                t[#t+1] = "a"
                t[#t+1] = i
                if number_indexes[i] then
                    t[#t+1] = "+0"
                end
            end
            t[#t+1] = "\n"
            t[#t+1] = [=[
                end
            ]=]
            t = table.concat(t, "")
            cache[pattern] = assert(loadstring(t))()
        end
        return cache[pattern]
    end

    function SuperVillain:DeFormat(text, pattern)
        if type(text) ~= "string" then
            error(("Error: DeFormat text argument %s (%s)."):format(type(text), text), 2)
        elseif type(pattern) ~= "string" then
            error(("Error: DeFormat pattern argument %s (%s)."):format(type(pattern), pattern), 2)
        end
        return _deformat(pattern)(text)
    end
end
--[[
##########################################################
SIMPLE BUTTON CONSTRUCT
##########################################################
]]--
local Button_OnEnter = function(self, ...)
    if InCombatLockdown() then return end
    GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT", 0, 4)
    GameTooltip:ClearLines()
    GameTooltip:AddLine(self.TText, 1, 1, 1)
    GameTooltip:Show()
end

function SuperVillain:CreateButton(frame, label, anchor, x, y, width, height, tooltip)
    local button = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
    button:SetWidth(width)
    button:SetHeight(height)
    button:SetPoint(anchor, x, y)
    button:SetText(label)
    button:RegisterForClicks("AnyUp")
    button:SetHitRectInsets(0, 0, 0, 0);
    button:SetFrameStrata("FULLSCREEN_DIALOG");
    button.TText = tooltip
    button:SetButtonTemplate()
    button:SetScript("OnEnter", Button_OnEnter)
    button:SetScript("OnLeave", GameTooltip_Hide)
    return button
end