Compare with Previous | Blame | View Log
--[[ -- Experimental oUF tags -- Status: Incomplete -- -- Credits: Vika, Cladhaire, Tekkub -- -- TODO: -- - Tag and Untag should be able to handle more than one fontstring at a time. ]] local parent = debugstack():match[[\AddOns\(.-)\]] local global = GetAddOnMetadata(parent, 'X-oUF') assert(global, 'X-oUF needs to be defined in the parent add-on.') local oUF = _G[global] local function Hex(r, g, b) if type(r) == "table" then if r.r then r, g, b = r.r, r.g, r.b else r, g, b = unpack(r) end end return string.format("|cff%02x%02x%02x", r*255, g*255, b*255) end local tags tags = { ["[class]"] = function(u) return UnitClass(u) end, ["[creature]"] = function(u) return UnitCreatureFamily(u) or UnitCreatureType(u) end, ["[curhp]"] = UnitHealth, ["[curpp]"] = UnitPower, ["[dead]"] = function(u) return UnitIsDead(u) and "Dead" or UnitIsGhost(u) and "Ghost" end, ["[difficulty]"] = function(u) if UnitCanAttack("player", u) then local l = UnitLevel(u); return Hex(GetDifficultyColor((l > 0) and l or 99)) end end, ["[faction]"] = function(u) return UnitFactionGroup(u) end, ["[leader]"] = function(u) return UnitIsPartyLeader(u) and "(L)" end, ["[leaderlong]"] = function(u) return UnitIsPartyLeader(u) and "(Leader)" end, ["[level]"] = function(u) local l = UnitLevel(u) return (l > 0) and l or "??" end, ["[maxhp]"] = UnitHealthMax, ["[maxpp]"] = UnitPowerMax, ["[missinghp]"] = function(u) return UnitHealthMax(u) - UnitHealth(u) end, ["[missingpp]"] = function(u) return UnitPowerMax(u) - UnitPower(u) end, ["[name]"] = function(u, r) return UnitName(r or u) end, ["[offline]"] = function(u) return (not UnitIsConnected(u) and "Offline") end, ["[perhp]"] = function(u) local m = UnitHealthMax(u); return m == 0 and 0 or math.floor(UnitHealth(u)/m*100+0.5) end, ["[perpp]"] = function(u) local m = UnitPowerMax(u); return m == 0 and 0 or math.floor(UnitPower(u)/m*100+0.5) end, ["[plus]"] = function(u) local c = UnitClassification(u); return (c == "elite" or c == "rareelite") and "+" end, ["[pvp]"] = function(u) return UnitIsPVP(u) and "PvP" end, ["[race]"] = function(u) return UnitRace(u) end, ["[raidcolor]"] = function(u) local _, x = UnitClass(u); return x and Hex(RAID_CLASS_COLORS[x]) end, ["[rare]"] = function(u) local c = UnitClassification(u); return (c == "rare" or c == "rareelite") and "Rare" end, ["[resting]"] = function(u) return u == "player" and IsResting() and "zzz" end, ["[sex]"] = function(u) local s = UnitSex(u) return s == 2 and "Male" or s == 3 and "Female" end, ["[smartclass]"] = function(u) return UnitIsPlayer(u) and tags["[class]"](u) or tags["[creature]"](u) end, ["[status]"] = function(u) return UnitIsDead(u) and "Dead" or UnitIsGhost(u) and "Ghost" or not UnitIsConnected(u) and "Offline" or tags["[resting]"](u) end, ["[threat]"] = function(u) local s = UnitThreatSituation(u) return s == 1 and "++" or s == 2 and "--" or s == 3 and "Aggro" end, ["[threatcolor]"] = function(u) return Hex(GetThreatStatusColor(UnitThreatSituation(u))) end, ["[cpoints]"] = function(u) local cp = GetComboPoints(u, 'target') return (cp > 0) and cp end, ['[smartlevel]'] = function(u) local c = UnitClassification(u) if(c == 'worldboss') then return 'Boss' else local plus = tags['[plus]'](u) local level = tags['[level]'](u) if(plus) then return level .. plus else return level end end end, ["[classification]"] = function(u) local c = UnitClassification(u) return c == "rare" and "Rare" or c == "eliterare" and "Rare Elite" or c == "elite" and "Elite" or c == "worldboss" and "Boss" end, ["[shortclassification]"] = function(u) local c = UnitClassification(u) return c == "rare" and "R" or c == "eliterare" and "R+" or c == "elite" and "+" or c == "worldboss" and "B" end, } local tagEvents = { ["[curhp]"] = "UNIT_HEALTH", ["[curpp]"] = "UNIT_ENERGY UNIT_FOCUS UNIT_MANA UNIT_RAGE UNIT_RUNIC_POWER", ["[dead]"] = "UNIT_HEALTH", ["[leader]"] = "PARTY_LEADER_CHANGED", ["[leaderlong]"] = "PARTY_LEADER_CHANGED", ["[level]"] = "UNIT_LEVEL PLAYER_LEVEL_UP", ["[maxhp]"] = "UNIT_MAXHEALTH", ["[maxpp]"] = "UNIT_MAXENERGY UNIT_MAXFOCUS UNIT_MAXMANA UNIT_MAXRAGE UNIT_MAXRUNIC_POWER", ["[missinghp]"] = "UNIT_HEALTH UNIT_MAXHEALTH", ["[missingpp]"] = "UNIT_MAXENERGY UNIT_MAXFOCUS UNIT_MAXMANA UNIT_MAXRAGE UNIT_ENERGY UNIT_FOCUS UNIT_MANA UNIT_RAGE UNIT_MAXRUNIC_POWER UNIT_RUNIC_POWER", ["[name]"] = "UNIT_NAME_UPDATE", ["[offline]"] = "UNIT_HEALTH", ["[perhp]"] = "UNIT_HEALTH UNIT_MAXHEALTH", ["[perpp]"] = "UNIT_MAXENERGY UNIT_MAXFOCUS UNIT_MAXMANA UNIT_MAXRAGE UNIT_ENERGY UNIT_FOCUS UNIT_MANA UNIT_RAGE UNIT_MAXRUNIC_POWER UNIT_RUNIC_POWER", ["[pvp]"] = "UNIT_FACTION", ["[resting]"] = "PLAYER_UPDATE_RESTING", ["[status]"] = "UNIT_HEALTH PLAYER_UPDATE_RESTING", ["[smartlevel]"] = "UNIT_LEVEL PLAYER_LEVEL_UP UNIT_CLASSIFICATION_CHANGED", ["[threat]"] = "UNIT_THREAT_SITUATION_UPDATE", ["[threatcolor]"] = "UNIT_THREAT_SITUATION_UPDATE", ['[cpoints]'] = 'UNIT_COMBO_POINTS UNIT_TARGET', ['[rare]'] = 'UNIT_CLASSIFICATION_CHANGED', ['[classification]'] = 'UNIT_CLASSIFICATION_CHANGED', ['[shortclassification]'] = 'UNIT_CLASSIFICATION_CHANGED', } local unitlessEvents = { PLAYER_TARGET_CHANGED = true, PLAYER_FOCUS_CHANGED = true, PLAYER_LEVEL_UP = true, } local events = {} local frame = CreateFrame"Frame" frame:SetScript('OnEvent', function(self, event, unit) local strings = events[event] if(strings) then for k, fontstring in next, strings do if(not unitlessEvents[event] and fontstring.parent.unit == unit and fontstring:IsVisible()) then fontstring:UpdateTag() end end end end) local eventlessUnits = {} local timer = .5 local lowestTimer = .5 local OnUpdate = function(self, elapsed) if(timer >= lowestTimer) then for k, fs in next, eventlessUnits do if(fs.parent:IsShown() and UnitExists(fs.parent.unit)) then fs:UpdateTag() end end timer = 0 end timer = timer + elapsed end local OnShow = function(self) for _, fs in next, self.__tags do fs:UpdateTag() end end local RegisterEvent = function(fontstr, event) if(not events[event]) then events[event] = {} end frame:RegisterEvent(event) table.insert(events[event], fontstr) end local RegisterEvents = function(fontstr, tagstr) -- Forcefully strip away any parentheses and the characters in them. tagstr = tagstr:gsub('%b()', '') for tag in tagstr:gmatch'[%[]%w+[%]]' do local tagevents = tagEvents[tag] if(tagevents) then for event in tagevents:gmatch'%S+' do RegisterEvent(fontstr, event) end end end end local UnregisterEvents = function(fontstr) for events, data in pairs(events) do for k, tagfsstr in pairs(data) do if(tagfsstr == fontstr) then if(#data[k] == 1) then frame:UnregisterEvent(event) end data[k] = nil end end end end local tagPool = {} local funcPool = {} local tmp = {} local Tag = function(self, fs, tagstr) if(not fs or not tagstr or self == oUF) then return end if(not self.__tags) then self.__tags = {} table.insert(self.__elements, OnShow) else -- Since people ignore everything that's good practice - unregister the tag -- if it already exists. for _, tag in pairs(self.__tags) do if(fs == tag) then -- We don't need to remove it from the __tags table as Untag handles -- that for us. self:Untag(fs) end end end fs.parent = self local func = tagPool[tagstr] if(not func) then -- Using .- in the match prevents use from supporting [] as prepend/append -- characters. Supporting these and having a single pattern here is a real -- headache however. local format = tagstr:gsub('%%', '%%%%'):gsub('[[].-[]]', '%%s') local args = {} for bracket in tagstr:gmatch'([[](.-)[]])' do local tfunc = funcPool[bracket] or tags[bracket] if(not tfunc) then -- ... local pre, tag, ap = bracket:match'[%[](%b())([%w]+)(%b())[%]]' if(not pre) then pre, tag = bracket:match'[%[](%b())([%w]+)[%]]' end if(not pre) then tag, ap = bracket:match'[%[]([%w]+)(%b())[%]]' end tag = (tag and '['.. tag ..']') tag = tags[tag] if(tag) then if(pre and ap) then pre = pre:sub(2,-2) ap = ap:sub(2,-2) tfunc = function(u) local str = tag(u) if(str) then return pre..str..ap end end elseif(pre) then pre = pre:sub(2,-2) tfunc = function(u) local str = tag(u) if(str) then return pre..str end end elseif(ap) then ap = ap:sub(2,-2) tfunc = function(u) local str = tag(u) if(str) then return str..ap end end end funcPool[bracket] = tfunc end end if(tfunc) then table.insert(args,tfunc) else return error(('Attempted to use invalid tag %s.'):format(bracket), 3) end end func = function(self) local unit = self.parent.unit local __unit = self.parent.__unit for i, func in next, args do tmp[i] = func(unit, __unit) or '' end self:SetFormattedText(format, unpack(tmp)) end tagPool[tagstr] = func end fs.UpdateTag = func local unit = self.unit if((unit and unit:match'%w+target') or fs.frequentUpdates) then if(type(fs.frequentUpdates) == 'number') then lowestTimer = math.min(fs.frequentUpdates, lowestTimer) end table.insert(eventlessUnits, fs) if(not frame:GetScript'OnUpdate') then frame:SetScript('OnUpdate', OnUpdate) end else RegisterEvents(fs, tagstr) if(unit == 'focus') then RegisterEvent(fs, 'PLAYER_FOCUS_CHANGED') elseif(unit == 'target') then RegisterEvent(fs, 'PLAYER_TARGET_CHANGED') elseif(unit == 'mouseover') then RegisterEvent(fs, 'UPDATE_MOUSEOVER_UNIT') end end table.insert(self.__tags, fs) end local Untag = function(self, fs) if(not fs or self == oUF) then return end UnregisterEvents(fs) for k, fontstr in next, eventlessUnits do if(fs == fontstr) then table.remove(eventlessUnits, k) end end for k, fontstr in next, self.__tags do if(fontstr == fs) then table.remove(self.__tags, k) end end fs.UpdateTag = nil end oUF.Tags = tags oUF.TagEvents = tagEvents oUF.UnitlessTagEvents = unitlessEvents oUF.Tag = Tag oUF.Untag = Untag