local setmetatable = setmetatable local type = type local tostring = tostring local select = select local pairs = pairs local tinsert = table.insert local tsort = table.sort local str_lower = string.lower local math_abs = math.abs local GetTime = GetTime local InCombatLockdown = InCombatLockdown local CreateFrame = CreateFrame local DEFAULT_CHAT_FRAME = DEFAULT_CHAT_FRAME local IsAddOnLoaded = IsAddOnLoaded local GetBattlefieldStatus = GetBattlefieldStatus local IsActiveBattlefieldArena = IsActiveBattlefieldArena local IsInInstance = IsInInstance local GetNumArenaOpponents = GetNumArenaOpponents local RELEASE_TYPES = { alpha = "Alpha", beta = "Beta", release = "Release"} local PREFIX = "TBC-Classic_v" local VERSION_REGEX = PREFIX .. "(%d+%.%d+)%-(%a)" local LibStub = LibStub --------------------------- -- CORE --------------------------- local MAJOR, MINOR = "Gladdy", 4 local Gladdy = LibStub:NewLibrary(MAJOR, MINOR) local L Gladdy.version_major_num = 1 Gladdy.version_minor_num = 0.22 Gladdy.version_num = Gladdy.version_major_num + Gladdy.version_minor_num Gladdy.version_releaseType = RELEASE_TYPES.release Gladdy.version = PREFIX .. Gladdy.version_num .. "-" .. Gladdy.version_releaseType Gladdy.VERSION_REGEX = VERSION_REGEX LibStub("AceTimer-3.0"):Embed(Gladdy) LibStub("AceComm-3.0"):Embed(Gladdy) Gladdy.modules = {} setmetatable(Gladdy, { __tostring = function() return MAJOR end }) function Gladdy:Print(...) local text = "|cff0384fcGladdy|r:" local val for i = 1, select("#", ...) do val = select(i, ...) if (type(val) == 'boolean') then val = val and "true" or false end text = text .. " " .. tostring(val) end DEFAULT_CHAT_FRAME:AddMessage(text) end function Gladdy:Warn(...) local text = "|cfffc0303Gladdy|r:" local val for i = 1, select("#", ...) do val = select(i, ...) if (type(val) == 'boolean') then val = val and "true" or false end text = text .. " " .. tostring(val) end DEFAULT_CHAT_FRAME:AddMessage(text) end Gladdy.events = CreateFrame("Frame") Gladdy.events.registered = {} Gladdy.events:RegisterEvent("PLAYER_LOGIN") Gladdy.events:RegisterEvent("CVAR_UPDATE") hooksecurefunc("VideoOptionsFrameOkay_OnClick", function(self, button, down, apply) if (self:GetName() == "VideoOptionsFrameApply") then Gladdy:PixelPerfectScale(true) end end) Gladdy.events:SetScript("OnEvent", function(self, event, ...) if (event == "PLAYER_LOGIN") then Gladdy:OnInitialize() Gladdy:OnEnable() elseif (event == "CVAR_UPDATE") then if (str_lower(select(1, ...)) == "uiscale") then Gladdy:PixelPerfectScale(true) end else local func = self.registered[event] if (type(Gladdy[func]) == "function") then Gladdy[func](Gladdy, event, ...) end end end) function Gladdy:RegisterEvent(event, func) self.events.registered[event] = func or event self.events:RegisterEvent(event) end function Gladdy:UnregisterEvent(event) self.events.registered[event] = nil self.events:UnregisterEvent(event) end function Gladdy:UnregisterAllEvents() self.events.registered = {} self.events:UnregisterAllEvents() end --------------------------- -- MODULE FUNCTIONS --------------------------- local function pairsByPrio(t) local a = {} for k, v in pairs(t) do tinsert(a, { k, v.priority }) end tsort(a, function(x, y) return x[2] > y[2] end) local i = 0 return function() i = i + 1 if (a[i] ~= nil) then return a[i][1], t[a[i][1]] else return nil end end end function Gladdy:IterModules() return pairsByPrio(self.modules) end function Gladdy:Call(module, func, ...) if (type(module) == "string") then module = self.modules[module] end if (type(module[func]) == "function") then module[func](module, ...) end end function Gladdy:SendMessage(message, ...) for _, module in self:IterModules() do self:Call(module, module.messages[message], ...) end end function Gladdy:NewModule(name, priority, defaults) local module = CreateFrame("Frame") module.name = name module.priority = priority or 0 module.defaults = defaults or {} module.messages = {} module.RegisterMessage = function(self, message, func) self.messages[message] = func or message end module.GetOptions = function() return nil end for k, v in pairs(module.defaults) do self.defaults.profile[k] = v end self.modules[name] = module return module end function Gladdy:CreateMover(frame, xConfig, yConfig, name, points, width, height, xOffset, yOffset) if not frame.mover then frame.mover = CreateFrame("Frame", nil, frame, BackdropTemplateMixin and "BackdropTemplate") frame.mover:SetFrameStrata("DIALOG") frame.mover:SetPoint(points[1], frame, points[2], xOffset or 0, yOffset or 0) frame.mover:SetHeight(height or frame:GetHeight()) frame.mover:SetWidth(width or frame:GetWidth()) local backdrop = { bgFile = "Interface/Tooltips/UI-Tooltip-Background", edgeFile = "", tile = true, tileSize = 16, edgeSize = 10, insets = {left = 0, right = 0, top = 0, bottom = 0} } frame.mover:SetBackdrop(backdrop) frame.mover:SetBackdropColor(0,1,0,0.5) frame.mover.border = CreateFrame("Frame", nil, frame.mover, BackdropTemplateMixin and "BackdropTemplate") frame.mover.border:SetBackdrop({ edgeFile = Gladdy:SMFetch("border", "highlightBorderStyle"), edgeSize = 2 }) frame.mover.border:SetAllPoints(frame.mover) frame.mover.border:SetBackdropBorderColor(0,1,0,1) frame.mover.text = frame.mover.border:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") frame.mover.text:SetText(name) frame.mover.text:SetPoint("CENTER") frame.mover:SetMovable(true) frame.mover:EnableMouse(true) frame.mover:SetScript("OnMouseDown", function(self) self.point = { frame:GetPoint() } self.start = { frame:GetCenter() } frame:StartMoving() self:StartMoving() end) frame.mover:SetScript("OnMouseUp", function(self) frame:StopMovingOrSizing() self:StopMovingOrSizing() self.stop = { frame:GetCenter() } local diffX = math_abs(self.start[1] - self.stop[1]) diffX = self.start[1] > self.stop[1] and -diffX or diffX local diffY = math_abs(self.start[2] - self.stop[2]) diffY = self.start[2] > self.stop[2] and -diffY or diffY frame:ClearAllPoints() frame:SetPoint(self.point[1], self.point[2], self.point[3], self.point[4] + diffX, self.point[5] + diffY) Gladdy.db[xConfig] = self.point[4] + diffX Gladdy.db[yConfig] = self.point[5] + diffY LibStub("AceConfigRegistry-3.0"):NotifyChange("Gladdy") Gladdy:UpdateFrame() end) else frame.mover:ClearAllPoints() frame.mover:SetPoint(points[1], frame, points[2], xOffset or 0, yOffset or 0) frame.mover:SetHeight(height or frame:GetHeight()) frame.mover:SetWidth(width or frame:GetWidth()) end if self.frame and self.frame.testing then frame.mover:Show() else frame.mover:Hide() end end --------------------------- -- INIT --------------------------- function Gladdy:DeleteUnknownOptions(tbl, refTbl, str) if str == nil then str = "Gladdy.db" end for k,v in pairs(tbl) do if refTbl[k] == nil then --Gladdy:Print("SavedVariable deleted:", str .. "." .. k, "not found!") tbl[k] = nil else if type(v) ~= type(refTbl[k]) then --Gladdy:Print("SavedVariable deleted:", str .. "." .. k, "type error!", "Expected", type(refTbl[k]), "but found", type(v)) tbl[k] = nil elseif type(v) == "table" then Gladdy:DeleteUnknownOptions(v, refTbl[k], str .. "." .. k) end end end end function Gladdy:PixelPerfectScale(update) local physicalWidth, physicalHeight = GetPhysicalScreenSize() local perfectUIScale = 768/physicalHeight--768/select(2, strsplit("x",({ GetScreenResolutions()})[GetCurrentResolution()])) if self.db and self.db.pixelPerfect and self.frame then self.frame:SetIgnoreParentScale(true) self.frame:SetScale(perfectUIScale) --self.db.frameScale = perfectUIScale --(GetCVar("useUiScale") == "1" and 1 + perfectUIScale - GetCVar("UIScale") or perfectUIScale) if update then self:UpdateFrame() end elseif self.frame then self.frame:SetIgnoreParentScale(false) end end function Gladdy:OnInitialize() self.dbi = LibStub("AceDB-3.0"):New("GladdyXZ", self.defaults) self.dbi.RegisterCallback(self, "OnProfileChanged", "OnProfileChanged") self.dbi.RegisterCallback(self, "OnProfileCopied", "OnProfileChanged") self.dbi.RegisterCallback(self, "OnProfileReset", "OnProfileChanged") self.db = self.dbi.profile self.LSM = LibStub("LibSharedMedia-3.0") self.LSM:Register("statusbar", "Gloss", "Interface\\AddOns\\Gladdy\\Images\\Gloss") self.LSM:Register("statusbar", "Smooth", "Interface\\AddOns\\Gladdy\\Images\\Smooth") self.LSM:Register("statusbar", "Minimalist", "Interface\\AddOns\\Gladdy\\Images\\Minimalist") self.LSM:Register("statusbar", "LiteStep", "Interface\\AddOns\\Gladdy\\Images\\LiteStep.tga") self.LSM:Register("border", "Gladdy Tooltip round", "Interface\\AddOns\\Gladdy\\Images\\UI-Tooltip-Border_round_selfmade") self.LSM:Register("border", "Gladdy Tooltip squared", "Interface\\AddOns\\Gladdy\\Images\\UI-Tooltip-Border_square_selfmade") self.LSM:Register("font", "DorisPP", "Interface\\AddOns\\Gladdy\\Images\\DorisPP.TTF") self.LSM:Register("border", "Square Full White", "Interface\\AddOns\\Gladdy\\Images\\Square_FullWhite.tga") L = self.L self.testData = { ["arena1"] = { name = "Swift", raceLoc = L["NightElf"], classLoc = L["Warrior"], class = "WARRIOR", health = 9635, healthMax = 14207, power = 76, powerMax = 100, powerType = 1, testSpec = L["Arms"], race = "NightElf" }, ["arena2"] = { name = "Vilden", raceLoc = L["Undead"], classLoc = L["Mage"], class = "MAGE", health = 10969, healthMax = 11023, power = 7833, powerMax = 10460, powerType = 0, testSpec = L["Frost"], race = "Scourge" }, ["arena3"] = { name = "Krymu", raceLoc = L["Human"], classLoc = L["Rogue"], class = "ROGUE", health = 1592, healthMax = 11740, power = 45, powerMax = 110, powerType = 3, testSpec = L["Subtlety"], race = "Human" }, ["arena4"] = { name = "Talmon", raceLoc = L["Human"], classLoc = L["Warlock"], class = "WARLOCK", health = 10221, healthMax = 14960, power = 9855, powerMax = 9855, powerType = 0, testSpec = L["Demonology"], race = "Human" }, ["arena5"] = { name = "Hydra", raceLoc = L["Undead"], classLoc = L["Priest"], class = "PRIEST", health = 11960, healthMax = 11960, power = 2515, powerMax = 10240, powerType = 0, testSpec = L["Discipline"], race = "Human" }, } self.cooldownSpellIds = {} self.spellTextures = {} self.specBuffs = self:GetSpecBuffs() self.specSpells = self:GetSpecSpells() self.buttons = {} self.guids = {} self.curBracket = nil self.curUnit = 1 self:SetupOptions() for _, module in self:IterModules() do self:Call(module, "Initialize") -- B.E > A.E :D end self:DeleteUnknownOptions(self.db, self.defaults.profile) if Gladdy.db.hideBlizzard == "always" then Gladdy:BlizzArenaSetAlpha(0) end end function Gladdy:OnProfileChanged() self.db = self.dbi.profile self:DeleteUnknownOptions(self.db, self.defaults.profile) self:HideFrame() self:ToggleFrame(3) end function Gladdy:OnEnable() self:RegisterEvent("UPDATE_BATTLEFIELD_STATUS") self:RegisterEvent("PLAYER_ENTERING_WORLD") self:RegisterEvent("PLAYER_REGEN_ENABLED") if (IsAddOnLoaded("Clique")) then for i = 1, 5 do self:CreateButton(i) end ClickCastFrames = ClickCastFrames or {} ClickCastFrames[self.buttons.arena1.secure] = true ClickCastFrames[self.buttons.arena2.secure] = true ClickCastFrames[self.buttons.arena3.secure] = true ClickCastFrames[self.buttons.arena4.secure] = true ClickCastFrames[self.buttons.arena5.secure] = true end if (not self.db.locked and self.db.x == 0 and self.db.y == 0) then self:Print(L["Welcome to Gladdy!"]) self:Print(L["First run has been detected, displaying test frame."]) self:Print(L["Valid slash commands are:"]) self:Print(L["/gladdy ui"]) self:Print(L["/gladdy test2-5"]) self:Print(L["/gladdy hide"]) self:Print(L["/gladdy reset"]) self:Print(L["If this is not your first run please lock or move the frame to prevent this from happening."]) self:HideFrame() self:ToggleFrame(3) self.showConfig = true end end function Gladdy:GetIconStyles() return { ["Interface\\AddOns\\Gladdy\\Images\\Border_rounded_blp"] = L["Gladdy Tooltip round"], ["Interface\\AddOns\\Gladdy\\Images\\Border_squared_blp"] = L["Gladdy Tooltip squared"], ["Interface\\AddOns\\Gladdy\\Images\\Border_Gloss"] = L["Gloss (black border)"], } end --------------------------- -- TEST --------------------------- function Gladdy:Test() self.frame.testing = true if self.curBracket then for i = 1, self.curBracket do local unit = "arena" .. i if (not self.buttons[unit]) then self:CreateButton(i) end local button = self.buttons[unit] for k, v in pairs(self.testData[unit]) do button[k] = v end for _, module in self:IterModules() do self:Call(module, "Test", unit) end button:SetAlpha(1) end end end --------------------------- -- EVENT HANDLING --------------------------- function Gladdy:PLAYER_ENTERING_WORLD() if self.showConfig then LibStub("AceConfigDialog-3.0"):Open("Gladdy", nil, LibStub("AceConfigDialog-3.0"):SelectGroup("Gladdy", "XiconProfiles")) self.showConfig = nil end if (self.frame and self.frame:IsVisible()) then self:Reset() self:HideFrame() end end function Gladdy:UPDATE_BATTLEFIELD_STATUS(_, index) local status, mapName, instanceID, levelRangeMin, levelRangeMax, teamSize, isRankedArena, suspendedQueue, bool, queueType = GetBattlefieldStatus(index) local instanceType = select(2, IsInInstance()) Gladdy:Debug("INFO", "UPDATE_BATTLEFIELD_STATUS", instanceType, status, teamSize) if ((instanceType == "arena" or GetNumArenaOpponents() > 0) and status == "active" and teamSize > 0) then self.curBracket = teamSize self:JoinedArena() end end function Gladdy:PLAYER_REGEN_ENABLED() if self.showFrame then self:UpdateFrame() if self.startTest then self:Test() self.startTest = nil end self.frame:Show() self.showFrame = nil end if self.hideFrame then self:Reset() self.frame:Hide() self.hideFrame = nil end end --------------------------- -- RESET FUNCTIONS (ARENA LEAVE) --------------------------- function Gladdy:Reset() if type(self.guids) == "table" then for k,_ in pairs(self.guids) do self.guids[k] = nil end end self.guids = {} self.curBracket = nil self.curUnit = 1 for _, module in self:IterModules() do self:Call(module, "Reset") end for unit in pairs(self.buttons) do self:ResetUnit(unit) end if Gladdy.db.hideBlizzard == "never" or Gladdy.db.hideBlizzard == "arena" then Gladdy:BlizzArenaSetAlpha(1) end end function Gladdy:ResetUnit(unit) local button = self.buttons[unit] if (not button) then return end button:SetAlpha(0) self:ResetButton(unit) for _, module in self:IterModules() do self:Call(module, "ResetUnit", unit) end end function Gladdy:ResetButton(unit) local button = self.buttons[unit] if (not button) then return end for k1, v1 in pairs(self.BUTTON_DEFAULTS) do if (type(v1) == "string") then button[k1] = nil elseif (type(v1) == "number") then button[k1] = 0 elseif (type(v1) == "table") then button[k1] = {} elseif (type(v1) == "boolean") then button[k1] = false end end end --------------------------- -- ARENA JOINED --------------------------- function Gladdy:JoinedArena() if not self.curBracket then self.curBracket = 2 end for i = 1, self.curBracket do if (not self.buttons["arena" .. i]) then self:CreateButton(i) end end self:SendMessage("JOINED_ARENA") if InCombatLockdown() then Gladdy:Print("Gladdy frames show as soon as you leave combat") self.showFrame = true else self:UpdateFrame() self.frame:Show() end for i=1, self.curBracket do self.buttons["arena" .. i]:SetAlpha(1) end if Gladdy.db.hideBlizzard == "arena" or Gladdy.db.hideBlizzard == "always" then Gladdy:BlizzArenaSetAlpha(0) end end --------------------------- -- BLIZZARD FRAMES --------------------------- function Gladdy:BlizzArenaSetAlpha(alpha) if IsAddOnLoaded("Blizzard_ArenaUI") then if (ArenaEnemyFrames) then ArenaEnemyFrames:SetAlpha(alpha) end if ArenaEnemyFrame1 then ArenaEnemyFrame1:SetAlpha(alpha) end if ArenaEnemyFrame1PetFrame then ArenaEnemyFrame1PetFrame:SetAlpha(alpha) end if ArenaEnemyFrame2 then ArenaEnemyFrame2:SetAlpha(alpha) end if ArenaEnemyFrame2PetFrame then ArenaEnemyFrame2PetFrame:SetAlpha(alpha) end if ArenaEnemyFrame3 then ArenaEnemyFrame3:SetAlpha(alpha) end if ArenaEnemyFrame3PetFrame then ArenaEnemyFrame3PetFrame:SetAlpha(alpha) end if ArenaEnemyFrame4 then ArenaEnemyFrame4:SetAlpha(alpha) end if ArenaEnemyFrame4PetFrame then ArenaEnemyFrame4PetFrame:SetAlpha(alpha) end if ArenaEnemyFrame5 then ArenaEnemyFrame5:SetAlpha(alpha) end if ArenaEnemyFrame5PetFrame then ArenaEnemyFrame5PetFrame:SetAlpha(alpha) end end end --------------------------- -- FONT/STATUSBAR/BORDER --------------------------- local defaults = {["statusbar"] = "Smooth", ["border"] = "Gladdy Tooltip round", ["font"] = "DorisPP"} local lastWarning = {} function Gladdy:SMFetch(lsmType, key) local smMediaType = self.LSM:Fetch(lsmType, Gladdy.db[key]) if (smMediaType == nil and Gladdy.db[key] ~= "None") then if not lastWarning[key] or GetTime() - lastWarning[key] > 120 then lastWarning[key] = GetTime() Gladdy:Warn("Could not find", "\"" .. lsmType .. "\" \"", Gladdy.db[key], " \" for", "\"" .. key .. "\"", "- setting it to", "\"" .. defaults[lsmType] .. "\"") end return self.LSM:Fetch(lsmType, defaults[lsmType]) end return smMediaType end