PageRenderTime 252ms CodeModel.GetById 120ms app.highlight 18ms RepoModel.GetById 83ms app.codeStats 0ms

/Tukui/modules/unitframes/core/oUF/elements/tags.lua

http://github.com/Asphyxia/Tukui
Lua | 650 lines | 546 code | 94 blank | 10 comment | 118 complexity | ed7cde516f9dec74179ac0ee37aa1ade MD5 | raw file
  1--[[
  2-- Credits: Vika, Cladhaire, Tekkub
  3]]
  4
  5local parent, ns = ...
  6local oUF = ns.oUF
  7
  8local _PATTERN = '%[..-%]+'
  9
 10local _ENV = {
 11	Hex = function(r, g, b)
 12		if type(r) == "table" then
 13			if r.r then r, g, b = r.r, r.g, r.b else r, g, b = unpack(r) end
 14		end
 15		return string.format("|cff%02x%02x%02x", r*255, g*255, b*255)
 16	end,
 17	ColorGradient = oUF.ColorGradient,
 18}
 19local _PROXY = setmetatable(_ENV, {__index = _G})
 20
 21local tagStrings = {
 22	["creature"] = [[function(u)
 23		return UnitCreatureFamily(u) or UnitCreatureType(u)
 24	end]],
 25
 26	["dead"] = [[function(u)
 27		if(UnitIsDead(u)) then
 28			return 'Dead'
 29		elseif(UnitIsGhost(u)) then
 30			return 'Ghost'
 31		end
 32	end]],
 33
 34	["difficulty"] = [[function(u)
 35		if UnitCanAttack("player", u) then
 36			local l = UnitLevel(u)
 37			return Hex(GetQuestDifficultyColor((l > 0) and l or 99))
 38		end
 39	end]],
 40
 41	["leader"] = [[function(u)
 42		if(UnitIsPartyLeader(u)) then
 43			return 'L'
 44		end
 45	end]],
 46
 47	["leaderlong"]  = [[function(u)
 48		if(UnitIsPartyLeader(u)) then
 49			return 'Leader'
 50		end
 51	end]],
 52
 53	["level"] = [[function(u)
 54		local l = UnitLevel(u)
 55		if(l > 0) then
 56			return l
 57		else
 58			return '??'
 59		end
 60	end]],
 61
 62	["missinghp"] = [[function(u)
 63		local current = UnitHealthMax(u) - UnitHealth(u)
 64		if(current > 0) then
 65			return current
 66		end
 67	end]],
 68
 69	["missingpp"] = [[function(u)
 70		local current = UnitPowerMax(u) - UnitPower(u)
 71		if(current > 0) then
 72			return current
 73		end
 74	end]],
 75
 76	["name"] = [[function(u, r)
 77		return UnitName(r or u)
 78	end]],
 79
 80	["offline"] = [[function(u)
 81		if(not UnitIsConnected(u)) then
 82			return 'Offline'
 83		end
 84	end]],
 85
 86	["perhp"] = [[function(u)
 87		local m = UnitHealthMax(u)
 88		if(m == 0) then
 89			return 0
 90		else
 91			return math.floor(UnitHealth(u)/m*100+.5)
 92		end
 93	end]],
 94
 95	["perpp"] = [[function(u)
 96		local m = UnitPowerMax(u)
 97		if(m == 0) then
 98			return 0
 99		else
100			return math.floor(UnitPower(u)/m*100+.5)
101		end
102	end]],
103
104	["plus"] = [[function(u)
105		local c = UnitClassification(u)
106		if(c == 'elite' or c == 'rareelite') then
107			return '+'
108		end
109	end]],
110
111	["pvp"] = [[function(u)
112		if(UnitIsPVP(u)) then
113			return 'PvP'
114		end
115	end]],
116
117	["raidcolor"] = [[function(u)
118		local _, x = UnitClass(u)
119		if(x) then
120			return Hex(_COLORS.class[x])
121		end
122	end]],
123
124	["rare"] = [[function(u)
125		local c = UnitClassification(u)
126		if(c == 'rare' or c == 'rareelite') then
127			return 'Rare'
128		end
129	end]],
130
131	["resting"] = [[function(u)
132		if(u == 'player' and IsResting()) then
133			return 'zzz'
134		end
135	end]],
136
137	["sex"] = [[function(u)
138		local s = UnitSex(u)
139		if(s == 2) then
140			return 'Male'
141		elseif(s == 3) then
142			return 'Female'
143		end
144	end]],
145
146	["smartclass"] = [[function(u)
147		if(UnitIsPlayer(u)) then
148			return _TAGS['class'](u)
149		end
150
151		return _TAGS['creature'](u)
152	end]],
153
154	["status"] = [[function(u)
155		if(UnitIsDead(u)) then
156			return 'Dead'
157		elseif(UnitIsGhost(u)) then
158			return 'Ghost'
159		elseif(not UnitIsConnected(u)) then
160			return 'Offline'
161		else
162			return _TAGS['resting'](u)
163		end
164	end]],
165
166	["threat"] = [[function(u)
167		local s = UnitThreatSituation(u)
168		if(s == 1) then
169			return '++'
170		elseif(s == 2) then
171			return '--'
172		elseif(s == 3) then
173			return 'Aggro'
174		end
175	end]],
176
177	["threatcolor"] = [[function(u)
178		return Hex(GetThreatStatusColor(UnitThreatSituation(u)))
179	end]],
180
181	["cpoints"] = [[function(u)
182		local cp
183		if(UnitHasVehicleUI'player') then
184			cp = GetComboPoints('vehicle', 'target')
185		else
186			cp = GetComboPoints('player', 'target')
187		end
188
189		if(cp > 0) then
190			return cp
191		end
192	end]],
193
194	['smartlevel'] = [[function(u)
195		local c = UnitClassification(u)
196		if(c == 'worldboss') then
197			return 'Boss'
198		else
199			local plus = _TAGS['plus'](u)
200			local level = _TAGS['level'](u)
201			if(plus) then
202				return level .. plus
203			else
204				return level
205			end
206		end
207	end]],
208
209	["classification"] = [[function(u)
210		local c = UnitClassification(u)
211		if(c == 'rare') then
212			return 'Rare'
213		elseif(c == 'eliterare') then
214			return 'Rare Elite'
215		elseif(c == 'elite') then
216			return 'Elite'
217		elseif(c == 'worldboss') then
218			return 'Boss'
219		end
220	end]],
221
222	["shortclassification"] = [[function(u)
223		local c = UnitClassification(u)
224		if(c == 'rare') then
225			return 'R'
226		elseif(c == 'eliterare') then
227			return 'R+'
228		elseif(c == 'elite') then
229			return '+'
230		elseif(c == 'worldboss') then
231			return 'B'
232		end
233	end]],
234
235	["group"] = [[function(unit)
236		local name, server = UnitName(unit)
237		if(server and server ~= "") then
238			name = string.format("%s-%s", name, server)
239		end
240
241		for i=1, GetNumRaidMembers() do
242			local raidName, _, group = GetRaidRosterInfo(i)
243			if( raidName == name ) then
244				return group
245			end
246		end
247	end]],
248
249	["deficit:name"] = [[function(u)
250		local missinghp = _TAGS['missinghp'](u)
251		if(missinghp) then
252			return '-' .. missinghp
253		else
254			return _TAGS['name'](u)
255		end
256	end]],
257
258	['pereclipse'] = [[function(u)
259		local m = UnitPowerMax('player', SPELL_POWER_ECLIPSE)
260		if(m == 0) then
261			return 0
262		else
263			return math.abs(UnitPower('player', SPELL_POWER_ECLIPSE)/m*100)
264		end
265	end]],
266
267	['curmana'] = [[function(unit)
268		return UnitPower(unit, SPELL_POWER_MANA)
269	end]],
270
271	['maxmana'] = [[function(unit)
272		return UnitPowerMax(unit, SPELL_POWER_MANA)
273	end]],
274}
275
276local tags = setmetatable(
277	{
278		curhp = UnitHealth,
279		curpp = UnitPower,
280		maxhp = UnitHealthMax,
281		maxpp = UnitPowerMax,
282		class = UnitClass,
283		faction = UnitFactionGroup,
284		race = UnitRace,
285	},
286
287	{
288		__index = function(self, key)
289			local tagFunc = tagStrings[key]
290			if(tagFunc) then
291				local func, err = loadstring('return ' .. tagFunc)
292				if(func) then
293					func = func()
294
295					-- Want to trigger __newindex, so no rawset.
296					self[key] = func
297					tagStrings[key] = nil
298
299					return func
300				else
301					error(err, 3)
302				end
303			end
304		end,
305		__newindex = function(self, key, val)
306			if(type(val) == 'string') then
307				tagStrings[key] = val
308			elseif(type(val) == 'function') then
309				-- So we don't clash with any custom envs.
310				if(getfenv(val) == _G) then
311					setfenv(val, _PROXY)
312				end
313
314				rawset(self, key, val)
315			end
316		end,
317	}
318)
319
320_ENV._TAGS = tags
321
322local tagEvents = {
323	["curhp"]               = "UNIT_HEALTH",
324	["dead"]                = "UNIT_HEALTH",
325	["leader"]              = "PARTY_LEADER_CHANGED",
326	["leaderlong"]          = "PARTY_LEADER_CHANGED",
327	["level"]               = "UNIT_LEVEL PLAYER_LEVEL_UP",
328	["maxhp"]               = "UNIT_MAXHEALTH",
329	["missinghp"]           = "UNIT_HEALTH UNIT_MAXHEALTH",
330	["name"]                = "UNIT_NAME_UPDATE",
331	["perhp"]               = "UNIT_HEALTH UNIT_MAXHEALTH",
332	["pvp"]                 = "UNIT_FACTION",
333	["resting"]             = "PLAYER_UPDATE_RESTING",
334	["smartlevel"]          = "UNIT_LEVEL PLAYER_LEVEL_UP UNIT_CLASSIFICATION_CHANGED",
335	["threat"]              = "UNIT_THREAT_SITUATION_UPDATE",
336	["threatcolor"]         = "UNIT_THREAT_SITUATION_UPDATE",
337	['cpoints']             = 'UNIT_COMBO_POINTS PLAYER_TARGET_CHANGED',
338	['rare']                = 'UNIT_CLASSIFICATION_CHANGED',
339	['classification']      = 'UNIT_CLASSIFICATION_CHANGED',
340	['shortclassification'] = 'UNIT_CLASSIFICATION_CHANGED',
341	["group"]               = "RAID_ROSTER_UPDATE",
342	["curpp"]               = 'UNIT_POWER',
343	["maxpp"]               = 'UNIT_MAXPOWER',
344	["missingpp"]           = 'UNIT_MAXPOWER UNIT_POWER',
345	["perpp"]               = 'UNIT_MAXPOWER UNIT_POWER',
346	["offline"]             = "UNIT_HEALTH UNIT_CONNECTION",
347	["status"]              = "UNIT_HEALTH PLAYER_UPDATE_RESTING UNIT_CONNECTION",
348	["pereclipse"]          = 'UNIT_POWER',
349	['curmana']             = 'UNIT_POWER UNIT_MAXPOWER',
350	['maxmana']             = 'UNIT_POWER UNIT_MAXPOWER',
351}
352
353local unitlessEvents = {
354	PLAYER_LEVEL_UP = true,
355	PLAYER_UPDATE_RESTING = true,
356	PLAYER_TARGET_CHANGED = true,
357
358	PARTY_LEADER_CHANGED = true,
359
360	RAID_ROSTER_UPDATE = true,
361
362	UNIT_COMBO_POINTS = true
363}
364
365local events = {}
366local frame = CreateFrame"Frame"
367frame:SetScript('OnEvent', function(self, event, unit)
368	local strings = events[event]
369	if(strings) then
370		for k, fontstring in next, strings do
371			if(fontstring:IsVisible() and (unitlessEvents[event] or fontstring.parent.unit == unit)) then
372				fontstring:UpdateTag()
373			end
374		end
375	end
376end)
377
378local OnUpdates = {}
379local eventlessUnits = {}
380
381local createOnUpdate = function(timer)
382	local OnUpdate = OnUpdates[timer]
383
384	if(not OnUpdate) then
385		local total = timer
386		local frame = CreateFrame'Frame'
387		local strings = eventlessUnits[timer]
388
389		frame:SetScript('OnUpdate', function(self, elapsed)
390			if(total >= timer) then
391				for k, fs in next, strings do
392					if(fs.parent:IsShown() and UnitExists(fs.parent.unit)) then
393						fs:UpdateTag()
394					end
395				end
396
397				total = 0
398			end
399
400			total = total + elapsed
401		end)
402
403		OnUpdates[timer] = frame
404	end
405end
406
407local OnShow = function(self)
408	for _, fs in next, self.__tags do
409		fs:UpdateTag()
410	end
411end
412
413local getTagName = function(tag)
414	local s = (tag:match('>+()') or 2)
415	local e = tag:match('.*()<+')
416	e = (e and e - 1) or -2
417
418	return tag:sub(s, e), s, e
419end
420
421local RegisterEvent = function(fontstr, event)
422	if(not events[event]) then events[event] = {} end
423
424	frame:RegisterEvent(event)
425	table.insert(events[event], fontstr)
426end
427
428local RegisterEvents = function(fontstr, tagstr)
429	for tag in tagstr:gmatch(_PATTERN) do
430		tag = getTagName(tag)
431		local tagevents = tagEvents[tag]
432		if(tagevents) then
433			for event in tagevents:gmatch'%S+' do
434				RegisterEvent(fontstr, event)
435			end
436		end
437	end
438end
439
440local UnregisterEvents = function(fontstr)
441	for event, data in pairs(events) do
442		for k, tagfsstr in pairs(data) do
443			if(tagfsstr == fontstr) then
444				if(#data == 1) then
445					frame:UnregisterEvent(event)
446				end
447
448				table.remove(data, k)
449			end
450		end
451	end
452end
453
454local tagPool = {}
455local funcPool = {}
456local tmp = {}
457
458local Tag = function(self, fs, tagstr)
459	if(not fs or not tagstr) then return end
460
461	if(not self.__tags) then
462		self.__tags = {}
463		table.insert(self.__elements, OnShow)
464	else
465		-- Since people ignore everything that's good practice - unregister the tag
466		-- if it already exists.
467		for _, tag in pairs(self.__tags) do
468			if(fs == tag) then
469				-- We don't need to remove it from the __tags table as Untag handles
470				-- that for us.
471				self:Untag(fs)
472			end
473		end
474	end
475
476	fs.parent = self
477
478	local func = tagPool[tagstr]
479	if(not func) then
480		local format, numTags = tagstr:gsub('%%', '%%%%'):gsub(_PATTERN, '%%s')
481		local args = {}
482
483		for bracket in tagstr:gmatch(_PATTERN) do
484			local tagFunc = funcPool[bracket] or tags[bracket:sub(2, -2)]
485			if(not tagFunc) then
486				local tagName, s, e = getTagName(bracket)
487
488				local tag = tags[tagName]
489				if(tag) then
490					s = s - 2
491					e = e + 2
492
493					if(s ~= 0 and e ~= 0) then
494						local pre = bracket:sub(2, s)
495						local ap = bracket:sub(e, -2)
496
497						tagFunc = function(u,r)
498							local str = tag(u,r)
499							if(str) then
500								return pre..str..ap
501							end
502						end
503					elseif(s ~= 0) then
504						local pre = bracket:sub(2, s)
505
506						tagFunc = function(u,r)
507							local str = tag(u,r)
508							if(str) then
509								return pre..str
510							end
511						end
512					elseif(e ~= 0) then
513						local ap = bracket:sub(e, -2)
514
515						tagFunc = function(u,r)
516							local str = tag(u,r)
517							if(str) then
518								return str..ap
519							end
520						end
521					end
522
523					funcPool[bracket] = tagFunc
524				end
525			end
526
527			if(tagFunc) then
528				table.insert(args, tagFunc)
529			else
530				return error(('Attempted to use invalid tag %s.'):format(bracket), 3)
531			end
532		end
533
534		if(numTags == 1) then
535			func = function(self)
536				local parent = self.parent
537				local realUnit
538				if(self.overrideUnit) then
539					realUnit = parent.realUnit
540				end
541
542				_ENV._COLORS = parent.colors
543				return self:SetFormattedText(
544					format,
545					args[1](parent.unit, realUnit) or ''
546				)
547			end
548		elseif(numTags == 2) then
549			func = function(self)
550				local parent = self.parent
551				local unit = parent.unit
552				local realUnit
553				if(self.overrideUnit) then
554					realUnit = parent.realUnit
555				end
556
557				_ENV._COLORS = parent.colors
558				return self:SetFormattedText(
559					format,
560					args[1](unit, realUnit) or '',
561					args[2](unit, realUnit) or ''
562				)
563			end
564		elseif(numTags == 3) then
565			func = function(self)
566				local parent = self.parent
567				local unit = parent.unit
568				local realUnit
569				if(self.overrideUnit) then
570					realUnit = parent.realUnit
571				end
572
573				_ENV._COLORS = parent.colors
574				return self:SetFormattedText(
575					format,
576					args[1](unit, realUnit) or '',
577					args[2](unit, realUnit) or '',
578					args[3](unit, realUnit) or ''
579				)
580			end
581		else
582			func = function(self)
583				local parent = self.parent
584				local unit = parent.unit
585				local realUnit
586				if(self.overrideUnit) then
587					realUnit = parent.realUnit
588				end
589
590				_ENV._COLORS = parent.colors
591				for i, func in next, args do
592					tmp[i] = func(unit, realUnit) or ''
593				end
594
595				-- We do 1, numTags because tmp can hold several unneeded variables.
596				return self:SetFormattedText(format, unpack(tmp, 1, numTags))
597			end
598		end
599
600		tagPool[tagstr] = func
601	end
602	fs.UpdateTag = func
603
604	local unit = self.unit
605	if((unit and unit:match'%w+target') or fs.frequentUpdates) then
606		local timer
607		if(type(fs.frequentUpdates) == 'number') then
608			timer = fs.frequentUpdates
609		else
610			timer = .5
611		end
612
613		if(not eventlessUnits[timer]) then eventlessUnits[timer] = {} end
614		table.insert(eventlessUnits[timer], fs)
615
616		createOnUpdate(timer)
617	else
618		RegisterEvents(fs, tagstr)
619	end
620
621	table.insert(self.__tags, fs)
622end
623
624local Untag = function(self, fs)
625	if(not fs) then return end
626
627	UnregisterEvents(fs)
628	for _, timers in next, eventlessUnits do
629		for k, fontstr in next, timers do
630			if(fs == fontstr) then
631				table.remove(timers, k)
632			end
633		end
634	end
635
636	for k, fontstr in next, self.__tags do
637		if(fontstr == fs) then
638			table.remove(self.__tags, k)
639		end
640	end
641
642	fs.UpdateTag = nil
643end
644
645oUF.Tags = tags
646oUF.TagEvents = tagEvents
647oUF.UnitlessTagEvents = unitlessEvents
648
649oUF:RegisterMetaFunction('Tag', Tag)
650oUF:RegisterMetaFunction('Untag', Untag)