--[[ ############################################################################## _____/\\\\\\\\\\\____/\\\________/\\\__/\\\________/\\\__/\\\\\\\\\\\_ # ___/\\\/////////\\\_\/\\\_______\/\\\_\/\\\_______\/\\\_\/////\\\///__ # __\//\\\______\///__\//\\\______/\\\__\/\\\_______\/\\\_____\/\\\_____ # ___\////\\\__________\//\\\____/\\\___\/\\\_______\/\\\_____\/\\\_____ # ______\////\\\________\//\\\__/\\\____\/\\\_______\/\\\_____\/\\\_____ # _________\////\\\______\//\\\/\\\_____\/\\\_______\/\\\_____\/\\\_____ # __/\\\______\//\\\______\//\\\\\______\//\\\______/\\\______\/\\\_____ # _\///\\\\\\\\\\\/________\//\\\________\///\\\\\\\\\/____/\\\\\\\\\\\_# ___\///////////___________\///___________\/////////_____\///////////_# ############################################################################## 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 ipairs = _G.ipairs; local type = _G.type; local error = _G.error; local pcall = _G.pcall; local tostring = _G.tostring; local tonumber = _G.tonumber; local tinsert = _G.tinsert; local string = _G.string; local math = _G.math; local table = _G.table; --[[ STRING METHODS ]]-- local format = string.format; --[[ MATH METHODS ]]-- local abs, ceil, floor, round = math.abs, math.ceil, math.floor, math.round; --[[ TABLE METHODS ]]-- local tremove, tsort = table.remove, table.sort; --[[ ########################################################## GET ADDON DATA ########################################################## ]]-- local SuperVillain, L = unpack(select(2, ...)); local MOD = SuperVillain.Registry:Expose("SVUnit"); local _, ns = ... local oUF_SuperVillain = ns.oUF --[[ MUNGLUNCH"s FASTER ASSERT FUNCTION ]]-- local assert = enforce; assert(oUF_SuperVillain, "SVUI was unable to locate oUF."); local LSM = LibStub("LibSharedMedia-3.0"); local AURA_FONT = [[Interface\AddOns\SVUI\assets\fonts\Display.ttf]]; local AURA_FONTSIZE = 11; local AURA_OUTLINE = "OUTLINE"; local shadowTex = [[Interface\AddOns\SVUI\assets\artwork\Template\GLOW]]; local counterOffsets = { ["TOPLEFT"] = {6, 1}, ["TOPRIGHT"] = {-6, 1}, ["BOTTOMLEFT"] = {6, 1}, ["BOTTOMRIGHT"] = {-6, 1}, ["LEFT"] = {6, 1}, ["RIGHT"] = {-6, 1}, ["TOP"] = {0, 0}, ["BOTTOM"] = {0, 0}, } local textCounterOffsets = { ["TOPLEFT"] = {"LEFT", "RIGHT", -2, 0}, ["TOPRIGHT"] = {"RIGHT", "LEFT", 2, 0}, ["BOTTOMLEFT"] = {"LEFT", "RIGHT", -2, 0}, ["BOTTOMRIGHT"] = {"RIGHT", "LEFT", 2, 0}, ["LEFT"] = {"LEFT", "RIGHT", -2, 0}, ["RIGHT"] = {"RIGHT", "LEFT", 2, 0}, ["TOP"] = {"RIGHT", "LEFT", 2, 0}, ["BOTTOM"] = {"RIGHT", "LEFT", 2, 0}, } --[[ ########################################################## LOCAL FUNCTIONS ########################################################## ]]-- local function SetAuraTemplate(aura) if aura.styled then return end if aura.SetNormalTexture then aura:SetNormalTexture("") end if aura.SetHighlightTexture then aura:SetHighlightTexture("") end if aura.SetPushedTexture then aura:SetPushedTexture("") end if aura.SetDisabledTexture then aura:SetDisabledTexture("") end aura:SetBackdrop({ bgFile = [[Interface\BUTTONS\WHITE8X8]], tile = false, tileSize = 0, edgeFile = [[Interface\BUTTONS\WHITE8X8]], edgeSize = 2, insets = { left = 0, right = 0, top = 0, bottom = 0 } }) aura:SetBackdropColor(0, 0, 0, 0) aura:SetBackdropBorderColor(0, 0, 0) aura:RegisterForClicks("RightButtonUp") aura:SetScript("OnClick", function(self) if not IsShiftKeyDown()then return end local name = self.name; if name then SuperVillain:AddonMessage(format(L['The spell "%s" has been added to the Blocked unitframe aura filter.'], name)) SuperVillain.Filters["Blocked"]["spells"][name] = {["enable"] = true, ["priority"] = 0} MOD:RefreshUnitFrames() end end) aura.styled = true end --[[ ########################################################## BUILD FUNCTION ########################################################## ]]-- function MOD:CreateBuffs(frame) local aura = CreateFrame("Frame", nil, frame) aura.spacing = 2; aura.PostCreateIcon = self.CreateAuraIcon; aura.PostUpdateIcon = self.PostUpdateAura; aura.CustomFilter = self.AuraFilter; aura:SetFrameLevel(10) aura.type = "buffs" self:WatchUnitMedia("aurafonts", aura) return aura end function MOD:CreateDebuffs(frame) local aura = CreateFrame("Frame", nil, frame) aura.spacing = 2; aura.PostCreateIcon = self.CreateAuraIcon; aura.PostUpdateIcon = self.PostUpdateAura; aura.CustomFilter = self.AuraFilter; aura.type = "debuffs" aura:SetFrameLevel(10) self:WatchUnitMedia("aurafonts", aura) return aura end function MOD:CreateAuraWatch(frame) local aWatch = CreateFrame("Frame", nil, frame) aWatch:SetFrameLevel(frame:GetFrameLevel() + 25) aWatch:FillInner(frame.Health) aWatch.presentAlpha = 1; aWatch.missingAlpha = 0; aWatch.strictMatching = true; aWatch.icons = {} return aWatch end function MOD:UpdateAuraTimer(elapsed) self.expiration = self.expiration - elapsed; if(self.nextUpdate > 0) then self.nextUpdate = self.nextUpdate - elapsed; return end if(self.expiration <= 0) then self:SetScript("OnUpdate", nil) if(self.text:GetFont()) then self.text:SetText("") end return end local expires = self.expiration; local calc, timeLeft = 0, 0; local timeFormat; if expires < 60 then if expires >= 4 then timeLeft = floor(expires) timeFormat = "|cffffff00%d|r" self.nextUpdate = 0.51 else timeLeft = expires timeFormat = "|cffff0000%.1f|r" self.nextUpdate = 0.051 end elseif expires < 3600 then timeFormat = "|cffffffff%dm|r" timeLeft = ceil(expires / 60); calc = floor((expires / 60) + .5); self.nextUpdate = calc > 1 and ((expires - calc) * 29.5) or (expires - 59.5); elseif expires < 86400 then timeFormat = "|cff66ffff%dh|r" timeLeft = ceil(expires / 3600); calc = floor((expires / 3600) + .5); self.nextUpdate = calc > 1 and ((expires - calc) * 1799.5) or (expires - 3570); else timeFormat = "|cff6666ff%dd|r" timeLeft = ceil(expires / 86400); calc = floor((expires / 86400) + .5); self.nextUpdate = calc > 1 and ((expires - calc) * 43199.5) or (expires - 86400); end if self.text:GetFont() then self.text:SetFormattedText(timeFormat, timeLeft) else self.text:SetFont(AURA_FONT, AURA_FONTSIZE, AURA_OUTLINE) self.text:SetFormattedText(timeFormat, timeLeft) end end function MOD:CreateAuraIcon(aura, index, offset) aura.cd.noOCC = true; aura.cd.noCooldownCount = true; aura.cd:SetReverse() aura.cd:FillInner(aura, 0, 0) aura.text = aura.cd:CreateFontString(nil, "OVERLAY") aura.text:SetFont(AURA_FONT, AURA_FONTSIZE, AURA_OUTLINE) aura.text:SetPoint("CENTER", 1, 1) aura.text:SetJustifyH("CENTER") aura.icon:FillInner(aura, 2, 2) aura.icon:SetTexCoord(0.1, 0.9, 0.1, 0.9) aura.icon:SetDrawLayer("ARTWORK") aura.count:ClearAllPoints() aura.count:SetFont(AURA_FONT, AURA_FONTSIZE, AURA_OUTLINE) aura.count:SetPoint("BOTTOMRIGHT", 1, 1) aura.count:SetJustifyH("RIGHT") aura.overlay:SetTexture(nil) aura.stealable:SetTexture(nil) SetAuraTemplate(aura) end function MOD:PostUpdateAura(unit, button, index, offset, filter, isDebuff, duration, timeLeft) local name, _, _, _, dtype, duration, expiration, _, isStealable = UnitAura(unit, index, button.filter) local isFriend = UnitIsFriend('player', unit) == 1 and true or false if button.isDebuff then if(not isFriend and button.owner ~= "player" and button.owner ~= "vehicle") then button:SetBackdropBorderColor(0.9, 0.1, 0.1) button.icon:SetDesaturated((unit and not unit:find('arena%d')) and true or false) else local color = DebuffTypeColor[dtype] or DebuffTypeColor.none if (name == "Unstable Affliction" or name == "Vampiric Touch") and SuperVillain.class ~= "WARLOCK" then button:SetBackdropBorderColor(0.05, 0.85, 0.94) else button:SetBackdropBorderColor(color.r * 0.6, color.g * 0.6, color.b * 0.6) end button.icon:SetDesaturated(false) end else if (isStealable) and not isFriend then button:SetBackdropBorderColor(237/255, 234/255, 142/255) else button:SetBackdropBorderColor(0,0,0,1) end end local size = button:GetParent().size if size then button:Size(size) end button.spell = name button.isStealable = isStealable if expiration and duration ~= 0 then if not button:GetScript('OnUpdate') then button.expirationTime = expiration button.expiration = expiration - GetTime() button.nextUpdate = -1 button:SetScript('OnUpdate', MOD.UpdateAuraTimer) end if button.expirationTime ~= expiration then button.expirationTime = expiration button.expiration = expiration - GetTime() button.nextUpdate = -1 end end if duration == 0 or expiration == 0 then button:SetScript('OnUpdate', nil) if(button.text:GetFont()) then button.text:SetText('') end end end function MOD:UpdateAuraWatchFromHeader(group, override) assert(self[group],"Invalid group specified.") for i=1,self[group]:GetNumChildren() do local frame = select(i, self[group]:GetChildren()) if frame and frame.Health then MOD:UpdateAuraWatch(frame,override) end end end local function CheckAuraFilter(setting,helpful) local friend, enemy = false, false if type(setting)=='boolean' then friend = setting; enemy = setting elseif setting and type(setting)~='string' then friend = setting.friendly; enemy = setting.enemy end if (friend and helpful) or (enemy and not helpful) then return true; end return false end function MOD:AuraFilter(unit, icon, name, _, _, _, dtype, duration, _, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossAura) if spellID == 65148 then return false end local isPlayer,friendly; local db = self:GetParent().db; local auraType = self.type; if not db or not auraType or not db[auraType] then return true end; local filtered=true; local allowed=true; local pass=false; local isPlayer=caster=='player'or caster=='vehicle' local friendly=UnitIsFriend('player',unit)==1 and true or false; local filterType=friendly and db.friendlyAuraType or db.enemyAuraType; icon.isPlayer=isPlayer; icon.owner=caster; icon.name=name; icon.priority=0; local shieldSpell = SuperVillain.Filters['Shield'][name] if shieldSpell and shieldSpell.enable then icon.priority=shieldSpell.priority end if CheckAuraFilter(db.filterPlayer,friendly)then if isPlayer then filtered=true else filtered=false end allowed=filtered; pass=true end if CheckAuraFilter(db.filterDispellable,friendly) then if (auraType=='buffs' and not isStealable) or (auraType=='debuffs' and dtype and not SuperVillain:DispellAvailable(dtype)) or dtype==nil then filtered=false end pass=true end if CheckAuraFilter(db.filterRaid,friendly) then if shouldConsolidate==1 then filtered=false end pass=true end if CheckAuraFilter(db.filterInfinite,friendly)then if duration==0 or not duration then filtered=false end pass=true end if CheckAuraFilter(db.useBlocked,friendly) then local blackListSpell=SuperVillain.Filters['Blocked'][name] if blackListSpell and blackListSpell.enable then filtered=false end pass=true end if CheckAuraFilter(db.useAllowed,friendly) then local whiteListSpell=SuperVillain.Filters['Allowed'][name] if whiteListSpell and whiteListSpell.enable then filtered=true; icon.priority=whiteListSpell.priority elseif not pass then filtered=false end pass=true end if db.useFilter and SuperVillain.Filters[db.useFilter] then local spellDB=SuperVillain.Filters[db.useFilter]; if db.useFilter ~= 'Blocked' then if spellDB[name] and spellDB[name].enable and allowed then filtered=true; icon.priority=spellDB[name].priority; if db.useFilter == 'Shield' and (spellID==86698 or spellID==86669) then filtered=false end elseif not pass then filtered=false end elseif spellDB[name] and spellDB[name].enable then filtered=false end end return filtered end --[[ ########################################################## UPDATE ########################################################## ]]-- function MOD:UpdateAuraUpvalues() AURA_FONT = LSM:Fetch("font",MOD.db.auraFont); AURA_FONTSIZE = MOD.db.auraFontSize; AURA_OUTLINE = MOD.db.auraFontOutline; end function MOD:UpdateAuraIconSettings(aura, useParent) if(useParent) then aura.text:SetFont(AURA_FONT, AURA_FONTSIZE, AURA_OUTLINE) aura.count:SetFont(AURA_FONT, AURA_FONTSIZE, AURA_OUTLINE) -- if aura:IsMouseEnabled() then -- aura:EnableMouse(false) -- end else local i = 1; while aura[i] do local icon = aura[i] icon.text:SetFont(AURA_FONT, AURA_FONTSIZE, AURA_OUTLINE) icon.count:SetFont(AURA_FONT, AURA_FONTSIZE, AURA_OUTLINE) -- if icon:IsMouseEnabled() then -- icon:EnableMouse(false) -- end i = i + 1 end end end function MOD:UpdateAuraWatch(frame,override) local temp = {} local AW = frame.AuraWatch; local db = frame.db.buffIndicator; if not db then return end if not db.enable then AW:Hide() return else AW:Show() end local bwSize = db.size; if frame.unit=='pet' and not override then local petBW = SuperVillain.Filters["PetBuffWatch"] or {} for _,buff in pairs(petBW)do if buff.style=='text'then buff.style='NONE' end tinsert(temp,buff) end else local unitBW = SuperVillain.Filters["BuffWatch"] or {} for _,buff in pairs(unitBW)do if buff.style=='text'then buff.style='NONE' end tinsert(temp,buff) end end if AW.icons then for i=1,#AW.icons do local iconTest=false; for j=1,#temp do if #temp[j].id and #temp[j].id==AW.icons[i] then iconTest=true; break end end if not iconTest then AW.icons[i]:Hide() AW.icons[i]=nil end end end for i=1,#temp do if temp[i].id then local buffName,_,buffTexture = GetSpellInfo(temp[i].id) if buffName then local watchedAura; if not AW.icons[temp[i].id]then watchedAura=CreateFrame("Frame",nil,AW) else watchedAura=AW.icons[temp[i].id] end watchedAura.name = buffName; watchedAura.image = buffTexture; watchedAura.spellID = temp[i].id; watchedAura.anyUnit = temp[i].anyUnit; watchedAura.style = temp[i].style; watchedAura.onlyShowMissing = temp[i].onlyShowMissing; watchedAura.presentAlpha = watchedAura.onlyShowMissing and 0 or 1; watchedAura.missingAlpha = watchedAura.onlyShowMissing and 1 or 0; watchedAura.textThreshold = temp[i].textThreshold or -1; watchedAura.displayText = temp[i].displayText; watchedAura:Width(bwSize) watchedAura:Height(bwSize) watchedAura:ClearAllPoints() watchedAura:SetPoint(temp[i].point, frame.Health, temp[i].point, temp[i].xOffset, temp[i].yOffset) if not watchedAura.icon then watchedAura.icon = watchedAura:CreateTexture(nil,"BORDER") watchedAura.icon:SetAllPoints(watchedAura) end if not watchedAura.text then local awText = CreateFrame('Frame',nil,watchedAura) awText:SetFrameLevel(watchedAura:GetFrameLevel()+50) watchedAura.text = awText:CreateFontString(nil,'BORDER') end if not watchedAura.border then watchedAura.border = watchedAura:CreateTexture(nil,"BACKGROUND") watchedAura.border:Point("TOPLEFT",-1,1) watchedAura.border:Point("BOTTOMRIGHT",1,-1) watchedAura.border:SetTexture([[Interface\BUTTONS\WHITE8X8]]) watchedAura.border:SetVertexColor(0,0,0) end if not watchedAura.cd then watchedAura.cd = CreateFrame("Cooldown",nil,watchedAura) watchedAura.cd:SetAllPoints(watchedAura) watchedAura.cd:SetReverse(true) watchedAura.cd:SetFrameLevel(watchedAura:GetFrameLevel()) end if watchedAura.style=='coloredIcon'then watchedAura.icon:SetTexture([[Interface\BUTTONS\WHITE8X8]]) if temp[i]["color"]then watchedAura.icon:SetVertexColor(temp[i]["color"].r, temp[i]["color"].g, temp[i]["color"].b) else watchedAura.icon:SetVertexColor(0.8,0.8,0.8) end watchedAura.icon:Show() watchedAura.border:Show() watchedAura.cd:SetAlpha(1) elseif watchedAura.style=='texturedIcon' then watchedAura.icon:SetVertexColor(1,1,1) watchedAura.icon:SetTexCoord(.18,.82,.18,.82) watchedAura.icon:SetTexture(watchedAura.image) watchedAura.icon:Show() watchedAura.border:Show() watchedAura.cd:SetAlpha(1) else watchedAura.border:Hide() watchedAura.icon:Hide() watchedAura.cd:SetAlpha(0) end if watchedAura.displayText then watchedAura.text:Show() local r,g,b=1,1,1; if temp[i].textColor then r,g,b = temp[i].textColor.r, temp[i].textColor.g, temp[i].textColor.b end watchedAura.text:SetTextColor(r,g,b) else watchedAura.text:Hide() end if not watchedAura.count then watchedAura.count=watchedAura:CreateFontString(nil,"OVERLAY") end watchedAura.count:ClearAllPoints() if watchedAura.displayText then local anchor,relative,x,y=unpack(textCounterOffsets[temp[i].point]) watchedAura.count:SetPoint(anchor, watchedAura.text, relative, x, y) else watchedAura.count:SetPoint("CENTER",unpack(counterOffsets[temp[i].point])) end watchedAura.count:SetFont(AURA_FONT, AURA_FONTSIZE, AURA_OUTLINE) watchedAura.text:SetFont(AURA_FONT, AURA_FONTSIZE, AURA_OUTLINE) watchedAura.text:ClearAllPoints() watchedAura.text:SetPoint(temp[i].point,watchedAura,temp[i].point) if temp[i].enable then AW.icons[temp[i].id]=watchedAura; if AW.watched then AW.watched[temp[i].id]=watchedAura end else AW.icons[temp[i].id]=nil; if AW.watched then AW.watched[temp[i].id]=nil end watchedAura:Hide() watchedAura=nil end end end end if frame.AuraWatch.Update then frame.AuraWatch.Update(frame) end temp=nil end --[[ ########################################################## UTILITY ########################################################## ]]-- function MOD:SmartAuraDisplay() local db = self.db; local unit = self.unit; if not db or not db.smartAuraDisplay or db.smartAuraDisplay == 'DISABLED' or not UnitExists(unit) then return end local buffs = self.Buffs; local debuffs = self.Debuffs; local bars = self.AuraBars; local friendly = UnitIsFriend('player',unit) == 1 and true or false; if friendly then if db.smartAuraDisplay == 'SHOW_DEBUFFS_ON_FRIENDLIES' then buffs:Hide() debuffs:Show() else buffs:Show() debuffs:Hide() end else if db.smartAuraDisplay == 'SHOW_DEBUFFS_ON_FRIENDLIES' then buffs:Show() debuffs:Hide() else buffs:Hide() debuffs:Show() end end if buffs:IsShown() then buffs:ClearAllPoints() SuperVillain:ReversePoint(buffs, db.buffs.anchorPoint, self, db.buffs.xOffset, db.buffs.yOffset) if db.aurabar.attachTo ~= 'FRAME' then bars:ClearAllPoints() bars:SetPoint('BOTTOMLEFT', buffs, 'TOPLEFT', 0, 1) bars:SetPoint('BOTTOMRIGHT', buffs, 'TOPRIGHT', 0, 1) end end if debuffs:IsShown() then debuffs:ClearAllPoints() SuperVillain:ReversePoint(debuffs, db.debuffs.anchorPoint, self, db.debuffs.xOffset, db.debuffs.yOffset) if db.aurabar.attachTo ~= 'FRAME' then bars:ClearAllPoints() bars:SetPoint('BOTTOMLEFT', debuffs, 'TOPLEFT', 0, 1) bars:SetPoint('BOTTOMRIGHT', debuffs, 'TOPRIGHT', 0, 1) end end end