· 10 years ago · Sep 10, 2016, 03:20 PM
1--[[--------------------------------------------------------------------
2 Grid
3 Compact party and raid unit frames.
4 Copyright (c) 2006-2009 Kyle Smith (Pastamancer).
5 Copyright (c) 2009-2016 Phanx <addons@phanx.net>.
6 All rights reserved. See the accompanying LICENSE file for details.
7 http://www.wowinterface.com/downloads/info5747-Grid.html
8 http://www.wowace.com/addons/grid/
9 http://www.curse.com/addons/wow/grid
10------------------------------------------------------------------------
11 Auras.lua
12 Grid status module for tracking buffs/debuffs.
13----------------------------------------------------------------------]]
14
15local _, Grid = ...
16local L = Grid.L
17
18local strutf8sub = string.utf8sub
19local format, gmatch, gsub, pairs, strfind, strlen, strlower, strmatch, strsub, tostring, type
20 = format, gmatch, gsub, pairs, strfind, strlen, strlower, strmatch, strsub, tostring, type
21local IsPlayerSpell, UnitAura, UnitClass, UnitGUID, UnitIsPlayer, UnitIsVisible
22 = IsPlayerSpell, UnitAura, UnitClass, UnitGUID, UnitIsPlayer, UnitIsVisible
23
24local GridFrame = Grid:GetModule("GridFrame")
25local GridRoster = Grid:GetModule("GridRoster")
26
27local GridStatusAuras = Grid:NewStatusModule("GridStatusAuras", "AceTimer-3.0")
28GridStatusAuras.menuName = L["Auras"]
29
30local _, PLAYER_CLASS = UnitClass("player")
31local PlayerCanDispel = {}
32
33local spell_names = {
34-- All
35 ["Ghost"] = GetSpellInfo(8326),
36-- Druid
37 ["Lifebloom"] = GetSpellInfo(33763),
38 ["Regrowth"] = GetSpellInfo(8936),
39 ["Rejuvenation"] = GetSpellInfo(774),
40 ["Wild Growth"] = GetSpellInfo(48438),
41-- Monk
42 ["Enveloping Mist"] = GetSpellInfo(124682),
43 ["Life Cocoon"] = GetSpellInfo(116849),
44 ["Renewing Mist"] = GetSpellInfo(115151),
45-- Paladin
46 ["Beacon of Faith"] = GetSpellInfo(156910),
47 ["Beacon of Light"] = GetSpellInfo(53563),
48 --["Eternal Flame"] = GetSpellInfo(114163), -- removed in 7.0
49 ["Forbearance"] = GetSpellInfo(25771),
50 --["Sacred Shield"] = GetSpellInfo(20925), -- removed in 7.0
51-- Priest
52 ["Atonement"] = GetSpellInfo(214206), -- new in 7.0, +healing mod, alternate spellid 214206
53 ["Clarity of Will"] = GetSpellInfo(152118), -- new in 7.0, shield
54 --["Grace"] = GetSpellInfo(77613), -- removed in 7.0, replaced by Atonement
55 ["Power Word: Shield"] = GetSpellInfo(17),
56 ["Prayer of Mending"] = GetSpellInfo(33076),
57 ["Renew"] = GetSpellInfo(139),
58 --["Weakened Soul"] = GetSpellInfo(6788), -- removed in 7.0 ?
59-- Shaman
60 --["Earth Shield"] = GetSpellInfo(204288), -- pvp only in 7.0
61 ["Riptide"] = GetSpellInfo(61295),
62}
63
64-- data used by aura scanning
65local buff_names = {}
66local player_buff_names = {}
67local debuff_names = {}
68
69local debuff_types = {
70 ["Curse"] = "dispel_curse",
71 ["Disease"] = "dispel_disease",
72 ["Magic"] = "dispel_magic",
73 ["Poison"] = "dispel_poison",
74}
75
76function GridStatusAuras:StatusForSpell(spell, isBuff)
77 return format(isBuff and "buff_%s" or "debuff_%s", gsub(spell, " ", ""))
78end
79
80function GridStatusAuras:TextForSpell(spell)
81 if strfind(spell, "%s") then
82 local str = ""
83 for word in gmatch(spell, "%S+") do
84 str = str .. strutf8sub(word, 1, 1)
85 end
86 return str
87 else
88 return strutf8sub(spell, 1, 3)
89 end
90end
91
92local statusDefaultDB = {
93 enable = true,
94 priority = 90,
95 duration = false,
96 color = { r = .5, g = .5, b = .5, a = 1 },
97 statusText = "name",
98 statusColor = "present",
99 refresh = 0.3,
100 durationTenths = false,
101 durationColorLow = { r = .15, g = .15, b = .15, a = 1 },
102 durationColorMiddle = { r = .35, g = .35, b = .35, a = 1 },
103 durationColorHigh = { r = .5, g = .5, b = .5, a = 1 },
104 durationLow = 2,
105 durationHigh = 4,
106 countColorLow = { r = 1, g = 0, b = 0, a = 1 },
107 countColorMiddle = { r = 1, g = 1, b = 0, a = 1 },
108 countColorHigh = { r = 0, g = 1, b = 0, a = 1 },
109 countLow = 1,
110 countHigh = 2,
111}
112
113-- Perform a deep copy of defaultDB into the given settings table except into the slots
114-- where the value is non-nil (non-default setting).
115function GridStatusAuras:CopyDefaults(settings, defaults)
116 if type(defaults) ~= "table" then return {} end
117 if type(settings) ~= "table" then settings = {} end
118 for k, v in pairs(defaults) do
119 if type(v) == "table" then
120 settings[k] = self:CopyDefaults(settings[k], v)
121 elseif type(v) ~= type(settings[k]) then
122 settings[k] = v
123 end
124 end
125 return settings
126end
127
128GridStatusAuras.defaultDB = {
129 advancedOptions = false,
130--[[
131 ["boss_aura"] = {
132 desc = L["Boss Aura"],
133 color = { r = 1, g = 0, b = 0, a = 1 },
134 priority = 90,
135 order = 20,
136 },
137]]
138 -- Debuff Types
139 ["dispel_curse"] = {
140 desc = format(L["Debuff type: %s"], L["Curse"]),
141 text = DEBUFF_SYMBOL_CURSE,
142 color = { r = .6, g = 0, b = 1, a = 1 },
143 durationColorLow = { r = .18, g = 0, b = .3, a = 1 },
144 durationColorMiddle = { r = .42, g = 0, b = .7, a = 1 },
145 durationColorHigh = { r = .6, g = 0, b = 1, a = 1 },
146 dispellable = true,
147 order = 25,
148 },
149 ["dispel_disease"] = {
150 desc = format(L["Debuff type: %s"], L["Disease"]),
151 text = DEBUFF_SYMBOL_DISEASE,
152 color = { r = .6, g = .4, b = 0, a = 1 },
153 durationColorLow = { r = .18, g = .12, b = 0, a = 1 },
154 durationColorMiddle = { r = .42, g = .28, b = 0, a = 1 },
155 durationColorHigh = { r = .6, g = .4, b = 0, a = 1 },
156 dispellable = true,
157 order = 25,
158 },
159 ["dispel_magic"] = {
160 desc = format(L["Debuff type: %s"], L["Magic"]),
161 text = DEBUFF_SYMBOL_MAGIC,
162 color = { r = .2, g = .6, b = 1, a = 1 },
163 durationColorLow = { r = .06, g = .18, b = .3, a = 1 },
164 durationColorMiddle = { r = .14, g = .42, b = .7, a = 1 },
165 durationColorHigh = { r = .2, g = .6, b = 1, a = 1 },
166 dispellable = true,
167 order = 25,
168 },
169 ["dispel_poison"] = {
170 desc = format(L["Debuff type: %s"], L["Poison"]),
171 text = DEBUFF_SYMBOL_POISON,
172 color = { r = 0, g = .6, b = 0, a = 1 },
173 durationColorLow = { r = 0, g = .18, b = 0, a = 1 },
174 durationColorMiddle = { r = 0, g = .42, b = 0, a = 1 },
175 durationColorHigh = { r = 0, g = .6, b = 0, a = 1 },
176 dispellable = true,
177 order = 25,
178 },
179
180 -- General Debuffs
181 [GridStatusAuras:StatusForSpell("Ghost")] = {
182 desc = format(L["Debuff: %s"], spell_names["Ghost"]),
183 debuff = spell_names["Ghost"],
184 text = GridStatusAuras:TextForSpell(spell_names["Ghost"]),
185 color = { r = .5, g = .5, b = .5, a = 1 },
186 durationColorLow = { r = .15, g = .15, b = .15, a = 1 },
187 durationColorMiddle = { r = .35, g = .35, b = .35, a = 1 },
188 durationColorHigh = { r = .5, g = .5, b = .5, a = 1 },
189 },
190
191 -- Druid
192 [GridStatusAuras:StatusForSpell("Lifebloom", true)] = {
193 desc = format(L["Buff: %s"], spell_names["Lifebloom"]),
194 buff = spell_names["Lifebloom"],
195 text = GridStatusAuras:TextForSpell(spell_names["Lifebloom"]),
196 color = { r = .3, g = .7, b = 0, a = 1 },
197 durationColorLow = { r = 1, g = 0, b = 0, a = 1 },
198 durationColorMiddle = { r = .21, g = .49, b = 0, a = 1 },
199 durationColorHigh = { r = .3, g = .7, b = 0, a = 1 },
200 countColorLow = { r = 1, g = 0, b = 0, a = 1 },
201 countColorMiddle = { r = 1, g = 1, b = 0, a = 1 },
202 countColorHigh = { r = 0, g = 1, b = 0, a = 1 },
203 countLow = 1,
204 countHigh = 2,
205 mine = true,
206 },
207 [GridStatusAuras:StatusForSpell("Regrowth", true)] = {
208 desc = format(L["Buff: %s"], spell_names["Regrowth"]),
209 buff = spell_names["Regrowth"],
210 text = GridStatusAuras:TextForSpell(spell_names["Regrowth"]),
211 color = { r = 1, g = .7, b = .1, a = 1 },
212 durationColorLow = { r = 1, g = 0, b = 0, a = 1 },
213 durationColorMiddle = { r = .7, g = .49, b = .07, a = 1 },
214 durationColorHigh = { r = 1, g = .7, b = .1, a = 1 },
215 mine = true,
216 },
217 [GridStatusAuras:StatusForSpell("Rejuvenation", true)] = {
218 desc = format(L["Buff: %s"], spell_names["Rejuvenation"]),
219 buff = spell_names["Rejuvenation"],
220 text = GridStatusAuras:TextForSpell(spell_names["Rejuvenation"]),
221 color = { r = 0, g = .3, b = .7, a = 1 },
222 durationColorLow = { r = 1, g = 0, b = 0, a = 1 },
223 durationColorMiddle = { r = 0, g = .21, b = .49, a = 1 },
224 durationColorHigh = { r = 0, g = .3, b = .7, a = 1 },
225 mine = true,
226 },
227
228 -- Monk
229 [GridStatusAuras:StatusForSpell("Enveloping Mist", true)] = {
230 buff = spell_names["Enveloping Mist"],
231 desc = format(L["Buff: %s"], spell_names["Enveloping Mist"]),
232 text = GridStatusAuras:TextForSpell(spell_names["Enveloping Mist"]),
233 color = { r = 0.2, g = 1, b = 0.2, a = 1 },
234 mine = true,
235 },
236 [GridStatusAuras:StatusForSpell("Life Cocoon", true)] = {
237 buff = spell_names["Life Cocoon"],
238 desc = format(L["Buff: %s"], spell_names["Life Cocoon"]),
239 text = GridStatusAuras:TextForSpell(spell_names["Life Cocoon"]),
240 color = { r = 0.8, g = 0.8, b = 0.4, a = 1 },
241 },
242 [GridStatusAuras:StatusForSpell("Renewing Mist", true)] = {
243 buff = spell_names["Renewing Mist"],
244 desc = format(L["Buff: %s"], spell_names["Renewing Mist"]),
245 text = GridStatusAuras:TextForSpell(spell_names["Renewing Mist"]),
246 color = { r = 0.4, g = 0, b = 0.8, a = 1 },
247 mine = true,
248 },
249
250 -- Paladin
251 [GridStatusAuras:StatusForSpell("Beacon of Faith", true)] = { -- new in 7.0
252 desc = format(L["Buff: %s"], spell_names["Beacon of Faith"]),
253 buff = spell_names["Beacon of Faith"],
254 text = GridStatusAuras:TextForSpell(spell_names["Beacon of Faith"]),
255 color = { r = .5, g = 0.7, b = 0.3, a = 1 },
256 durationColorLow = { r = 1, g = 0, b = 0, a = 1 },
257 durationColorMiddle = { r = .49, g = .49, b = 0, a = 1 },
258 durationColorHigh = { r = .7, g = .7, b = 0, a = 1 },
259 durationLow = 5,
260 durationHigh = 10,
261 mine = true,
262 },
263 [GridStatusAuras:StatusForSpell("Beacon of Light", true)] = {
264 desc = format(L["Buff: %s"], spell_names["Beacon of Light"]),
265 buff = spell_names["Beacon of Light"],
266 text = GridStatusAuras:TextForSpell(spell_names["Beacon of Light"]),
267 color = { r = .5, g = 0.7, b = 0.3, a = 1 },
268 durationColorLow = { r = 1, g = 0, b = 0, a = 1 },
269 durationColorMiddle = { r = .49, g = .49, b = 0, a = 1 },
270 durationColorHigh = { r = .7, g = .7, b = 0, a = 1 },
271 durationLow = 5,
272 durationHigh = 10,
273 mine = true,
274 },
275 [GridStatusAuras:StatusForSpell("Forbearance")] = {
276 desc = format(L["Debuff: %s"], spell_names["Forbearance"]),
277 debuff = spell_names["Forbearance"],
278 text = GridStatusAuras:TextForSpell(spell_names["Forbearance"]),
279 color = { r = .5, g = .5, b = .5, a = 1 },
280 durationColorLow = { r = .15, g = .15, b = .15, a = 1 },
281 durationColorMiddle = { r = .35, g = .35, b = .35, a = 1 },
282 durationColorHigh = { r = .5, g = .5, b = .5, a = 1 },
283 },
284
285 -- Priest
286 [GridStatusAuras:StatusForSpell("Atonement", true)] = {
287 buff = spell_names["Atonement"],
288 desc = format(L["Buff: %s"], spell_names["Atonement"]),
289 text = GridStatusAuras:TextForSpell(spell_names["Atonement"]),
290 color = { r = 0.2, g = 0.8, b = 1, a = 1 },
291 mine = true,
292 },
293 [GridStatusAuras:StatusForSpell("Clarity of Will", true)] = {
294 desc = format(L["Buff: %s"], spell_names["Clarity of Will"]),
295 buff = spell_names["Clarity of Will"],
296 text = GridStatusAuras:TextForSpell(spell_names["Clarity of Will"]),
297 color = { r = .8, g = .8, b = 0, a = 1 },
298 durationColorLow = { r = 1, g = 0, b = 0, a = 1 },
299 durationColorMiddle = { r = .56, g = .56, b = 0, a = 1 },
300 durationColorHigh = { r = .8, g = .8, b = 0, a = 1 },
301 },
302 [GridStatusAuras:StatusForSpell("Power Word: Shield", true)] = {
303 desc = format(L["Buff: %s"], spell_names["Power Word: Shield"]),
304 buff = spell_names["Power Word: Shield"],
305 text = GridStatusAuras:TextForSpell(spell_names["Power Word: Shield"]),
306 color = { r = .8, g = .8, b = 0, a = 1 },
307 durationColorLow = { r = 1, g = 0, b = 0, a = 1 },
308 durationColorMiddle = { r = .56, g = .56, b = 0, a = 1 },
309 durationColorHigh = { r = .8, g = .8, b = 0, a = 1 },
310 },
311 [GridStatusAuras:StatusForSpell("Prayer of Mending", true)] = {
312 buff = spell_names["Prayer of Mending"],
313 desc = format(L["Buff: %s"], spell_names["Prayer of Mending"]),
314 text = GridStatusAuras:TextForSpell(spell_names["Prayer of Mending"]),
315 color = { r = 0.2, g = 0.8, b = 1, a = 1 },
316 mine = true,
317 },
318 [GridStatusAuras:StatusForSpell("Renew", true)] = {
319 desc = format(L["Buff: %s"], spell_names["Renew"]),
320 buff = spell_names["Renew"],
321 text = GridStatusAuras:TextForSpell(spell_names["Renew"]),
322 color = { r = 0, g = .7, b = .3, a = 1 },
323 durationColorLow = { r = 1, g = 0, b = 0, a = 1 },
324 durationColorMiddle = { r = 0, g = .49, b = .21, a = 1 },
325 durationColorHigh = { r = 0, g = .7, b = .3, a = 1 },
326 mine = true,
327 },
328
329 -- Shaman
330 [GridStatusAuras:StatusForSpell("Riptide", true)] = {
331 desc = format(L["Buff: %s"], spell_names["Riptide"]),
332 buff = spell_names["Riptide"],
333 text = GridStatusAuras:TextForSpell(spell_names["Riptide"]),
334 color = { r = .4, g = 0, b = .8, a = 1 },
335 durationColorLow = { r = 1, g = 0, b = 0, a = 1 },
336 durationColorMiddle = { r = .28, g = 0, b = .56, a = 1 },
337 durationColorHigh = { r = .4, g = 0, b = .8, a = 1 },
338 mine = true,
339 },
340}
341
342local default_auras = {}
343do
344 for status, settings in pairs(GridStatusAuras.defaultDB) do
345 if type(settings) == "table" and settings.text then
346 GridStatusAuras:CopyDefaults(settings, statusDefaultDB)
347 default_auras[status] = true
348 end
349 end
350end
351
352GridStatusAuras.extraOptions = {}
353
354function GridStatusAuras:PostInitialize()
355 self:RegisterStatuses()
356
357 -- Wasn't supposed to be released yet, kill it with fire.
358 self.db.profile.boss_aura = nil
359
360 -- Remove statuses for spells that were removed from the game in 7.0:
361 for name, isBuff in pairs({
362 ["Grace"] = true,
363 ["Earth Shield"] = true,
364 ["Eternal Flame"] = true,
365 ["Sacred Shield"] = true,
366 ["Weakened Soul"] = true,
367 }) do
368 local status = self:StatusForSpell(name, isBuff)
369 if self.db.profile[status] then
370 self:Debug("Removed deprecated status:", name)
371 self.db.profile[status] = nil
372 end
373 end
374
375 self.options.args["add_buff"] = {
376 name = L["Add Buff"],
377 desc = L["Create a new buff status."],
378 order = 11,
379 width = "double",
380 type = "input",
381 usage = L["<buff name>"],
382 get = false,
383 set = function(_, v)
384 self:AddAura(v, true)
385 end,
386 }
387 self.options.args["add_debuff"] = {
388 name = L["Add Debuff"],
389 desc = L["Create a new debuff status."],
390 order = 31,
391 width = "double",
392 type = "input",
393 usage = L["<debuff name>"],
394 get = false,
395 set = function(_, v)
396 self:AddAura(v, false)
397 end,
398 }
399 self.options.args["delete_aura"] = {
400 name = L["Remove Aura"],
401 desc = L["Remove an existing buff or debuff status."],
402 order = -2,
403 type = "group",
404 dialogInline = true,
405 args = {},
406 }
407 self.options.args["advancedOptions"] = {
408 name = L["Show advanced options"],
409 desc = L["Show advanced options for buff and debuff statuses.\n\nBeginning users may wish to leave this disabled until you are more familiar with Grid, to avoid being overwhelmed by complicated options menus."],
410 order = -1,
411 width = "full",
412 type = "toggle",
413 get = function()
414 return self.db.profile.advancedOptions
415 end,
416 set = function(_, v)
417 self.db.profile.advancedOptions = v
418 end,
419 }
420end
421
422function GridStatusAuras:PostEnable()
423 self:CreateRemoveOptions()
424 self:UpdateDispellable()
425end
426
427function GridStatusAuras:PostReset()
428 self:UnregisterStatuses()
429 self:RegisterStatuses()
430 self:CreateRemoveOptions()
431 self:ResetDurationStatuses()
432 self:UpdateAuraScanList()
433end
434
435function GridStatusAuras:EnabledStatusCount()
436 local enable_count = 0
437
438 for status, settings in pairs(self.db.profile) do
439 if type(settings) == "table" and settings.enable then
440 enable_count = enable_count + 1
441 end
442 end
443
444 return enable_count
445end
446
447function GridStatusAuras:OnStatusEnable(status)
448 self:RegisterMessage("Grid_UnitJoined")
449 self:RegisterEvent("UNIT_AURA", "ScanUnitAuras")
450 self:RegisterEvent("SPELLS_CHANGED", "UpdateDispellable")
451
452 self:DeleteDurationStatus(status)
453 self:UpdateDispellable()
454 self:UpdateAuraScanList()
455 self:UpdateAllUnitAuras()
456end
457
458function GridStatusAuras:OnStatusDisable(status)
459 self.core:SendStatusLostAllUnits(status)
460 self:DeleteDurationStatus(status)
461 self:UpdateAuraScanList()
462
463 if self:EnabledStatusCount() == 0 then
464 self:UnregisterMessage("Grid_UnitJoined")
465 self:UnregisterEvent("UNIT_AURA")
466 end
467end
468
469function GridStatusAuras:RegisterStatuses()
470 local profile = self.db.profile
471
472 for status, settings in pairs(profile) do
473 if type(settings) == "table" then
474 if settings.desc then
475 --self:Debug("Registering status:", status)
476 if settings.buff == nil and settings.debuff == nil and not self.defaultDB[status] then
477 self:Debug("Upgrading old aura:", settings.desc)
478 local aura = strmatch(settings.desc, gsub(L["Buff: %s"], "%%s", "(.+)"))
479 if aura then
480 settings.aura = aura
481 if settings.text == aura then
482 settings.text = self:TextForSpell(aura)
483 end
484 --self:Debug("Upgraded buff:", aura)
485 else
486 aura = strmatch(settings.desc, gsub(L["Debuff: %s"], "%%s", "(.+)"))
487 if aura then
488 settings.debuff = aura
489 if settings.text == aura then
490 settings.text = self:TextForSpell(aura)
491 end
492 --self:Debug("Upgraded debuff:", aura)
493 else
494 self:Debug("Upgrade failed!")
495 end
496 end
497 end
498 --[[if status == "boss_aura" then
499 self:RegisterStatus(status, settings.desc, { text = false }, false, settings.order)
500
501 else]] if settings.buff or settings.debuff or self.defaultDB[status] then
502 local name = settings.text
503 local desc = settings.desc or name
504 local isBuff = not not settings.buff
505 local order = settings.order or (isBuff and 15 or 35)
506
507 self:Debug("Registering", status, desc)
508 if not self.defaultDB[status] then
509 self.defaultDB[status] = {}
510 self:CopyDefaults(self.defaultDB[status], statusDefaultDB)
511 end
512 self:CopyDefaults(settings, self.defaultDB[status])
513 self:RegisterStatus(status, desc, self:OptionsForStatus(status, isBuff), false, order)
514 end
515 else
516 -- Can't do this because it screws people who play in multiple languages.
517 --self:Debug("Removing invalid status:", status)
518 --profile[status] = nil
519 end
520 end
521 end
522 self.db:RegisterDefaults({ profile = self.defaultDB or {} })
523end
524
525function GridStatusAuras:UnregisterStatuses()
526 for status, moduleName, desc in self.core:RegisteredStatusIterator() do
527 if moduleName == self.name then
528 self:UnregisterStatus(status)
529 self.options.args[status] = nil
530 end
531 end
532end
533
534local classes = {}
535do
536 local t = {}
537 FillLocalizedClassList(t, false)
538 for token, name in pairs(t) do
539 classes[token:lower()] = name
540 end
541end
542
543function GridStatusAuras:OptionsForStatus(status, isBuff)
544 local auraOptions = {
545 text = {
546 name = L["Text"],
547 desc = L["Text to display on text indicators"],
548 order = 50,
549 type = "input",
550 get = function()
551 return GridStatusAuras.db.profile[status].text
552 end,
553 set = function(_, v)
554 GridStatusAuras.db.profile[status].text = v
555 GridStatusAuras:UpdateAllUnitAuras()
556 end,
557 },
558--[[ -- ##DELETE
559 class = {
560 name = L["Class Filter"],
561 desc = L["Show status for the selected classes."],
562 order = 200,
563 type = "group",
564 dialogInline = true,
565 hidden = function()
566 return not self.db.profile.advancedOptions
567 end,
568 args = {
569 pet = {
570 name = L["Pet"],
571 desc = L["Show on pets and vehicles."],
572 order = -1,
573 width = "double",
574 type = "toggle",
575 get = function()
576 return GridStatusAuras.db.profile[status].pet ~= false
577 end,
578 set = function(_, v)
579 GridStatusAuras.db.profile[status].pet = v
580 GridStatusAuras:UpdateAllUnitAuras()
581 end,
582 },
583 },
584 },
585]]
586 statusInfo = {
587 name = L["Status Information"],
588 desc = L["Change what information is displayed using the status color and text."],
589 order = 300,
590 type = "group",
591 dialogInline = true,
592 hidden = function()
593 return not self.db.profile.advancedOptions
594 end,
595 args = {
596 colorInfo = {
597 name = L["Color"],
598 desc = L["Change which information is shown by the status color."],
599 order = 310,
600 width = "double",
601 type = "select",
602 values = {
603 ["present"] = L["Present or missing"],
604 ["duration"] = L["Time left"],
605 ["count"] = L["Stack count"],
606 },
607 get = function()
608 return self.db.profile[status].statusColor
609 end,
610 set = function(_, v)
611 self.db.profile[status].statusColor = v
612 self:UpdateAllUnitAuras()
613 end,
614 },
615 textInfo = {
616 name = L["Text"],
617 desc = L["Change which information is shown by the status text."],
618 order = 320,
619 width = "double",
620 type = "select",
621 values = {
622 ["name"] = L["Buff name"],
623 ["duration"] = L["Time left"],
624 ["count"] = L["Stack count"],
625 },
626 get = function()
627 return self.db.profile[status].statusText
628 end,
629 set = function(_, v)
630 self.db.profile[status].statusText = v
631 self:UpdateAllUnitAuras()
632 end,
633 hidden = function()
634 return not self.db.profile.advancedOptions
635 end,
636 },
637 durationTenths = {
638 name = L["Show time left to tenths"],
639 desc = L["Show the time left to tenths of a second, instead of only whole seconds."],
640 order = 330,
641 width = "double",
642 type = "toggle",
643 get = function()
644 return self.db.profile[status].durationTenths
645 end,
646 set = function(_, v)
647 self.db.profile[status].durationTenths = v
648 self:UpdateAllUnitAuras()
649 end,
650 hidden = function()
651 return not self.db.profile.advancedOptions or self.db.profile[status].statusText ~= "duration"
652 end,
653 },
654 countSettings = {
655 name = format(L["%s colors"], L["Stack count"]),
656 desc = format(L["%s colors and threshold values."], L["Stack count"]),
657 order = 350,
658 type = "group",
659 dialogInline = true,
660 get = function(info)
661 local optionName = info[#info]
662 if (info.type == "color") then
663 local color = self.db.profile[status][optionName]
664 return color.r, color.g, color.b, color.a
665 elseif (info.type == "range") then
666 return self.db.profile[status][optionName]
667 end
668 end,
669 set = function(info, r, g, b, a)
670 local optionName = info[#info]
671 if (info.type == "color") then
672 local color = self.db.profile[status][optionName]
673 color.r = r
674 color.g = g
675 color.b = b
676 color.a = a or 1
677 elseif (info.type == "range") then
678 self.db.profile[status][optionName] = r
679 end
680 self:UpdateAllUnitAuras()
681 end,
682 hidden = function()
683 return not self.db.profile.advancedOptions or self.db.profile[status].statusColor ~= "count"
684 end,
685 args = {
686 countColorLow = {
687 name = L["Low color"],
688 desc = format(L["Color when %s is below the low threshold value."], L["Stack count"]),
689 order = 351,
690 type = "color",
691 },
692 countLow = {
693 name = L["Low threshold"],
694 desc = format(L["%s is low below this value."], L["Stack count"]),
695 order = 352,
696 type = "range",
697 min = 0, softMin = 0, max = 99, softMax = 10, step = 1,
698 },
699 countColorMiddle = {
700 name = L["Middle color"],
701 desc = format(L["Color when %s is between the low and high threshold values."], L["Stack count"]),
702 order = 353,
703 width = "full",
704 type = "color",
705 },
706 countColorHigh = {
707 name = L["High color"],
708 desc = format(L["Color when %s is above the high threshold value."], L["Stack count"]),
709 order = 354,
710 type = "color",
711 },
712 countHigh = {
713 name = L["High threshold"],
714 desc = format(L["%s is high above this value."], L["Stack count"]),
715 order = 355,
716 type = "range",
717 min = 0, softMin = 0, max = 99, softMax = 10, step = 1,
718 },
719 },
720 },
721 durationSettings = {
722 name = format(L["%s colors"], L["Duration"]),
723 desc = format(L["%s colors and threshold values."], L["Duration"]),
724 order = 360,
725 type = "group",
726 dialogInline = true,
727 get = function(info)
728 local optionName = info[#info]
729 if (info.type == "color") then
730 local color = self.db.profile[status][optionName]
731 return color.r, color.g, color.b, color.a
732 elseif (info.type == "range") then
733 return self.db.profile[status][optionName]
734 end
735 end,
736 set = function(info, r, g, b, a)
737 local optionName = info[#info]
738 if (info.type == "color") then
739 local color = self.db.profile[status][optionName]
740 color.r = r
741 color.g = g
742 color.b = b
743 color.a = a or 1
744 elseif (info.type == "range") then
745 self.db.profile[status][optionName] = r
746 end
747 self:UpdateAllUnitAuras()
748 end,
749 hidden = function()
750 return not self.db.profile.advancedOptions or self.db.profile[status].statusColor ~= "duration"
751 end,
752 args = {
753 durationColorLow = {
754 name = L["Low color"],
755 desc = format(L["Color when %s is below the low threshold value."], L["Duration"]),
756 order = 361,
757 type = "color",
758 },
759 durationLow = {
760 name = L["Low threshold"],
761 desc = format(L["%s is low below this value."], L["Duration"]),
762 order = 362,
763 type = "range",
764 min = 0, softMin = 0, max = 99, softMax = 10, step = 1,
765 },
766 durationColorMiddle = {
767 name = L["Middle color"],
768 desc = format(L["Color when %s is between the low and high threshold values."], L["Duration"]),
769 order = 363,
770 width = "full",
771 type = "color",
772 },
773 durationColorHigh = {
774 name = L["High color"],
775 desc = format(L["Color when %s is above the high threshold value."], L["Duration"]),
776 order = 364,
777 type = "color",
778 },
779 durationHigh = {
780 name = L["High threshold"],
781 desc = format(L["%s is high above this value."], L["Duration"]),
782 order = 365,
783 type = "range",
784 min = 0, softMin = 0, max = 99, softMax = 10, step = 1,
785 },
786 },
787 },
788 refresh = {
789 name = L["Refresh interval"],
790 desc = L["Time in seconds between each refresh of the duration status."],
791 order = 390,
792 width = "double",
793 type = "range",
794 min = 0.1,
795 max = 0.5,
796 step = 0.1,
797 get = function()
798 return self.db.profile[status].refresh
799 end,
800 set = function(_, v)
801 self.db.profile[status].refresh = v
802 self:UpdateAllUnitAuras()
803 end,
804 hidden = function()
805 return not self.db.profile.advancedOptions or self.db.profile[status].statusColor ~= "duration"
806 end,
807 },
808 },
809 },
810 }
811--[[ -- ##DELETE
812 for class, name in pairs(classes) do
813 local class, name = class, name
814 auraOptions.class.args[class] = {
815 name = name,
816 desc = format(L["Show on %s players."], name),
817 type = "toggle",
818 get = function()
819 return GridStatusAuras.db.profile[status][class] ~= false
820 end,
821 set = function(_, v)
822 GridStatusAuras.db.profile[status][class] = v
823 GridStatusAuras:UpdateAllUnitAuras()
824 end,
825 }
826 end
827]]
828 if isBuff then
829 auraOptions.statusInfo.args.textInfo.values["name"] = L["Buff name"]
830 auraOptions.mine = {
831 name = L["Show if mine"],
832 desc = L["Display status only if the buff was cast by you."],
833 order = 60,
834 width = "double",
835 type = "toggle",
836 get = function()
837 return GridStatusAuras.db.profile[status].mine
838 end,
839 set = function(_, v)
840 GridStatusAuras.db.profile[status].mine = v
841 GridStatusAuras:DeleteDurationStatus(status)
842 GridStatusAuras:UpdateAuraScanList()
843 GridStatusAuras:UpdateAllUnitAuras()
844 end,
845 }
846 auraOptions.missing = {
847 name = L["Show if missing"],
848 desc = L["Display status only if the buff is not active."],
849 order = 70,
850 width = "double",
851 type = "toggle",
852 get = function()
853 return GridStatusAuras.db.profile[status].missing
854 end,
855 set = function(_, v)
856 GridStatusAuras.db.profile[status].missing = v
857 GridStatusAuras:UpdateAllUnitAuras()
858 end,
859 }
860 end
861
862 -- super inefficient...
863 for name, found in pairs(debuff_types) do
864 if status == found then
865 auraOptions.dispellable = {
866 name = L["Show only dispellable"],
867 desc = format(L["Show %s debuffs only if you can dispel them."], name),
868 order = 60,
869 width = "double",
870 type = "toggle",
871 get = function()
872 return GridStatusAuras.db.profile[status].dispellable
873 end,
874 set = function(_, v)
875 GridStatusAuras.db.profile[status].dispellable = v
876 GridStatusAuras:UpdateAllUnitAuras()
877 end,
878 }
879 break
880 end
881 end
882
883 return auraOptions
884end
885
886function GridStatusAuras:CreateRemoveOptions()
887 for status, settings in pairs(self.db.profile) do
888 local status = status
889 if type(settings) == "table" and settings.text and not default_auras[status] then
890 local debuffName = settings.desc or settings.text
891 self.options.args.delete_aura.args[status] = {
892 name = debuffName,
893 desc = format(L["Remove %s from the menu"], debuffName),
894 width = "double",
895 type = "execute",
896 func = function() return
897 self:DeleteAura(status)
898 end,
899 }
900 end
901 end
902end
903
904function GridStatusAuras:AddAura(name, isBuff)
905 if strlen(name) < 1 then
906 return self:Debug("AddAura failed, no name entered!")
907 end
908
909 local status = GridStatusAuras:StatusForSpell(name, isBuff)
910
911 -- status already exists
912 if self.db.profile[status] then
913 return self:Debug("AddAura failed, status exists!", name, status)
914 end
915
916 local desc = isBuff and format(L["Buff: %s"], name) or format(L["Debuff: %s"], name)
917
918 if not self.defaultDB[status] then
919 self.defaultDB[status] = {}
920 self:CopyDefaults(self.defaultDB[status], statusDefaultDB)
921 self.db:RegisterDefaults({ profile = self.defaultDB or {} })
922 end
923
924 local settings = {
925 text = self:TextForSpell(name),
926 desc = desc,
927 }
928 if isBuff then
929 settings.buff = name
930 else
931 settings.debuff = name
932 end
933 self:CopyDefaults(settings, self.defaultDB[status])
934 self.db.profile[status] = settings
935
936 self.options.args.delete_aura.args[status] = {
937 name = desc,
938 desc = format(L["Remove %s from the menu"], desc),
939 width = "double",
940 type = "execute",
941 func = function()
942 return self:DeleteAura(status)
943 end,
944 }
945
946 local order = isBuff and 15 or 35
947
948 self:RegisterStatus(status, desc, self:OptionsForStatus(status, isBuff), false, order)
949 self:OnStatusEnable(status)
950end
951
952function GridStatusAuras:DeleteAura(status)
953 self:UnregisterStatus(status)
954 self.options.args[status] = nil
955 self.options.args.delete_aura.args[status] = nil
956 self.db.profile[status] = nil
957 for indicator, indicatorTbl in pairs(GridFrame.db.profile.statusmap) do
958 indicatorTbl[status] = nil
959 end
960 self:DeleteDurationStatus(status)
961 self:UpdateAuraScanList()
962end
963
964function GridStatusAuras:UpdateAllUnitAuras()
965 for guid, unitid in GridRoster:IterateRoster() do
966 self:ScanUnitAuras("UpdateAllUnitAuras", unitid, guid)
967 end
968end
969
970function GridStatusAuras:Grid_UnitJoined(event, guid, unitid)
971 self:ScanUnitAuras(event, unitid, guid)
972end
973
974function GridStatusAuras:UpdateDispellable()
975 if PLAYER_CLASS == "DRUID" then
976 -- 88423 Nature's Cure Restoration Curse, Poison, Magic
977 -- 2782 Remove Corruption Balance, Feral, Guardian Curse, Poison
978 PlayerCanDispel.Curse = IsPlayerSpell(88423) or IsPlayerSpell(2782)
979 PlayerCanDispel.Magic = IsPlayerSpell(88423)
980 PlayerCanDispel.Poison = IsPlayerSpell(88423) or IsPlayerSpell(2782)
981
982 elseif PLAYER_CLASS == "MONK" then
983 -- 115450 Detox Mistweaver Disease, Poison, Magic
984 -- 218164 Detox Brewmaster, Windwalker Disease, Poison
985 PlayerCanDispel.Disease = IsPlayerSpell(115450) or IsPlayerSpell(218164)
986 PlayerCanDispel.Magic = IsPlayerSpell(115450)
987 PlayerCanDispel.Poison = IsPlayerSpell(115450) or IsPlayerSpell(218164)
988
989 elseif PLAYER_CLASS == "PALADIN" then
990 -- 4987 Cleanse Holy Disease, Poison, Magic
991 -- 213644 Cleanse Toxins Protection, Retribution Disease, Poison
992 PlayerCanDispel.Disease = IsPlayerSpell(4987) or IsPlayerSpell(213644)
993 PlayerCanDispel.Magic = IsPlayerSpell(4987)
994 PlayerCanDispel.Poison = IsPlayerSpell(4987) or IsPlayerSpell(213644)
995
996 elseif PLAYER_CLASS == "PRIEST" then
997 -- 527 Purify Discipline, Holy Disease, Magic
998 -- 213634 Purify Disease Shadow Disease
999 PlayerCanDispel.Disease = IsPlayerSpell(527) or IsPlayerSpell(213634)
1000 PlayerCanDispel.Magic = IsPlayerSpell(527)
1001
1002 elseif PLAYER_CLASS == "SHAMAN" then
1003 -- 77130 Purify Spirit Restoration Curse, Magic
1004 -- 51886 Cleanse Spirit Elemental, Enhancement Curse
1005 PlayerCanDispel.Curse = IsPlayerSpell(77130) or IsPlayerSpell(51886)
1006 PlayerCanDispel.Magic = IsPlayerSpell(77130)
1007
1008 elseif PLAYER_CLASS == "WARLOCK" then
1009 -- 115276 Sear Magic (Fel Imp)
1010 -- 89808 Singe Magic (Imp)
1011 PlayerCanDispel.Magic = IsPlayerSpell(115276, true) or IsPlayerSpell(89808, true)
1012 end
1013end
1014
1015-- Unit Aura Driver
1016--
1017-- Primary Requirements:
1018-- * Identify the presence of known buffs by name.
1019-- * Identify the presence of known buffs by name that are cast by the player.
1020-- * Identify the presence of known debuffs by name.
1021-- * Identify the presence of unknown debuffs by dispel type.
1022--
1023-- * The ability to filter all of the above by class.
1024--
1025-- Optional/Secondary Requirements:
1026-- * Identify the absence of known buffs by name.
1027-- * Identify the absence of known buffs by name that are cast by the player.
1028
1029-- Proposal:
1030-- * Iterate over known buff names and call UnitAura(unit, name, "HELPFUL") for
1031-- each one. It is likely that the list of buff names is shorter than the
1032-- number of buffs on the unit.
1033-- * Iterate over known buff names that are cast by the player and call
1034-- UnitAura(unit, name, "HELPFUL|PLAYER") for each one. It is likely that the
1035-- combined list of buff names and buff names that are cast by the player is
1036-- shorter than the number of buffs on the unit.
1037-- * Iterate over all debuffs on the unit by calling
1038-- UnitAura(unit, index, "HARMFUL"). It is likely that the list of debuffs is
1039-- longer than the number of debuffs on the unit. While scanning the debuffs
1040-- keep track of each debuff type seen and information about the last debuff
1041-- of that type seen.
1042
1043-- durationAuras[status][guid] = { <aura properties> }
1044GridStatusAuras.durationAuras = {}
1045GridStatusAuras.durationTimer = {
1046 timer = nil,
1047 refresh = nil,
1048 minRefresh = nil,
1049}
1050
1051local GetTime = GetTime
1052local now = GetTime()
1053
1054local ICON_TEX_COORDS = { left = 0.06, right = 0.94, top = 0.06, bottom = 0.94 }
1055
1056-- Simple resource pool implemented as a singly-linked list.
1057local Pool = {
1058 pool = nil,
1059 new = function(self, obj) -- create new Pool object
1060 obj = obj or {}
1061 setmetatable(obj, self)
1062 self.__index = self
1063 return obj
1064 end,
1065 get = function(self) -- get a cleaned item from the pool
1066 if not self.pool then self.pool = { nextPoolItem = self.pool } end
1067 local item = self.pool
1068 self.pool = self.pool.nextPoolItem
1069 item.nextPoolItem = nil
1070 if self.clean then
1071 self:clean(item)
1072 end
1073 return item
1074 end,
1075 put = function(self, item) -- put an item back into the pool; caller shall remove references to item
1076 item.nextPoolItem = self.pool
1077 self.pool = item
1078 end,
1079 clean = nil, -- called in Pool:new() to return a "cleaned" pool item
1080 empty = function(self) -- empty the pool
1081 while self.pool do
1082 local l = self.pool
1083 self.pool = self.pool.nextPoolItem
1084 l = nil
1085 end
1086 end,
1087}
1088
1089-- durationAuraPool is a Pool of tables used by durationAuras[status][guid]
1090local durationAuraPool = Pool:new(
1091 {
1092 clean = function(self, item)
1093 item.duration = nil
1094 item.expirationTime = nil
1095 end
1096 }
1097)
1098
1099function GridStatusAuras:UnitGainedDurationStatus(status, guid, class, name, rank, icon, count, debuffType, duration, expirationTime, caster, isStealable)
1100 local timer = self.durationTimer
1101 local settings = self.db.profile[status]
1102 if not settings then return end
1103
1104 if settings.enable and (settings.statusText == "duration" or settings.statusColor == "duration") then
1105 if not self.durationAuras[status] then
1106 self.durationAuras[status] = {}
1107 end
1108 if not self.durationAuras[status][guid] then
1109 self.durationAuras[status][guid] = durationAuraPool:get()
1110 end
1111 self.durationAuras[status][guid] = {
1112 class = class,
1113 rank = rank,
1114 icon = icon,
1115 count = count,
1116 debuffType = debuffType,
1117 duration = duration,
1118 expirationTime = expirationTime,
1119 caster = caster,
1120 isStealable = isStealable,
1121 }
1122 if not timer.minRefresh or settings.refresh < timer.minRefresh then
1123 timer.minRefresh = settings.refresh
1124 end
1125 else
1126 self:UnitLostDurationStatus(status, guid, class, name)
1127 end
1128end
1129
1130function GridStatusAuras:UnitLostDurationStatus(status, guid, class, name)
1131 local auras = self.durationAuras[status]
1132 if auras and auras[guid] then
1133 durationAuraPool:put(auras[guid])
1134 auras[guid] = nil
1135 end
1136end
1137
1138function GridStatusAuras:DeleteDurationStatus(status)
1139 local auras = self.durationAuras[status]
1140 if not auras then return end
1141 for guid in pairs(auras) do
1142 durationAuraPool:put(auras[guid])
1143 auras[guid] = nil
1144 end
1145 self.durationAuras[status] = nil
1146end
1147
1148function GridStatusAuras:ResetDurationStatuses()
1149 for status in pairs(self.durationAuras) do
1150 self:DeleteDurationStatus(status)
1151 end
1152 durationAuraPool:empty()
1153end
1154
1155function GridStatusAuras:HasActiveDurations()
1156 for status, auras in pairs(self.durationAuras) do
1157 for guid in pairs(auras) do
1158 return true
1159 end
1160 end
1161 return false
1162end
1163
1164function GridStatusAuras:ResetDurationTimer(hasActiveDurations)
1165 local timer = self.durationTimer
1166 if hasActiveDurations then
1167 if timer.timer and timer.refresh and timer.minRefresh ~= timer.refresh then
1168 self:Debug("ResetDurationTimer: cancel timer", timer.minRefresh, timer.refresh)
1169 self:CancelTimer(timer.timer, true)
1170 end
1171 timer.refresh = timer.minRefresh
1172 if not timer.timer then
1173 self:Debug("ResetDurationTimer: set timer", timer.refresh)
1174 timer.timer = self:ScheduleRepeatingTimer("RefreshActiveDurations", timer.refresh)
1175 end
1176 else
1177 if timer.timer then
1178 self:Debug("ResetDurationTimer: cancel timer")
1179 self:CancelTimer(timer.timer, true)
1180 end
1181 timer.timer = nil
1182 timer.refresh = nil
1183 end
1184end
1185
1186function GridStatusAuras:StatusTextColor(settings, count, timeLeft)
1187 local text, color
1188
1189 count = count or 0
1190 timeLeft = timeLeft or 0
1191
1192 if settings.statusText == "name" then
1193 text = settings.text
1194 elseif settings.statusText == "count" then
1195 text = tostring(count)
1196 elseif settings.statusText == "duration" then
1197 if settings.durationTenths then
1198 text = format("%.1f", timeLeft)
1199 else
1200 text = format("%d", timeLeft)
1201 end
1202 end
1203
1204 if settings.missing or settings.statusColor == "present" then
1205 color = settings.color
1206 elseif settings.statusColor == "duration" then
1207 if timeLeft <= settings.durationLow then
1208 color = settings.durationColorLow
1209 elseif timeLeft <= settings.durationHigh then
1210 color = settings.durationColorMiddle
1211 else
1212 color = settings.durationColorHigh
1213 end
1214 elseif settings.statusColor == "count" then
1215 if count <= settings.countLow then
1216 color = settings.countColorLow
1217 elseif count <= settings.countHigh then
1218 color = settings.countColorMiddle
1219 else
1220 color = settings.countColorHigh
1221 end
1222 end
1223
1224 return text, color
1225end
1226
1227function GridStatusAuras:RefreshActiveDurations()
1228 now = GetTime()
1229
1230 self:Debug("RefreshActiveDurations", now)
1231
1232 for status, guids in pairs(self.durationAuras) do
1233 local settings = self.db.profile[status]
1234 if settings and settings.enable and not settings.missing then -- and settings[class] ~= false then -- ##DELETE
1235 for guid, aura in pairs(guids) do
1236 local count, duration, expirationTime, icon = aura.count, aura.duration, aura.expirationTime, aura.icon
1237 local start = expirationTime and (expirationTime - duration)
1238 local timeLeft = expirationTime and expirationTime > now and (expirationTime - now) or 0
1239 local text, color = self:StatusTextColor(settings, count, timeLeft)
1240 self.core:SendStatusGained(guid,
1241 status,
1242 settings.priority,
1243 nil,
1244 color,
1245 text,
1246 count,
1247 nil,
1248 icon,
1249 start,
1250 duration,
1251 count,
1252 ICON_TEX_COORDS)
1253 end
1254 -- else
1255 -- self.core:SendStatusLost(guid, status) -- XXX "guid" is undefined=nil here; what is the purpose?!
1256 end
1257 end
1258end
1259
1260function GridStatusAuras:UnitGainedBuff(guid, class, name, rank, icon, count, debuffType, duration, expirationTime, caster, isStealable)
1261 self:Debug("UnitGainedBuff", guid, class, name)
1262
1263 local status = buff_names[name]
1264 local settings = status and self.db.profile[status]
1265 if not settings then return end
1266
1267 settings.icon = icon
1268
1269 if settings.enable and not settings.missing then -- and settings[class] ~= false then -- ##DELETE
1270 local start = expirationTime and (expirationTime - duration)
1271 local timeLeft = expirationTime and expirationTime > now and (expirationTime - now) or 0
1272 local text, color = self:StatusTextColor(settings, count, timeLeft)
1273 if duration and expirationTime and duration > 0 and expirationTime > 0 then
1274 self:UnitGainedDurationStatus(status, guid, class, name, rank, icon, count, debuffType, duration, expirationTime, caster, isStealable)
1275 end
1276 self.core:SendStatusGained(guid,
1277 status,
1278 settings.priority,
1279 nil,
1280 color,
1281 text,
1282 count,
1283 nil,
1284 icon,
1285 start,
1286 duration,
1287 count,
1288 ICON_TEX_COORDS)
1289 else
1290 self.core:SendStatusLost(guid, status)
1291 end
1292end
1293
1294function GridStatusAuras:UnitLostBuff(guid, class, name)
1295 --self:Debug("UnitLostBuff", guid, class, name)
1296
1297 local status = buff_names[name]
1298 local settings = self.db.profile[status]
1299 if not settings then return end
1300
1301 if settings.enable and settings.missing then -- and settings[class] ~= false then -- ##DELETE
1302 local text, color = self:StatusTextColor(settings, 0, 0)
1303 self:UnitLostDurationStatus(status, guid, class, name)
1304 self.core:SendStatusGained(guid,
1305 status,
1306 settings.priority,
1307 nil,
1308 color,
1309 text,
1310 nil,
1311 nil,
1312 settings.icon,
1313 nil,
1314 nil,
1315 nil,
1316 ICON_TEX_COORDS)
1317 else
1318 self.core:SendStatusLost(guid, status)
1319 end
1320end
1321
1322function GridStatusAuras:UnitGainedPlayerBuff(guid, class, name, rank, icon, count, debuffType, duration, expirationTime, caster, isStealable)
1323 self:Debug("UnitGainedPlayerBuff", guid, name)
1324
1325 local status = player_buff_names[name]
1326 local settings = self.db.profile[status]
1327 if not settings then return end
1328
1329 settings.icon = icon
1330
1331 if settings.enable and not settings.missing then -- and settings[class] ~= false then -- ##DELETE
1332 local start = expirationTime and (expirationTime - duration)
1333 local timeLeft = expirationTime and expirationTime > now and (expirationTime - now) or 0
1334 local text, color = self:StatusTextColor(settings, count, timeLeft)
1335 if duration and expirationTime and duration > 0 and expirationTime > 0 then
1336 self:UnitGainedDurationStatus(status, guid, class, name, rank, icon, count, debuffType, duration, expirationTime, caster, isStealable)
1337 end
1338 self.core:SendStatusGained(guid,
1339 status,
1340 settings.priority,
1341 nil,
1342 color,
1343 text,
1344 count,
1345 nil,
1346 icon,
1347 start,
1348 duration,
1349 count,
1350 ICON_TEX_COORDS)
1351 else
1352 self.core:SendStatusLost(guid, status)
1353 end
1354end
1355
1356function GridStatusAuras:UnitLostPlayerBuff(guid, class, name)
1357 --self:Debug("UnitLostPlayerBuff", guid, name)
1358
1359 local status = player_buff_names[name]
1360 local settings = self.db.profile[status]
1361 if not settings then return end
1362
1363 if settings.enable and settings.missing then -- and settings[class] ~= false then -- ##DELETE
1364 local text, color = self:StatusTextColor(settings, 0, 0)
1365 self:UnitLostDurationStatus(status, guid, class, name)
1366 self.core:SendStatusGained(guid,
1367 status,
1368 settings.priority,
1369 nil,
1370 color,
1371 text,
1372 nil,
1373 nil,
1374 settings.icon,
1375 nil,
1376 nil,
1377 nil,
1378 ICON_TEX_COORDS)
1379 else
1380 self.core:SendStatusLost(guid, status)
1381 end
1382end
1383
1384function GridStatusAuras:UnitGainedDebuff(guid, class, name, rank, icon, count, debuffType, duration, expirationTime, casterUnit, canStealOrPurge, shouldConsolidate, spellID, canApply, isBossAura, isCastByPlayer)
1385 self:Debug("UnitGainedDebuff", guid, class, name)
1386
1387 local status = debuff_names[name]
1388 local settings = self.db.profile[status]
1389 if not settings then return end
1390
1391 if settings.enable then -- and settings[class] ~= false then -- ##DELETE
1392 local start = expirationTime and (expirationTime - duration)
1393 local timeLeft = expirationTime and expirationTime > now and (expirationTime - now) or 0
1394 local text, color = self:StatusTextColor(settings, count, timeLeft)
1395 if duration and expirationTime and duration > 0 and expirationTime > 0 then
1396 self:UnitGainedDurationStatus(status, guid, class, name, rank, icon, count, debuffType, duration, expirationTime, casterUnit, canStealOrPurge, shouldConsolidate, spellID, canApply, isBossAura, isCastByPlayer)
1397 end
1398 self.core:SendStatusGained(guid,
1399 status,
1400 settings.priority,
1401 nil,
1402 color,
1403 text,
1404 count,
1405 nil,
1406 icon,
1407 start,
1408 duration,
1409 count,
1410 ICON_TEX_COORDS)
1411 else
1412 self.core:SendStatusLost(guid, status)
1413 end
1414end
1415
1416function GridStatusAuras:UnitLostDebuff(guid, class, name)
1417 --self:Debug("UnitLostDebuff", guid, class, name)
1418 local status = debuff_names[name]
1419 local settings = self.db.profile[status]
1420 if not settings then return end
1421
1422 self:UnitLostDurationStatus(status, guid, class, name)
1423 self.core:SendStatusLost(guid, status)
1424end
1425
1426function GridStatusAuras:UnitGainedDebuffType(guid, class, name, rank, icon, count, debuffType, duration, expirationTime, casterUnit, canStealOrPurge, shouldConsolidate, spellID, canApply, isBossAura, isCastByPlayer)
1427 self:Debug("UnitGainedDebuffType", guid, class, debuffType)
1428
1429 local status = debuffType and debuff_types[debuffType]
1430 local settings = self.db.profile[status]
1431 if not settings then return end
1432
1433 if settings.enable and (PlayerCanDispel[debuffType] or not settings.dispellable) then -- and settings[class] ~= false then -- ##DELETE
1434 local start = expirationTime and (expirationTime - duration)
1435 local timeLeft = expirationTime and expirationTime > now and (expirationTime - now) or 0
1436 local text, color = self:StatusTextColor(settings, count, timeLeft)
1437 if duration and expirationTime and duration > 0 and expirationTime > 0 then
1438 self:UnitGainedDurationStatus(status, guid, class, name, rank, icon, count, debuffType, duration, expirationTime, casterUnit, canStealOrPurge, shouldConsolidate, spellID, canApply, isBossAura, isCastByPlayer)
1439 end
1440 self.core:SendStatusGained(guid,
1441 status,
1442 settings.priority,
1443 nil,
1444 color,
1445 text,
1446 count,
1447 nil,
1448 icon,
1449 start,
1450 duration,
1451 count,
1452 ICON_TEX_COORDS)
1453 else
1454 self.core:SendStatusLost(guid, status)
1455 end
1456end
1457
1458function GridStatusAuras:UnitLostDebuffType(guid, class, debuffType)
1459 --self:Debug("UnitLostDebuffType", guid, class, debuffType)
1460
1461 local status = debuffType and debuff_types[debuffType]
1462 local settings = self.db.profile[status]
1463 if not settings then return end
1464
1465 self:UnitLostDurationStatus(status, guid, class, debuffType)
1466 self.core:SendStatusLost(guid, status)
1467end
1468
1469function GridStatusAuras:UnitGainedBossDebuff(guid, class, name, rank, icon, count, debuffType, duration, expirationTime, casterUnit, canStealOrPurge, shouldConsolidate, spellID, canApply, isBossAura, isCastByPlayer)
1470 local status = "boss_aura"
1471 local settings = self.db.profile[status]
1472 if settings.enable then
1473 local start = expirationTime and (expirationTime - duration)
1474 local timeLeft = expirationTime and expirationTime > now and (expirationTime - now) or 0
1475 local text, color = self:StatusTextColor(settings, count, timeLeft)
1476 if duration and expirationTime and duration > 0 and expirationTime > 0 then
1477 self:UnitGainedDurationStatus(status, guid, class, name, rank, icon, count, debuffType, duration, expirationTime, casterUnit, canStealOrPurge, shouldConsolidate, spellID, canApply, isBossAura, isCastByPlayer)
1478 end
1479 self.core:SendStatusGained(guid,
1480 status,
1481 settings.priority,
1482 nil,
1483 color,
1484 text,
1485 count,
1486 nil,
1487 icon,
1488 start,
1489 duration,
1490 count,
1491 ICON_TEX_COORDS)
1492 else
1493 self.core:SendStatusLost(guid, status)
1494 end
1495end
1496
1497function GridStatusAuras:UnitLostBossDebuff(guid, class, name)
1498 --self:Debug("UnitLostBossDebuff", guid, class, name)
1499 local status = "boss_aura"
1500
1501 self:UnitLostDurationStatus(status, guid, class, name)
1502 self.core:SendStatusLost(guid, status)
1503end
1504
1505function GridStatusAuras:UpdateAuraScanList()
1506 self:Debug("UpdateAuraScanList")
1507
1508 wipe(buff_names)
1509 wipe(player_buff_names)
1510 wipe(debuff_names)
1511
1512 for status, settings in pairs(self.db.profile) do
1513 if type(settings) == "table" and settings.enable then
1514 local name = settings.buff or settings.debuff
1515 self:Debug(status, name)
1516
1517 if name and not debuff_types[name] then
1518 local isBuff = not not settings.buff
1519
1520 if isBuff then
1521 if settings.mine then
1522 self:Debug("Added to player_buff_names")
1523 player_buff_names[name] = status
1524 else
1525 self:Debug("Added to buff_names")
1526 buff_names[name] = status
1527 end
1528 else
1529 self:Debug("Added to debuff_names")
1530 debuff_names[name] = status
1531 end
1532 end
1533 end
1534 end
1535end
1536
1537-- temp tables
1538local buff_names_seen = {}
1539local player_buff_names_seen = {}
1540local debuff_names_seen = {}
1541local debuff_types_seen = {}
1542
1543function GridStatusAuras:ScanUnitAuras(event, unit, guid)
1544 if not guid then guid = UnitGUID(unit) end
1545 if not GridRoster:IsGUIDInRaid(guid) then
1546 return
1547 end
1548
1549 local seenBossAura
1550
1551 local _, class
1552 --[[ -- ##DELETE
1553 if UnitIsPlayer(unit) then
1554 _, class = UnitClass(unit)
1555 if class then
1556 class = class:lower()
1557 end
1558 else
1559 class = "pet" -- should catch pets, vehicles, and anything else
1560 end
1561 ]]
1562
1563 self:Debug("UNIT_AURA", unit, guid)
1564
1565 now = GetTime()
1566
1567 for status, auras in pairs(self.durationAuras) do
1568 if auras[guid] then
1569 durationAuraPool:put(auras[guid])
1570 auras[guid] = nil
1571 end
1572 end
1573
1574 if UnitIsVisible(unit) then
1575 -- scan for buffs
1576 for buff_name in pairs(buff_names) do
1577 local name, rank, icon, count, debuffType, duration, expirationTime, caster, isStealable = UnitAura(unit, buff_name, nil, "HELPFUL")
1578 if name then
1579 buff_names_seen[name] = true
1580 self:UnitGainedBuff(guid, class, name, rank, icon, count, debuffType, duration, expirationTime, caster, isStealable)
1581 end
1582 end
1583
1584 -- scan for buffs cast by the player
1585 for buff_name in pairs(player_buff_names) do
1586 local name, rank, icon, count, debuffType, duration, expirationTime, caster, isStealable = UnitAura(unit, buff_name, nil, "HELPFUL|PLAYER")
1587 if name then
1588 player_buff_names_seen[name] = true
1589 self:UnitGainedPlayerBuff(guid, class, name, rank, icon, count, debuffType, duration, expirationTime, caster, isStealable)
1590 end
1591 end
1592
1593 -- scan for debuffs
1594 for index = 1, 40 do
1595 local name, rank, icon, count, debuffType, duration, expirationTime, casterUnit, canStealOrPurge, shouldConsolidate, spellID, canApply, isBossAura, isCastByPlayer = UnitAura(unit, index, "HARMFUL")
1596 if not name then
1597 break
1598 end
1599 if debuff_names[name] then
1600 debuff_names_seen[name] = true
1601 self:UnitGainedDebuff(guid, class, name, rank, icon, count, debuffType, duration, expirationTime, casterUnit, canStealOrPurge, shouldConsolidate, spellID, canApply, isBossAura, isCastByPlayer)
1602 --[[
1603 elseif isBossAura then
1604 seenBossAura = true
1605 self:UnitGainedBossDebuff(guid, class, name, rank, icon, count, debuffType, duration, expirationTime, casterUnit, canStealOrPurge, shouldConsolidate, spellID, canApply, isBossAura, isCastByPlayer)
1606 ]]
1607 elseif debuff_types[debuffType] then
1608 -- elseif so that a named debuff doesn't trigger the type status
1609 debuff_types_seen[debuffType] = true
1610 self:UnitGainedDebuffType(guid, class, name, rank, icon, count, debuffType, duration, expirationTime, casterUnit, canStealOrPurge, shouldConsolidate, spellID, canApply, isBossAura, isCastByPlayer)
1611 end
1612 end
1613 end
1614
1615 -- handle lost buffs
1616 for name in pairs(buff_names) do
1617 if not buff_names_seen[name] then
1618 self:UnitLostBuff(guid, class, name)
1619 else
1620 buff_names_seen[name] = nil
1621 end
1622 end
1623
1624 for name in pairs(player_buff_names) do
1625 if not player_buff_names_seen[name] then
1626 self:UnitLostPlayerBuff(guid, class, name)
1627 else
1628 player_buff_names_seen[name] = nil
1629 end
1630 end
1631
1632 -- handle lost debuffs
1633 for name in pairs(debuff_names) do
1634 if not debuff_names_seen[name] then
1635 self:UnitLostDebuff(guid, class, name)
1636 else
1637 debuff_names_seen[name] = nil
1638 end
1639 end
1640
1641 for debuffType in pairs(debuff_types) do
1642 if not debuff_types_seen[debuffType] then
1643 self:UnitLostDebuffType(guid, class, debuffType)
1644 else
1645 debuff_types_seen[debuffType] = nil
1646 end
1647 end
1648--[[
1649 if not seenBossAura then
1650 self:UnitLostBossDebuff(guid, class)
1651 end
1652]]
1653 self:ResetDurationTimer(self:HasActiveDurations())
1654end