if not WeakAuras.IsCorrectVersion() or not WeakAuras.IsLibsOK() then return end local AddonName, Private = ... local L = WeakAuras.L local MSQ, MSQ_Version = LibStub("Masque", true); if MSQ then if MSQ_Version <= 80100 then MSQ = nil WeakAuras.prettyPrint(L["Please upgrade your Masque version"]) else MSQ:AddType("WA_Aura", {"Icon", "Cooldown"}) end end -- WoW API local _G = _G local default = { icon = true, desaturate = false, iconSource = -1, inverse = false, width = 64, height = 64, color = {1, 1, 1, 1}, selfPoint = "CENTER", anchorPoint = "CENTER", anchorFrameType = "SCREEN", xOffset = 0, yOffset = 0, zoom = 0, keepAspectRatio = false, frameStrata = 1, cooldown = false, cooldownEdge = false }; WeakAuras.regionPrototype.AddAlphaToDefault(default); local screenWidth, screenHeight = math.ceil(GetScreenWidth() / 20) * 20, math.ceil(GetScreenHeight() / 20) * 20; local properties = { desaturate = { display = L["Desaturate"], setter = "SetDesaturated", type = "bool", }, width = { display = L["Width"], setter = "SetRegionWidth", type = "number", min = 1, softMax = screenWidth, bigStep = 1, default = 32 }, height = { display = L["Height"], setter = "SetRegionHeight", type = "number", min = 1, softMax = screenHeight, bigStep = 1, default = 32 }, color = { display = L["Color"], setter = "Color", type = "color" }, inverse = { display = L["Inverse"], setter = "SetInverse", type = "bool" }, cooldownEdge = { display = { L["Cooldown"], L["Edge"]}, setter = "SetCooldownEdge", type = "bool", }, zoom = { display = L["Zoom"], setter = "SetZoom", type = "number", min = 0, max = 1, step = 0.01, default = 0, isPercent = true }, iconSource = { display = {L["Icon"], L["Source"]}, setter = "SetIconSource", type = "list", values = {} }, displayIcon = { display = {L["Icon"], L["Fallback"]}, setter = "SetIcon", type = "icon", } }; WeakAuras.regionPrototype.AddProperties(properties, default); local function GetProperties(data) local result = CopyTable(properties) result.iconSource.values = Private.IconSources(data) return result end local function GetTexCoord(region, texWidth, aspectRatio) region.currentCoord = region.currentCoord or {} local usesMasque = false if region.MSQGroup then local db = region.MSQGroup.db if db and not db.Disabled then usesMasque = true region.currentCoord[1], region.currentCoord[2], region.currentCoord[3], region.currentCoord[4], region.currentCoord[5], region.currentCoord[6], region.currentCoord[7], region.currentCoord[8] = region.icon:GetTexCoord() end end if (not usesMasque) then region.currentCoord[1], region.currentCoord[2], region.currentCoord[3], region.currentCoord[4], region.currentCoord[5], region.currentCoord[6], region.currentCoord[7], region.currentCoord[8] = 0, 0, 0, 1, 1, 0, 1, 1; end local xRatio = aspectRatio < 1 and aspectRatio or 1; local yRatio = aspectRatio > 1 and 1 / aspectRatio or 1; for i, coord in ipairs(region.currentCoord) do local aspectRatio = (i % 2 == 1) and xRatio or yRatio; region.currentCoord[i] = (coord - 0.5) * texWidth * aspectRatio + 0.5; end return unpack(region.currentCoord) end local function AnchorSubRegion(self, subRegion, anchorType, selfPoint, anchorPoint, anchorXOffset, anchorYOffset) if anchorType == "area" then WeakAuras.regionPrototype.AnchorSubRegion(selfPoint == "region" and self or self.icon, subRegion, anchorType, selfPoint, anchorPoint, anchorXOffset, anchorYOffset) else subRegion:ClearAllPoints() anchorPoint = anchorPoint or "CENTER" local anchorRegion = self.icon if anchorPoint:sub(1, 6) == "INNER_" then if not self.inner then self.inner = CreateFrame("Frame", nil, self) self.inner:SetPoint("CENTER") self.UpdateInnerOuterSize() end anchorRegion = self.inner anchorPoint = anchorPoint:sub(7) elseif anchorPoint:sub(1, 6) == "OUTER_" then if not self.outer then self.outer = CreateFrame("Frame", nil, self) self.outer:SetPoint("CENTER") self.UpdateInnerOuterSize() end anchorRegion = self.outer anchorPoint = anchorPoint:sub(7) end anchorXOffset = anchorXOffset or 0 anchorYOffset = anchorYOffset or 0 if not Private.point_types[selfPoint] then selfPoint = "CENTER" end if not Private.point_types[anchorPoint] then anchorPoint = "CENTER" end subRegion:SetPoint(selfPoint, anchorRegion, anchorPoint, anchorXOffset, anchorYOffset) end end local function setDesaturated(self, desaturated, ...) self.isDesaturated = desaturated and 1 or 0 return self._SetDesaturated(self, desaturated, ...) end local function setTexture(self, ...) local apply = self._SetTexture(self, ...) if self.isDesaturated ~= nil then self:_SetDesaturated(self.isDesaturated) end return apply end local function cooldown_onUpdate(self, e) if self._duration then if self._delay then if not self._paused then self._delay = self._delay - e end self:_SetCooldown(GetTime(), self._duration) if self._delay <= 0 then self._delay = nil if not self._paused then self:SetScript("OnUpdate", nil) end end return end if self._paused then self:_SetCooldown(GetTime() - self._paused, self._duration) end end end local function cooldown_pause(self) if not self._paused then self._paused = GetTime() - (self._start or 0) self:SetScript("OnUpdate", cooldown_onUpdate) end end local function cooldown_resume(self) if self._paused then self._start = GetTime() - self._paused end self._paused = nil self:SetScript("OnUpdate", nil) end local function cooldown_setCooldown(self, start, duration, ...) if self._start == start and self._duration == duration then return end self._start = start self._duration = duration local getTime = GetTime() local delay = start - getTime if delay > 0 then self._delay = delay self:SetScript("OnUpdate", cooldown_onUpdate) end if self._paused then self._paused = getTime - self._start end return self._SetCooldown(self, math.min(getTime, start), duration, ...) end local function create(parent, data) local font = "GameFontHighlight"; local region = CreateFrame("Frame", nil, parent); region.regionType = "icon" region:SetMovable(true); region:SetResizable(true); region:SetMinResize(1, 1); function region.UpdateInnerOuterSize() local width = region.width * math.abs(region.scalex); local height = region.height * math.abs(region.scaley); local iconWidth local iconHeight if MSQ then iconWidth = region.button:GetWidth() iconHeight = region.button:GetHeight() else iconWidth = region:GetWidth() iconHeight = region:GetHeight() end if region.inner then region.inner:SetSize(iconWidth - 0.2 * width, iconHeight - 0.2 * height) end if region.outer then region.outer:SetSize(iconWidth + 0.1 * width, iconHeight + 0.1 * height) end end local button if MSQ then button = CreateFrame("Button", nil, region) button.data = data region.button = button; button:EnableMouse(false); button:Disable(); button:SetAllPoints(); end local icon = region:CreateTexture(nil, "BACKGROUND"); if MSQ then icon:SetAllPoints(button); button:SetScript("OnSizeChanged", region.UpdateInnerOuterSize); else icon:SetAllPoints(region); region:SetScript("OnSizeChanged", region.UpdateInnerOuterSize); end region.icon = icon; icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark"); icon._SetDesaturated = icon.SetDesaturated icon.SetDesaturated = setDesaturated icon._SetTexture = icon.SetTexture icon.SetTexture = setTexture --This section creates a unique frame id for the cooldown frame so that it can be created with a global reference --The reason is so that WeakAuras cooldown frames can interact properly with OmniCC (i.e., put on its ignore list for timer overlays) local id = data.id; local frameId = id:lower():gsub(" ", "_"); if(_G["WeakAurasCooldown"..frameId]) then local baseFrameId = frameId; local num = 2; while(_G["WeakAurasCooldown"..frameId]) do frameId = baseFrameId..num; num = num + 1; end end region.frameId = frameId; local cooldown = CreateFrame("Cooldown", "WeakAurasCooldown"..frameId, region, "CooldownFrameTemplate"); region.cooldown = cooldown; cooldown:SetAllPoints(icon); cooldown._SetCooldown = cooldown.SetCooldown; cooldown.SetCooldown = cooldown_setCooldown; cooldown.Pause = cooldown_pause; cooldown.Resume = cooldown_resume; local SetFrameLevel = region.SetFrameLevel; function region.SetFrameLevel(self, level) SetFrameLevel(region, level); cooldown:SetFrameLevel(level); if button then button:SetFrameLevel(level); end end WeakAuras.regionPrototype.create(region); region.AnchorSubRegion = AnchorSubRegion return region; end local function modify(parent, region, data) -- Legacy members stacks and text2 region.stacks = nil region.text2 = nil WeakAuras.regionPrototype.modify(parent, region, data); local button, icon, cooldown = region.button, region.icon, region.cooldown; region.iconSource = data.iconSource region.displayIcon = data.displayIcon if MSQ then local masqueId = data.id:lower():gsub(" ", "_"); if region.masqueId ~= masqueId then region.masqueId = masqueId region.MSQGroup = MSQ:Group("WeakAuras", region.masqueId, data.uid); region.MSQGroup:SetName(data.id) region.MSQGroup:AddButton(button, {Icon = icon, Cooldown = cooldown}, "WA_Aura", true); button.data = data end end function region:UpdateSize() local width = region.width * math.abs(region.scalex); local height = region.height * math.abs(region.scaley); region:SetWidth(width); region:SetHeight(height); if MSQ then button:SetWidth(width); button:SetHeight(height); button:SetAllPoints(); end region:UpdateTexCoords(); end function region:UpdateTexCoords() local mirror_h = region.scalex < 0; local mirror_v = region.scaley < 0; local texWidth = 1 - 0.5 * region.zoom; local aspectRatio if not region.keepAspectRatio then aspectRatio = 1; else local width = region.width * math.abs(region.scalex); local height = region.height * math.abs(region.scaley); if width == 0 or height == 0 then aspectRatio = 1; else aspectRatio = width / height; end end if region.MSQGroup then region.MSQGroup:RemoveButton(button) region.MSQGroup:AddButton(button, {Icon = icon, Cooldown = cooldown}, "WA_Aura", true) end local ulx, uly, llx, lly, urx, ury, lrx, lry = GetTexCoord(region, texWidth, aspectRatio) if(mirror_h) then if(mirror_v) then icon:SetTexCoord(lrx, lry, urx, ury, llx, lly, ulx, uly) else icon:SetTexCoord(urx, ury, lrx, lry, ulx, uly, llx, lly) end else if(mirror_v) then icon:SetTexCoord(llx, lly, ulx, uly, lrx, lry, urx, ury) else icon:SetTexCoord(ulx, uly, llx, lly, urx, ury, lrx, lry) end end end region.width = data.width; region.height = data.height; region.scalex = 1; region.scaley = 1; region.keepAspectRatio = data.keepAspectRatio; region.zoom = data.zoom; region:UpdateSize() icon:SetDesaturated(data.desaturate); local tooltipType = Private.CanHaveTooltip(data); if(tooltipType and data.useTooltip) then if not region.tooltipFrame then region.tooltipFrame = CreateFrame("Frame", nil, region); region.tooltipFrame:SetAllPoints(region); region.tooltipFrame:SetScript("OnEnter", function() Private.ShowMouseoverTooltip(region, region); end); region.tooltipFrame:SetScript("OnLeave", Private.HideTooltip); end region.tooltipFrame:EnableMouse(true); elseif region.tooltipFrame then region.tooltipFrame:EnableMouse(false); end cooldown:SetReverse(not data.inverse); function region:Color(r, g, b, a) region.color_r = r; region.color_g = g; region.color_b = b; region.color_a = a; if (r or g or b) then a = a or 1; end icon:SetVertexColor(region.color_anim_r or r, region.color_anim_g or g, region.color_anim_b or b, region.color_anim_a or a); if region.button then region.button:SetAlpha(region.color_anim_a or a or 1); end end function region:ColorAnim(r, g, b, a) region.color_anim_r = r; region.color_anim_g = g; region.color_anim_b = b; region.color_anim_a = a; if (r or g or b) then a = a or 1; end icon:SetVertexColor(r or region.color_r, g or region.color_g, b or region.color_b, a or region.color_a); if MSQ then region.button:SetAlpha(a or region.color_a or 1); end end function region:GetColor() return region.color_r or data.color[1], region.color_g or data.color[2], region.color_b or data.color[3], region.color_a or data.color[4]; end region:Color(data.color[1], data.color[2], data.color[3], data.color[4]); function region:SetIcon(iconPath) if self.displayIcon == iconPath then return end self.displayIcon = iconPath self:UpdateIcon() end function region:SetIconSource(source) if self.iconSource == source then return end self.iconSource = source self:UpdateIcon() end function region:UpdateIcon() local iconPath if self.iconSource == -1 then iconPath = self.state.icon elseif self.iconSource == 0 then iconPath = self.displayIcon else local triggernumber = self.iconSource if triggernumber and self.states[triggernumber] then iconPath = self.states[triggernumber].icon end end iconPath = iconPath or self.displayIcon or "Interface\\Icons\\INV_Misc_QuestionMark" icon:SetTexture(iconPath) end function region:Scale(scalex, scaley) if region.scalex == scalex and region.scaley == scaley then return end region.scalex = scalex; region.scaley = scaley; region:UpdateSize(); end function region:SetDesaturated(b) icon:SetDesaturated(b); end function region:SetRegionWidth(width) region.width = width region:UpdateSize(); end function region:SetRegionHeight(height) region.height = height region:UpdateSize(); end function region:SetInverse(inverse) cooldown:SetReverse(not inverse); if (cooldown.expirationTime and cooldown.duration and cooldown:IsShown()) then -- WORKAROUND SetReverse not applying until next frame cooldown:SetCooldown(0, 0); cooldown:SetCooldown(cooldown.expirationTime - cooldown.duration, cooldown.duration); end end function region:SetCooldownEdge(cooldownEdge) region.cooldownEdge = cooldownEdge; cooldown:SetDrawEdge(cooldownEdge); end region:SetCooldownEdge(data.cooldownEdge) function region:SetZoom(zoom) region.zoom = zoom; region:UpdateTexCoords(); end cooldown.expirationTime = nil; cooldown.duration = nil; cooldown:Hide() if(data.cooldown) then function region:SetValue(value, total) cooldown.value = value cooldown.total = total if (value >= 0 and value <= total) then cooldown:Show() cooldown:SetCooldown(GetTime() - (total - value), total) else cooldown:Hide(); end end function region:SetTime(duration, expirationTime) if (duration > 0 and expirationTime > GetTime()) then cooldown:Show(); cooldown.expirationTime = expirationTime; cooldown.duration = duration; cooldown:SetCooldown(expirationTime - duration, duration); else cooldown.expirationTime = expirationTime; cooldown.duration = duration; cooldown:Hide(); end end function region:PreShow() if (cooldown.duration and cooldown.duration > 0.01) then cooldown:Show(); cooldown:SetCooldown(cooldown.expirationTime - cooldown.duration, cooldown.duration); cooldown:Resume() end end function region:Update() local state = region.state if state.progressType == "timed" then local expirationTime if state.paused == true then if not region.paused then region:Pause() end cooldown:Pause() expirationTime = GetTime() + (state.remaining or 0) else if region.paused then region:Resume() end cooldown:Resume() expirationTime = state.expirationTime and state.expirationTime > 0 and state.expirationTime or math.huge; end local duration = state.duration or 0 if region.adjustedMinRelPercent then region.adjustedMinRel = region.adjustedMinRelPercent * duration end local adjustMin = region.adjustedMin or region.adjustedMinRel or 0; local max if duration == 0 then max = 0 elseif region.adjustedMax then max = region.adjustedMax elseif region.adjustedMaxRelPercent then region.adjustedMaxRel = region.adjustedMaxRelPercent * duration max = region.adjustedMaxRel else max = duration end region:SetTime(max - adjustMin, expirationTime - adjustMin, state.inverse); elseif state.progressType == "static" then local value = state.value or 0; local total = state.total or 0; if region.adjustedMinRelPercent then region.adjustedMinRel = region.adjustedMinRelPercent * total end local adjustMin = region.adjustedMin or region.adjustedMinRel or 0; local max = region.adjustedMax or region.adjustedMaxRel or total; region:SetValue(value - adjustMin, max - adjustMin); cooldown:Pause() else region:SetTime(0, math.huge) end region:UpdateIcon() end else region.SetValue = nil region.SetTime = nil function region:Update() local state = region.state region:UpdateIcon() end end -- Backwards compability function function region:SetGlow(glow) for index, subRegion in ipairs(self.subRegions) do if subRegion.type == "subglow" then subRegion:SetVisible(glow) end end end WeakAuras.regionPrototype.modifyFinish(parent, region, data); --- WORKAROUND -- This fixes a issue with barmodels not appearing on icons if the -- icon is shown delayed region:SetWidth(region:GetWidth()) region:SetHeight(region:GetHeight()) end local function validate(data) Private.EnforceSubregionExists(data, "subbackground") end WeakAuras.RegisterRegionType("icon", create, modify, default, GetProperties, validate)