--[[ ############################################################################## _____/\\\\\\\\\\\____/\\\________/\\\__/\\\________/\\\__/\\\\\\\\\\\_ # ___/\\\/////////\\\_\/\\\_______\/\\\_\/\\\_______\/\\\_\/////\\\///__ # __\//\\\______\///__\//\\\______/\\\__\/\\\_______\/\\\_____\/\\\_____ # ___\////\\\__________\//\\\____/\\\___\/\\\_______\/\\\_____\/\\\_____ # ______\////\\\________\//\\\__/\\\____\/\\\_______\/\\\_____\/\\\_____ # _________\////\\\______\//\\\/\\\_____\/\\\_______\/\\\_____\/\\\_____ # __/\\\______\//\\\______\//\\\\\______\//\\\______/\\\______\/\\\_____ # _\///\\\\\\\\\\\/________\//\\\________\///\\\\\\\\\/____/\\\\\\\\\\\_# ___\///////////___________\///___________\/////////_____\///////////_# ############################################################################## S U P E R - V I L L A I N - U I By: Munglunch # ############################################################################## ]]-- --[[ GLOBALS ]]-- local _G = _G; local unpack = _G.unpack; local select = _G.select; local pairs = _G.pairs; local type = _G.type; local rawset = _G.rawset; local rawget = _G.rawget; local tinsert = _G.tinsert; local tremove = _G.tremove; local tostring = _G.tostring; local error = _G.error; local getmetatable = _G.getmetatable; local setmetatable = _G.setmetatable; local string = _G.string; local math = _G.math; local table = _G.table; --[[ STRING METHODS ]]-- local upper = string.upper; local format, find, match, gsub = string.format, string.find, string.match, string.gsub; --[[ MATH METHODS ]]-- local floor = math.floor --[[ TABLE METHODS ]]-- local tsort, tconcat = table.sort, table.concat; --[[ ########################################################## CONSTANTS ########################################################## ]]-- SVUI_LIB, SVUI_LOCALE = {}, {} BINDING_HEADER_SVUI = GetAddOnMetadata(..., "Title"); SLASH_RELOADUI1="/rl" SLASH_RELOADUI2="/reloadui" SlashCmdList.RELOADUI=ReloadUI --[[ ########################################################## LOCAL VARIABLES ########################################################## ]]-- local callbacks = {}; local numCallbacks = 0; --[[ ########################################################## MUNGLUNCH's FASTER ASSERT FUNCTION ########################################################## ]]-- function enforce(condition, ...) if not condition then if next({...}) then local fn = function (...) return(string.format(...)) end local s,r = pcall(fn, ...) if s then error("Error!: " .. r, 2) end end error("Error!", 2) end end local assert = enforce; --[[ ########################################################## OBJECT CONSTRUCTOR GLOBAL ########################################################## ]]-- local PreLoadQueue, InitQueue, PostLoadQueue = {},{},{}; local INFO_BY = "%s by %s"; local INFO_VERSION = "%s%s - Version: %d"; local INFO_NEW = "%s (Newest: %d)"; local INFO_NAME = "Plugins"; local INFO_HEADER = "SuperVillain UI (version %.3f): Plugins"; if GetLocale() == "ruRU" then INFO_BY = "%s от %s"; INFO_VERSION = "%s%s - Версия: %d"; INFO_NEW = "%s (Последняя: %d)"; INFO_NAME = "Плагины"; INFO_HEADER = "SuperVillain UI (устарела %.3f): Плагины"; end local rootstring = function(self) return self.__namekey end local changeDBVar = function(self, value, key, sub1, sub2, sub3) local config = self.__owner.db[self.__namekey] if((sub1 and sub2 and sub3) and (config[sub1] and config[sub1][sub2] and config[sub1][sub2][sub3])) then self.__owner.db[self.__namekey][sub1][sub2][sub3][key] = value elseif((sub1 and sub2) and (config[sub1] and config[sub1][sub2])) then self.__owner.db[self.__namekey][sub1][sub2][key] = value elseif(sub1 and config[sub1]) then self.__owner.db[self.__namekey][sub1][key] = value else self.__owner.db[self.__namekey][key] = value end self.db = self.__owner.db[self.__namekey] end local innerOnEvent = function(self, event, ...) local obj = self.__owner if self[event] and type(self[event]) == "function" then self[event](obj, event, ...) end end local registerEvent = function(self, eventname, eventfunc) if not self.___eventframe then self.___eventframe = CreateFrame("Frame", nil) self.___eventframe.__owner = self self.___eventframe:SetScript("OnEvent", innerOnEvent) end local fn = eventfunc if type(eventfunc) == "string" then fn = self[eventfunc] elseif(not fn and self[eventname]) then fn = self[eventname] end self.___eventframe[eventname] = fn self.___eventframe:RegisterEvent(eventname) end local unregisterEvent = function(self, event, ...) if(self.___eventframe) then self.___eventframe:UnregisterEvent(event) end end local addonEvent = function(self, event, addon) if addon == "SVUI_ConfigOMatic" then local list = self.__owner.Plugins for i, plugin in pairs(list) do if(plugin.callback) then plugin.callback() end end end end local function SetPrototype(obj, name, parent) local addonmeta = {} local oldmeta = getmetatable(obj) if oldmeta then for k, v in pairs(oldmeta) do addonmeta[k] = v end end addonmeta.__tostring = rootstring setmetatable( obj, addonmeta ) obj.__namekey = name obj.__owner = parent if(parent.db[name]) then obj.db = parent.db[name] end obj.initialized = false obj.CombatLocked = false obj.ChangeDBVar = changeDBVar obj.RegisterEvent = registerEvent obj.UnregisterEvent = unregisterEvent return obj end local Registry_SetCallback = function(self, fn) if(fn and type(fn) == "function") then self.Callbacks[#self.Callbacks+1] = fn end end local Registry_NewScript = function(self, fn) if(fn and type(fn) == "function") then PostLoadQueue[#PostLoadQueue+1] = fn end end local Registry_NewPackage = function(self, obj, name, priority) if self.__owner[name] then return end if(priority == "pre") then PreLoadQueue[#PreLoadQueue+1] = name else InitQueue[#InitQueue+1] = name end self.Packages[#self.Packages+1] = name self.__owner[name] = SetPrototype(obj, name, self.__owner) if(self.__owner.CoreEnabled) then if(self.__owner[name].Load) then self.__owner[name]:Load() end end end local Registry_FetchPlugins = function(self) local list = ""; for name, plugin in pairs(self.Plugins) do if name ~= self.MAJOR then local author = GetAddOnMetadata(name, "Author") local Pname = GetAddOnMetadata(name, "Title") or name local color = plugin.old and "|cffFF0000" or "|cff00FF00" list = ("%s%s"):format(list, Pname) if author then list = self.INFO_BY:format(list, author) end list = self.INFO_VERSION:format(list, color, plugin.version) if plugin.old then list = self.INFO_NEW:format(list, plugin.newversion) end list = ("%s|r\n"):format(list) end end return list end local Registry_NewPlugin = function(self, name, func) local ver = name == self.MAJOR and self.MINOR or GetAddOnMetadata(name, "Version") self.Plugins[name] = { version = ver, callback = func } local enable, loadable = select(4,GetAddOnInfo("SVUI_ConfigOMatic")) if(enable and loadable and IsAddOnLoaded("SVUI_ConfigOMatic")) then if name ~= self.MAJOR then self.__owner.Options.args.plugins.args.pluginlist.name = self:FetchPlugins() end if(func) then func() end end end local Registry_RunCallbacks = function(self) for i=1, #self.Callbacks do local fn = self.Callbacks[i] if(fn and type(fn) == "function") then fn() end end end local Registry_Update = function(self, name, dataOnly) local obj = self.__owner[name] if obj then if obj.ReLoad and not dataOnly then obj:ReLoad() end end end local Registry_UpdateAll = function(self) local list = self.Packages for _,name in pairs(list) do local obj = self.__owner[name] if obj and obj.ReLoad then obj:ReLoad() end end end local Registry_Lights = function(self) if not PreLoadQueue then return end for i=1,#PreLoadQueue do local name = PreLoadQueue[i] local obj = self.__owner[name] if obj and not obj.initialized then if self.__owner.db[name] then obj.db = self.__owner.db[name] end if obj.Load then obj:Load() obj.Load = nil end obj.initialized = true; end end PreLoadQueue = nil end local Registry_Camera = function(self) if not InitQueue then return end for i=1,#InitQueue do local name = InitQueue[i] local obj = self.__owner[name] if obj and not obj.initialized then if self.__owner.db[name] then obj.db = self.__owner.db[name] end if obj.Load then obj:Load() obj.Load = nil end obj.initialized = true; end end InitQueue = nil end local Registry_Action = function(self) if not PostLoadQueue then return end for i=1, #PostLoadQueue do local fn = PostLoadQueue[i] if(fn and type(fn) == "function") then fn() end end PostLoadQueue = nil end local AppendRegistry = function(obj, major, minor) local methods = { __owner = obj, Packages = {}, Plugins = {}, Callbacks = {}, MAJOR = major, MINOR = minor, INFO_BY = INFO_BY, INFO_VERSION = INFO_VERSION, INFO_NEW = INFO_NEW, INFO_NAME = INFO_NAME, INFO_HEADER = INFO_HEADER, SetCallback = Registry_SetCallback, NewScript = Registry_NewScript, NewPackage = Registry_NewPackage, FetchPlugins = Registry_FetchPlugins, NewPlugin = Registry_NewPlugin, RunCallbacks = Registry_RunCallbacks, Update = Registry_Update, UpdateAll = Registry_UpdateAll, Lights = Registry_Lights, Camera = Registry_Camera, Action = Registry_Action } local mt = {__tostring = rootstring} setmetatable(methods, mt) return methods end function SVUI_LIB:SetObject(major, minor, registry) local obj = {} obj.__namekey = major obj.version = minor local mt = {} local old = getmetatable(obj) if old then for k, v in pairs(old) do mt[k] = v end end mt.__tostring = rootstring setmetatable(obj, mt) if(registry) then obj.Registry = AppendRegistry(obj, major, minor) end return obj end --[[ ########################################################## LOCALIZATION GLOBAL ########################################################## ]]-- local failsafe = function() assert(false) end local metaread = { __index = function(self, key) rawset(self, key, key) return key end } local activeLocale local defaultwrite = setmetatable({}, { __newindex = function(self, key, value) if not rawget(activeLocale, key) then rawset(activeLocale, key, value == true and key or value) end end, __index = failsafe }) local metawrite = setmetatable({}, { __newindex = function(self, key, value) rawset(activeLocale, key, value == true and key or value) end, __index = failsafe }) function SVUI_LOCALE:SetLocalStrings(locale, isDefault) local gameLocale = GetLocale() if gameLocale == "enGB" then gameLocale = "enUS" end if not SVUI_LOCALE["LANG"] then SVUI_LOCALE["LANG"] = setmetatable({}, metaread) end activeLocale = SVUI_LOCALE["LANG"] if isDefault then return defaultwrite elseif(locale == GAME_LOCALE or locale == gameLocale) then return metawrite end end function SVUI_LOCALE:SetObject() return SVUI_LOCALE["LANG"] end --[[ ########################################################## LOCAL FUNCTIONS ########################################################## ]]-- local function formatValueString(text) if "string" == type(text) then text = gsub(text,"\n","\\n") if match(gsub(text,"[^'\"]",""),'^"+$') then return "'"..text.."'"; else return '"'..gsub(text,'"','\\"')..'"'; end else return tostring(text); end end local function formatKeyString(text) if "string"==type(text) and match(text,"^[_%a][_%a%d]*$") then return text; else return "["..formatValueString(text).."]"; end end 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 ########################################################## ]]-- SVUI_CLASS_COLORS = {}; do 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 = format("ff%02x%02x%02x", 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 --[[ ########################################################## APPENDED GLOBAL FUNCTIONS ########################################################## ]]-- function math.parsefloat(value,decimal) if decimal and decimal > 0 then local calc1 = 10 ^ decimal; local calc2 = (value * calc1) + 0.5; return floor(calc2) / calc1 end return floor(value + 0.5) end function table.dump(targetTable) local dumpTable = {}; local dumpCheck = {}; for key,value in ipairs(targetTable) do tinsert(dumpTable, formatValueString(value)); dumpCheck[key] = true; end for key,value in pairs(targetTable) do if not dumpCheck[key] then tinsert(dumpTable, "\n "..formatKeyString(key).." = "..formatValueString(value)); end end local output = tconcat(dumpTable, ", "); return "{ "..output.." }"; end function table.copy(targetTable,deepCopy,mergeTable) mergeTable = mergeTable or {}; if targetTable==nil then return nil end if mergeTable[targetTable] then return mergeTable[targetTable] end local replacementTable = {} for key,value in pairs(targetTable)do if deepCopy and type(value) == "table" then replacementTable[key] = table.copy(value, deepCopy, mergeTable) else replacementTable[key] = value end end setmetatable(replacementTable, table.copy(getmetatable(targetTable), deepCopy, mergeTable)) mergeTable[targetTable] = replacementTable; return replacementTable end function string.trim(this) return this:find'^%s*$' and '' or this:match'^%s*(.*%S)' end function string.color(this,color) return "|cff"..color..this.."|r"; end function string.link(this,prefix,text,color) return "|H"..prefix..":"..tostring(text).."|h"..tostring(this):color(color or"ffffff").."|h"; end