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