Quantcast
--[[
##############################################################################
_____/\\\\\\\\\\\____/\\\________/\\\__/\\\________/\\\__/\\\\\\\\\\\_       #
 ___/\\\/////////\\\_\/\\\_______\/\\\_\/\\\_______\/\\\_\/////\\\///__      #
  __\//\\\______\///__\//\\\______/\\\__\/\\\_______\/\\\_____\/\\\_____     #
   ___\////\\\__________\//\\\____/\\\___\/\\\_______\/\\\_____\/\\\_____    #
    ______\////\\\________\//\\\__/\\\____\/\\\_______\/\\\_____\/\\\_____   #
     _________\////\\\______\//\\\/\\\_____\/\\\_______\/\\\_____\/\\\_____  #
      __/\\\______\//\\\______\//\\\\\______\//\\\______/\\\______\/\\\_____ #
       _\///\\\\\\\\\\\/________\//\\\________\///\\\\\\\\\/____/\\\\\\\\\\\_#
        ___\///////////___________\///___________\/////////_____\///////////_#
##############################################################################
S U P E R - V I L L A I N - U I   By: Munglunch                              #
############################################################################## ]]--

--[[ GLOBALS ]]--

local _G = _G;
--LUA
local unpack        = _G.unpack;
local select        = _G.select;
local assert        = _G.assert;
local type          = _G.type;
local error         = _G.error;
local pcall         = _G.pcall;
local print         = _G.print;
local ipairs        = _G.ipairs;
local pairs         = _G.pairs;
local next          = _G.next;
local rawset        = _G.rawset;
local rawget        = _G.rawget;
local tostring      = _G.tostring;
local tonumber      = _G.tonumber;
local getmetatable  = _G.getmetatable;
local setmetatable  = _G.setmetatable;
--STRING
local string        = _G.string;
local upper         = string.upper;
local format        = string.format;
local find          = string.find;
local match         = string.match;
local gsub          = string.gsub;
--MATH
local math          = _G.math;
local floor         = math.floor
local random        = math.random;
--TABLE
local table         = _G.table;
local tsort         = table.sort;
local tconcat       = table.concat;
local tinsert       = _G.tinsert;
local tremove       = _G.tremove;
local twipe         = _G.wipe;
--BLIZZARD API
local ReloadUI              = _G.ReloadUI;
local GetLocale             = _G.GetLocale;
local CreateFrame           = _G.CreateFrame;
local IsAddOnLoaded         = _G.IsAddOnLoaded;
local InCombatLockdown      = _G.InCombatLockdown;
local GetAddOnInfo          = _G.GetAddOnInfo;
local LoadAddOn             = _G.LoadAddOn;
local LibStub               = _G.LibStub;
local GetAddOnMetadata      = _G.GetAddOnMetadata;
local GetCVarBool           = _G.GetCVarBool;
local GameTooltip           = _G.GameTooltip;
local StaticPopup_Hide      = _G.StaticPopup_Hide;
local ERR_NOT_IN_COMBAT     = _G.ERR_NOT_IN_COMBAT;

--[[  CONSTANTS ]]--

_G.BINDING_HEADER_SVUI = "Supervillain UI";
_G.BINDING_NAME_SVUI_MARKERS = "Raid Markers";
_G.BINDING_NAME_SVUI_DOCKS = "Toggle Docks";
_G.BINDING_NAME_SVUI_RIDE = "Let's Ride";

_G.SlashCmdList.RELOADUI = ReloadUI
_G.SLASH_RELOADUI1 = "/rl"
_G.SLASH_RELOADUI2 = "/reloadui"

--[[ GET THE REGISTRY LIB ]]--

local SVLib = LibSuperVillain("Registry");

--[[
#####################################################################################
  /$$$$$$  /$$    /$$ /$$   /$$ /$$$$$$        /$$$$$$   /$$$$$$  /$$$$$$$  /$$$$$$$$
 /$$__  $$| $$   | $$| $$  | $$|_  $$_/       /$$__  $$ /$$__  $$| $$__  $$| $$_____/
| $$  \__/| $$   | $$| $$  | $$  | $$        | $$  \__/| $$  \ $$| $$  \ $$| $$
|  $$$$$$ |  $$ / $$/| $$  | $$  | $$        | $$      | $$  | $$| $$$$$$$/| $$$$$
 \____  $$ \  $$ $$/ | $$  | $$  | $$        | $$      | $$  | $$| $$__  $$| $$__/
 /$$  \ $$  \  $$$/  | $$  | $$  | $$        | $$    $$| $$  | $$| $$  \ $$| $$
|  $$$$$$/   \  $/   |  $$$$$$/ /$$$$$$      |  $$$$$$/|  $$$$$$/| $$  | $$| $$$$$$$$
 \______/     \_/     \______/ |______/       \______/  \______/ |__/  |__/|________/
#####################################################################################
]]--

--[[ LOCALS ]]--

local callbacks = {};
local numCallbacks = 0;
local playerClass = select(2, UnitClass("player"));
local messagePattern = "|cffFF2F00%s:|r";
local debugPattern = "|cffFF2F00%s|r [|cff992FFF%s|r]|cffFF2F00:|r";
local errorPattern = "|cffff0000Error -- |r|cffff9900Required addon '|r|cffffff00%s|r|cffff9900' is %s.|r"

--[[ HELPERS ]]--

local function _removedeprecated()
    --[[ BEGIN DEPRECATED ]]--

    --[[ END DEPRECATED ]]--
end

local function _sendmessage(msg, prefix)
    if(type(msg) == "table") then
         msg = tostring(msg)
    end
    if(not msg) then return end
    if(prefix) then
        local outbound = ("%s %s"):format(prefix, msg);
        print(outbound)
    else
        print(msg)
    end
end

--[[ CLASS COLOR LOCALS ]]--

local function RegisterCallback(self, m, h)
    assert(type(m) == "string" or type(m) == "function", "Bad argument #1 to :RegisterCallback (string or function expected)")
    if type(m) == "string" then
        assert(type(h) == "table", "Bad argument #2 to :RegisterCallback (table expected)")
        assert(type(h[m]) == "function", "Bad argument #1 to :RegisterCallback (m \"" .. m .. "\" not found)")
        m = h[m]
    end
    callbacks[m] = h or true
    numCallbacks = numCallbacks + 1
end

local function UnregisterCallback(self, m, h)
    assert(type(m) == "string" or type(m) == "function", "Bad argument #1 to :UnregisterCallback (string or function expected)")
    if type(m) == "string" then
        assert(type(h) == "table", "Bad argument #2 to :UnregisterCallback (table expected)")
        assert(type(h[m]) == "function", "Bad argument #1 to :UnregisterCallback (m \"" .. m .. "\" not found)")
        m = h[m]
    end
    callbacks[m] = nil
    numCallbacks = numCallbacks + 1
end

local function DispatchCallbacks()
    if (numCallbacks < 1) then return end
    for m, h in pairs(callbacks) do
        local ok, err = pcall(m, h ~= true and h or nil)
        if not ok then
            print("ERROR:", err)
        end
    end
end

--[[ BUILD CLASS COLOR GLOBAL ]]--

local SVUI_CLASS_COLORS;

do
    local env = getfenv(0)
    env.SVUI_CLASS_COLORS = {}
    SVUI_CLASS_COLORS = env.SVUI_CLASS_COLORS

    local classes = {};
    local supercolors = {
        ["HUNTER"]        = { r = 0.454, g = 0.698, b = 0 },
        ["WARLOCK"]       = { r = 0.286, g = 0,     b = 0.788 },
        ["PRIEST"]        = { r = 0.976, g = 1,     b = 0.839 },
        ["PALADIN"]       = { r = 0.956, g = 0.207, b = 0.733 },
        ["MAGE"]          = { r = 0,     g = 0.796, b = 1 },
        ["ROGUE"]         = { r = 1,     g = 0.894, b = 0.117 },
        ["DRUID"]         = { r = 1,     g = 0.513, b = 0 },
        ["SHAMAN"]        = { r = 0,     g = 0.38,  b = 1 },
        ["WARRIOR"]       = { r = 0.698, g = 0.36,  b = 0.152 },
        ["DEATHKNIGHT"]   = { r = 0.847, g = 0.117, b = 0.074 },
        ["MONK"]          = { r = 0.015, g = 0.886, b = 0.38 },
    };
    for class in pairs(RAID_CLASS_COLORS) do
        tinsert(classes, class)
    end
    tsort(classes)
    setmetatable(SVUI_CLASS_COLORS,{
        __index = function(t, k)
            if k == "RegisterCallback" then return RegisterCallback end
            if k == "UnregisterCallback" then return UnregisterCallback end
            if k == "DispatchCallbacks" then return DispatchCallbacks end
        end
    });
    for i, class in ipairs(classes) do
        local color = supercolors[class]
        local r, g, b = color.r, color.g, color.b
        local hex = ("ff%02x%02x%02x"):format(r * 255, g * 255, b * 255)
        if not SVUI_CLASS_COLORS[class] or not SVUI_CLASS_COLORS[class].r or not SVUI_CLASS_COLORS[class].g or not SVUI_CLASS_COLORS[class].b then
            SVUI_CLASS_COLORS[class] = {
                r = r,
                g = g,
                b = b,
                colorStr = hex,
            }
        end
    end
    classes = nil
end

--[[ CORE ENGINE CONSTRUCT ]]--

-- We have to send the names of our three SavedVariables files since the WoW API
-- has no method for parsing them in LUA.
local SVUI = SVLib:NewCore("SVUI_Global", "SVUI_Errors", "SVUI_Profile", "SVUI_Cache")

SVUI.ConfigID           = "SVUI_ConfigOMatic";
SVUI.class              = playerClass;
SVUI.ClassRole          = "";
SVUI.UnitRole           = "NONE";
SVUI.ConfigurationMode  = false;

--[[ EMBEDDED LIBS ]]--

SVUI.L          = LibSuperVillain("Linguist"):Lang();
SVUI.Animate    = LibSuperVillain("Animate");
SVUI.Timers     = LibSuperVillain("Timers");

SVUI.Screen = CreateFrame("Frame", "SVUIParent", UIParent);
SVUI.Screen:SetFrameLevel(UIParent:GetFrameLevel());
SVUI.Screen:SetPoint("CENTER", UIParent, "CENTER");
SVUI.Screen:SetSize(UIParent:GetSize());

SVUI.Hidden = CreateFrame("Frame", nil, UIParent);
SVUI.Hidden:Hide();

SVUI.Options = {
    type = "group",
    name = "|cff339fffConfig-O-Matic|r",
    args = {
        plugins = {
            order = -2,
            type = "group",
            name = "Plugins",
            childGroups = "tab",
            args = {
                pluginheader = {
                    order = 1,
                    type = "header",
                    name = "Supervillain Plugins",
                },
                pluginOptions = {
                    order = 2,
                    type = "group",
                    name = "",
                    args = {
                        pluginlist = {
                            order = 1,
                            type = "group",
                            name = "Summary",
                            args = {
                                active = {
                                    order = 1,
                                    type = "description",
                                    name = function() return SVLib:GetPlugins() end
                                }
                            }
                        },
                    }
                }
            }
        }
    }
}

--[[ BUILD LOGIN MESSAGES ]]--

local commandments = {
    {
        "schemes diabolical",
        "henchmen in-line",
        "entrances grand",
        "battles glorious",
        "power absolute",
    },
    {
        "traps inescapable",
        "enemies overthrown",
        "monologues short",
        "victories infamous",
        "identity a mystery",
    }
};

function SVUI:SetLoginMessage()
    local first = commandments[1][random(1,5)]
    local second = commandments[2][random(1,5)]
    local logMsg1 = (self.L["LOGIN_MSG"]):format(first, second)
    local logMsg2 = (self.L["LOGIN_MSG2"]):format(self.Version)
    local outbound = (messagePattern):format(self.NameID)
    _sendmessage(logMsg1, outbound)
    _sendmessage(logMsg2, outbound)
end

--[[ CORE FUNCTIONS ]]--

function SVUI:fubar() return end

function SVUI:StaticPopup_Show(arg)
    if arg == "ADDON_ACTION_FORBIDDEN" then
        StaticPopup_Hide(arg)
    end
end

function SVUI:Debugger(msg)
    if(not self.DebugMode) then return end
    local outbound = (debugPattern):format(self.NameID, "DEBUG")
    _sendmessage(msg, outbound)
end

function SVUI:AddonMessage(msg)
    local outbound = (messagePattern):format(self.NameID)
    _sendmessage(msg, outbound)
end

function SVUI:ResetAllUI(confirmed)
    if InCombatLockdown()then
        self:AddonMessage(ERR_NOT_IN_COMBAT)
        return
    end
    if(not confirmed) then
        self:StaticPopup_Show('RESET_UI_CHECK')
        return
    end
    self.Setup:Reset()
end

function SVUI:ResetUI(confirmed)
    if InCombatLockdown()then
        self:AddonMessage(ERR_NOT_IN_COMBAT)
        return
    end
    if(not confirmed) then
        self:StaticPopup_Show('RESETMOVERS_CHECK')
        return
    end
    self.Mentalo:Reset()
end

function SVUI:ImportProfile(key)
    self.SystemAlert["COPY_PROFILE_PROMPT"].text = "Are you sure you want to copy the profile '" .. key .. "'?"
    self.SystemAlert["COPY_PROFILE_PROMPT"].OnAccept = function() SVLib:ImportDatabase(key) end
    self:StaticPopup_Show("COPY_PROFILE_PROMPT")
end

function SVUI:ToggleConfig()
    if InCombatLockdown() then
        self:AddonMessage(ERR_NOT_IN_COMBAT)
        self:RegisterEvent('PLAYER_REGEN_ENABLED')
        return
    end
    if not IsAddOnLoaded(self.ConfigID) then
        local _,_,_,_,_,state = GetAddOnInfo(self.ConfigID)
        if state ~= "MISSING" and state ~= "DISABLED" then
            LoadAddOn(self.ConfigID)
            local config_version = GetAddOnMetadata(self.ConfigID, "Version")
            if(tonumber(config_version) < 4) then
                self:StaticPopup_Show("CLIENT_UPDATE_REQUEST")
            end
        else
            local errorMessage = (errorPattern):format(self.ConfigID, state)
            self:AddonMessage(errorMessage)
            return
        end
    end
    local aceConfig = LibStub("AceConfigDialog-3.0")
    local switch = not aceConfig.OpenFrames[self.NameID] and "Open" or "Close"
    aceConfig[switch](aceConfig, self.NameID)
    GameTooltip:Hide()
end

function SVUI:TaintHandler(taint, sourceName, sourceFunc)
    if GetCVarBool('scriptErrors') ~= 1 then return end
    local errorString = ("Error Captured: %s->%s->{%s}"):format(taint, sourceName or "Unknown", sourceFunc or "Unknown")
    self:AddonMessage(errorString)
    self:StaticPopup_Show("TAINT_RL")
end

function SVUI:VersionCheck()
    local minimumVersion = 5.0;
    --print(table.dump(self.safedata))
    local installedVersion = self.safedata.install_version;
    if(installedVersion) then
        if(type(installedVersion) == "string") then
            installedVersion = tonumber(installedVersion)
        end
        if(type(installedVersion) == "number" and installedVersion < minimumVersion) then
            --_removedeprecated()  -- No current deprecated entries to remove
            self.Setup:Install(true)
        end
    else
        self.Setup:Install(true)
    end
end

function SVUI:RefreshEverything(bypass)
    self:RefreshAllSystemMedia();
    self.Mentalo:SetPositions();
    SVLib:RefreshAll();
    if not bypass then
        self:VersionCheck()
    end
end

--[[ EVENT HANDLERS ]]--

function SVUI:PLAYER_ENTERING_WORLD()
    if(not self.RoleIsSet) then
        self:PlayerInfoUpdate()
    end
    if(not self.MediaInitialized) then
        self:RefreshAllSystemMedia()
    end
    local _,instanceType = IsInInstance()
    if(instanceType == "pvp") then
        self.BGTimer = self.Timers:ExecuteLoop(RequestBattlefieldScoreData, 5)
    elseif(self.BGTimer) then
        self.Timers:RemoveLoop(self.BGTimer)
        self.BGTimer = nil
    end
    if(not InCombatLockdown()) then
        collectgarbage("collect")
    end
end

function SVUI:PET_BATTLE_CLOSE()
    self:PushDisplayAudit()
    SVLib:LiveUpdate()
end

function SVUI:PET_BATTLE_OPENING_START()
    self:FlushDisplayAudit()
end

function SVUI:PLAYER_REGEN_DISABLED()
    local forceClosed = false;

    if(IsAddOnLoaded(self.ConfigID)) then
        local aceConfig=LibStub("AceConfigDialog-3.0")
        if aceConfig.OpenFrames[self.NameID] then
            self:RegisterEvent("PLAYER_REGEN_ENABLED")
            aceConfig:Close(self.NameID)
            forceClosed = true
        end
    end

    if(self.Mentalo.Frames) then
        for frame,_ in pairs(self.Mentalo.Frames) do
            if _G[frame] and _G[frame]:IsShown() then
                forceClosed = true;
                _G[frame]:Hide()
            end
        end
    end

    if(HenchmenFrameModel and HenchmenFrame and HenchmenFrame:IsShown()) then
        HenchmenFrame:Hide()
        HenchmenFrameBG:Hide()
        forceClosed = true;
    end

    if forceClosed == true then
        self:AddonMessage(ERR_NOT_IN_COMBAT)
    end

    if(self.NeedsFrameAudit) then
        self:PushDisplayAudit()
    end
end

function SVUI:PLAYER_REGEN_ENABLED()
    self:ToggleConfig()
    self:UnregisterEvent("PLAYER_REGEN_ENABLED")
end

function SVUI:TaintHandler(event, taint, sourceName, sourceFunc)
    if GetCVarBool('scriptErrors') ~= 1 then return end
    local errorString = ("Error Captured: %s->%s->{%s}"):format(taint, sourceName or "Unknown", sourceFunc or "Unknown")
    self:AddonMessage(errorString)
end

--[[ LOAD FUNCTIONS ]]--

function SVUI:ReLoad()
    self.Timers:ClearAllTimers();
    self:RefreshAllSystemMedia();
    self.Mentalo:SetPositions();
    self:AddonMessage("All user settings reloaded");
end

function SVUI:PreLoad()
    self.Timers:ClearAllTimers()

    self:RegisterEvent('PLAYER_REGEN_DISABLED');
    self:RegisterEvent("PLAYER_ENTERING_WORLD");
    self:RegisterEvent("UI_SCALE_CHANGED");
    self:RegisterEvent("PET_BATTLE_CLOSE");
    self:RegisterEvent("PET_BATTLE_OPENING_START");
    self:RegisterEvent("ADDON_ACTION_BLOCKED", "TaintHandler");
    self:RegisterEvent("ADDON_ACTION_FORBIDDEN", "TaintHandler");
    self:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED", "PlayerInfoUpdate");
    self:RegisterEvent("PLAYER_TALENT_UPDATE", "PlayerInfoUpdate");
    self:RegisterEvent("CHARACTER_POINTS_CHANGED", "PlayerInfoUpdate");
    self:RegisterEvent("UNIT_INVENTORY_CHANGED", "PlayerInfoUpdate");
    self:RegisterEvent("UPDATE_BONUS_ACTIONBAR", "PlayerInfoUpdate");
end

function SVUI:Initialize()
    SVLib:Initialize();

    self:UI_SCALE_CHANGED()

    self:RefreshSystemFonts();
    self:LoadSystemAlerts();

    self.Timers:Initialize();

    self:LoadFramework();

    self.safedata = SVLib:GetSafeData();

    SVLib:Launch();

    self:UI_SCALE_CHANGED("PLAYER_LOGIN")
    self:PlayerInfoUpdate();
    self:VersionCheck()
    self:RefreshAllSystemMedia();

    hooksecurefunc("StaticPopup_Show", self.StaticPopup_Show)

    SVLib:RefreshModule("SVMap");

    collectgarbage("collect")

    if self.db.general.loginmessage then
        self:SetLoginMessage()
    end
end
--[[
##########################################################
THE CLEANING LADY
##########################################################
]]--
local LemonPledge = 0;
local Consuela = CreateFrame("Frame")
Consuela:RegisterAllEvents()
Consuela:SetScript("OnEvent", function(self, event)
    LemonPledge = LemonPledge  +  1
    if(InCombatLockdown()) then return end;
    if(LemonPledge > 10000) then
        collectgarbage("collect");
        LemonPledge = 0;
    end
end)