8b1be3a - More auras and stagger tracking
[wowui.git] / OmaTMW / TellMeWhen.lua
1 -- TellMeWhen.lua
2 local _;
3 local pairs = pairs;
4 local GetSpecialization = GetSpecialization;
5 local UnitExists = UnitExists;
6 local UnitAura = UnitAura;
7 local GetTotemInfo = GetTotemInfo;
8
9 -- character specific frames
10 -- TODO try with auraFilter, remove characters, change to classes
11 --[[local classes = {
12     ["PALADIN"] = {
13         ["PLAYER HELPFUL"] = {
14             {
15                 unit = "player",
16                 auras = {["Divine Shield"]=1, ""},
17                 frames = {
18                     [1] = {
19                         x = 0,
20                         y = 0,
21                         width = 1,
22                         height = 1,
23                     },
24                 },
25             },
26         },
27     },
28 };
29 local specs = {
30     [retSpecid] = {
31         ["PLAYER HELPFUL"] = {
32             {
33                 unit = "player",
34                 auras = {["Judgment"]=1},
35                 frames = {
36                     [1] = {
37                     },
38                 },
39             },
40         },
41         ["PLAYER HARMFUL"] = {
42             {
43             },
44         },
45     },
46 };
47 --]]
48 local chars = {
49     ["Sylvanas"] = {
50         ["Vildana"] = {
51             {
52                 unit = "target",
53                 spec = 3, -- Retribution
54                 auras = {"Judgment"},
55                 auraFilter = "PLAYER HARMFUL",
56                 x = 570, -- placed over Innervate frame
57                 y = 440,
58                 width = 80,
59                 height = 80,
60             },
61             {
62                 unit = "player",
63                 spec = 3, -- Retribution
64                 auras = {"Divine Purpose"},
65                 auraFilter = "PLAYER HELPFUL",
66                 x = 570,
67                 y = 530,
68                 width = 40,
69                 height = 40,
70             },
71             {
72                 unit = "player",
73                 spec = 2, -- Protection
74                 auras = {"Shield of the Righteous"},
75                 auraFilter = "PLAYER HELPFUL",
76                 x = 570,
77                 y = 440,
78                 width = 80,
79                 height = 80,
80             },
81             {
82                 unit = "player",
83                 auras = {"Divine Shield"},
84                 auraFilter = "PLAYER HELPFUL",
85                 x = 660,
86                 y = 440,
87                 width = 80,
88                 height = 80,
89             },
90         },
91     },
92     ["Stormreaver"] = {
93         ["Vildan"] = {
94             {
95                 unit = "target",
96                 auras = {"Bursting With Pride"},
97                 auraFilter = "HELPFUL",
98                 x = 570, -- placed over Innervate frame
99                 y = 440,
100                 width = 80,
101                 height = 80,
102             },
103             {
104                 unit = "player",
105                 auras = {"Divine Purpose"},
106                 auraFilter = "PLAYER HELPFUL",
107                 x = 570,
108                 y = 530,
109                 width = 40,
110                 height = 40,
111             },
112             {
113                 unit = "player",
114                 spec = 2, -- Protection
115                 auras = {"Shield of the Righteous"},
116                 auraFilter = "PLAYER HELPFUL",
117                 x = 570,
118                 y = 440,
119                 width = 80,
120                 height = 80,
121             },
122             {
123                 unit = "player",
124                 auras = {"Seraphim", "Righteous Verdict"},
125                 auraFilter = "PLAYER HELPFUL",
126                 x = 570,
127                 y = 480,
128                 width = 40,
129                 height = 40,
130             },
131             {
132                 unit = "player",
133                 auras = {"Divine Shield"},
134                 auraFilter = "PLAYER HELPFUL",
135                 x = 660,
136                 y = 440,
137                 width = 80,
138                 height = 80,
139             },
140             {
141                 unit = "player",
142                 auras = {"Devotion Aura"},
143                 auraFilter = "HELPFUL",
144                 invert = true,
145                 x = 720,
146                 y = 440,
147                 width = 60,
148                 height = 60,
149             },
150         },
151         ["Gilden"] = {
152             {
153                 unit = "target",
154                 auras = {"Shadow Word: Pain", "Purge the Wicked"},
155                 auraFilter = "PLAYER HARMFUL",
156                 x = 660,
157                 y = 440,
158                 width = 60,
159                 height = 60,
160             },
161             {
162                 unit = "player",
163                 auras = {"Power Word: Fortitude"},
164                 auraFilter = "HELPFUL",
165                 invert = true,
166                 x = 720,
167                 y = 440,
168                 width = 60,
169                 height = 60,
170             },
171             {
172                 unit = "player",
173                 auras = {"Overcharge Mana"},
174                 auraFilter = "PLAYER HELPFUL",
175                 x = 720,
176                 y = 440,
177                 width = 60,
178                 height = 60,
179             },
180         },
181         ["Gedren"] = {
182             {
183                 totems = {1}, -- Efflorescence
184                 x = 660,
185                 y = 440,
186                 width = 80,
187                 height = 80,
188             },
189         },
190         ["Gazden"] = {
191             {
192                 unit = "player",
193                 auras = {"Tidal Waves"},
194                 auraFilter = "PLAYER HELPFUL",
195                 x = 660,
196                 y = 440,
197                 width = 40,
198                 height = 40,
199             },
200             {
201                 unit = "player",
202                 auras = {"Healing Rain"},
203                 auraFilter = "PLAYER HELPFUL",
204                 x = 700,
205                 y = 440,
206                 width = 40,
207                 height = 40,
208             },
209             {
210                 unit = "player",
211                 auras = {"Unleash Life"},
212                 auraFilter = "PLAYER HELPFUL",
213                 x = 740,
214                 y = 440,
215                 width = 40,
216                 height = 40,
217             },
218         },
219         ["Gran"] = {
220             {
221                 unit = "player",
222                 auras = {"Tidal Waves"},
223                 auraFilter = "PLAYER HELPFUL",
224                 x = 660,
225                 y = 440,
226                 width = 40,
227                 height = 40,
228             },
229             {
230                 unit = "player",
231                 auras = {"Healing Rain"},
232                 auraFilter = "PLAYER HELPFUL",
233                 x = 700,
234                 y = 440,
235                 width = 40,
236                 height = 40,
237             },
238             {
239                 unit = "player",
240                 auras = {"Unleash Life"},
241                 auraFilter = "PLAYER HELPFUL",
242                 x = 740,
243                 y = 440,
244                 width = 40,
245                 height = 40,
246             },
247         },
248         ["Gedrin"] = {
249             {
250                 unit = "pet",
251                 auras = {"Frenzy"},
252                 auraFilter = "HELPFUL",
253                 x = 660,
254                 y = 440,
255                 width = 40,
256                 height = 40,
257             },
258         },
259     },
260 };
261
262 -- settings entry:
263 -- unit = unitID where to check auras, not required for totem checkers
264 -- spec = player spec index to show frame, if nil show always
265 -- auras = list of auras to track, in priority order
266 -- auraFilter = filter for UnitAura
267 -- totems = list of totem slots to track, in priority order, only checked if auras is nil
268 -- x = x position relative to UIParent bottom left
269 -- y = y position relative to UIParent bottom left
270 -- width = width
271 -- height = height
272
273 -- global frames' settings
274 local settings = {
275     {
276         unit = "player",
277         auras = {"Innervate", "Gift of the Titans", "Power Infusion"},
278         auraFilter = "HELPFUL",
279         x = 570,
280         y = 440,
281         width = 80,
282         height = 80,
283     },
284     {
285         unit = "player",
286         auras = {"Ineffable Truth"},
287         auraFilter = "HELPFUL",
288         x = 570,
289         y = 440,
290         width = 40,
291         height = 40,
292     },
293     {
294         unit = "player",
295         auras = {
296             "Delusions", "Entropic Blast", "Necrotic Embrace", "Flametouched", "Shadowtouched",
297             "Blazing Eruption", "Shattering Scream", "Consuming Hunger", "Unstable Soul",
298             "Time Bomb", "Broken Shard", "Demolished", "Fetid Rot", "Roiling Deceit",
299             "Putrid Blood", "Endemic Virus", "Lingering Infection", "Gigavolt Charge",
300             "Crackling Lightning", "Storm's Wail", "Death's Door", "Deathly Withering",
301             "Chilling Touch", "Volatile Charge", "Liquid Gold", "Drained Soul", "Evoke Anguish",
302             "Ancient Curse", "Corrosion", "Debilitating Spit", "Tasty Morsel", "Encroaching Shadows",
303             "Corrupted Existence", "Madness Bomb", "Crimson Chorus", "Essence Sap", "Bloodlight",
304             "Arcane Vulnerability", "Wicked Laceration", "Withering Touch"
305         },
306         auraFilter = "HARMFUL",
307         x = 660,
308         y = 530,
309         width = 80,
310         height = 80,
311     },
312     {
313         unit = "player",
314         auras = {
315             "Adaptive Membrane", "Gluttonous Miasma"
316         },
317         auraFilter = "HARMFUL",
318         x = 660,
319         y = 780,
320         width = 160,
321         height = 160,
322     },
323 };
324
325 local frames = {};
326 local units = {}; -- mapping from unitID to frames
327 local totems = {}; -- mapping to frames with totems
328 local bosses = {};
329 for i = 1, MAX_BOSS_FRAMES do
330     bosses[i] = "boss"..i;
331 end
332 local currentSpec = 0; -- 0 is invalid
333
334 local Indicators = CreateFrame("Frame", "OmaTMW", UIParent);
335
336 local function updateAuraFrame(frame)
337     local unit = frame.unit;
338     if UnitExists(unit) and (not frame.spec or frame.spec == currentSpec) then
339         local name, icon, count, duration, expires;
340         local auraFilter = frame.auraFilter;
341         local i = 1;
342         while true do
343             name, icon, count, _, duration, expires = UnitAura(unit, i, auraFilter);
344             if not name then break end
345             -- possible improvement to add spellID as an option
346             if frame.auras[name] and not frame.invert then
347                 if count > 1 then
348                     frame.stack:SetText(count);
349                     frame.stack:Show();
350                 else
351                     frame.stack:Hide();
352                 end
353                 if expires > 0 then
354                     frame.cd:SetCooldown(expires - duration, duration);
355                     frame.cd:Show();
356                 else
357                     frame.cd:Hide();
358                 end
359                 frame.icon:SetTexture(icon);
360                 frame:Show();
361                 return;
362             elseif frame.auras[name] and frame.invert then
363                 frame.icon:SetTexture(icon);
364                 frame:Hide();
365                 return;
366             end
367             i = i + 1;
368         end
369         if frame.invert then
370             frame:Show(); -- not functional without first hiding once to get SetTexture
371             return;
372         end
373     end
374     frame:Hide();
375 end
376
377 local function updateAuras(unit)
378     if units[unit] then
379         for _, i in pairs(units[unit]) do
380             updateAuraFrame(frames[i]);
381         end
382     end
383 end
384
385 local function updateTotemFrame(frame, slot)
386     local _, name, start, duration, icon = GetTotemInfo(slot);
387     if name ~= "" then
388         frame.cd:SetCooldown(start, duration);
389         frame.cd:Show();
390         frame.icon:SetTexture(icon);
391         frame:Show();
392     else
393         frame:Hide();
394     end
395 end
396
397 local function updateTotems(slot)
398     if totems[slot] then
399         for _, i in pairs(totems[slot]) do
400             updateTotemFrame(frames[i], slot);
401         end
402     end
403 end
404
405 local function createTMW(name, config, parent)
406     local frame = CreateFrame("Frame", name, parent);
407     frame:SetPoint("TOPLEFT", parent, "BOTTOMLEFT", config.x, config.y+config.height);
408     frame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", config.x+config.width, config.y);
409     frame.unit = config.unit;
410     frame.spec = config.spec;
411     if config.auras then
412         frame.auras = {};
413         for _, v in pairs(config.auras) do
414             frame.auras[v] = true;
415         end
416     end
417     frame.auraFilter = config.auraFilter;
418     frame.totems = config.totems;
419     frame.invert = config.invert;
420     frame:Hide();
421     frame.base = frame:CreateTexture(nil, "BACKGROUND");
422     frame.base:SetAllPoints();
423     frame.base:SetColorTexture(0, 0, 0, 0.6);
424     frame.icon = frame:CreateTexture(nil, "ARTWORK");
425     frame.icon:SetPoint("TOPLEFT", frame.base, "TOPLEFT", 1, -1);
426     frame.icon:SetPoint("BOTTOMRIGHT", frame.base, "BOTTOMRIGHT", -1, 1);
427     frame.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93);
428     frame.stack = frame:CreateFontString(nil, "OVERLAY", "NumberFontNormalLarge");
429     frame.stack:SetFont(STANDARD_TEXT_FONT, 26, "OUTLINE");
430     frame.stack:SetPoint("TOPLEFT");
431     frame.stack:Hide();
432     frame.cd = CreateFrame("Cooldown", name.."CD", frame, "CooldownFrameTemplate");
433     frame.cd:SetReverse(true);
434     frame.cd:SetAllPoints();
435     return frame;
436 end
437
438 local function initialize()
439     Indicators:SetFrameStrata("LOW");
440     Indicators:SetPoint("BOTTOMLEFT");
441     Indicators:SetWidth(1);
442     Indicators:SetHeight(1);
443     currentSpec = GetSpecialization();
444     local name, realm = UnitFullName("player");
445     if chars[realm] and chars[realm][name] then
446         for _, config in pairs(chars[realm][name]) do
447             table.insert(settings, config)
448         end
449     end
450     for i, config in pairs(settings) do
451         if config.unit then
452             if not units[config.unit] then units[config.unit] = {} end
453             table.insert(units[config.unit], i);
454         end
455         if config.totems then
456             for _, slot in pairs(config.totems) do
457                 if not totems[slot] then totems[slot] = {} end
458                 table.insert(totems[slot], i);
459             end
460         end
461         frames[i] = createTMW("OmaTMW"..i, config, Indicators);
462     end
463
464     for _, frame in pairs(frames) do
465         if frame.auras then updateAuraFrame(frame) end
466         if frame.totems then
467             for _, slot in pairs(frame.totems) do updateTotemFrame(frame, slot) end
468         end
469     end
470 end
471
472 Indicators:RegisterEvent("UNIT_AURA");
473 Indicators:RegisterEvent("PLAYER_TARGET_CHANGED");
474 Indicators:RegisterEvent("PLAYER_TOTEM_UPDATE");
475 Indicators:RegisterEvent("INSTANCE_ENCOUNTER_ENGAGE_UNIT");
476 Indicators:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED");
477 Indicators:RegisterEvent("PLAYER_LOGIN");
478 Indicators:SetScript("OnEvent", function(self, event, arg1)
479     if event == "UNIT_AURA" then
480         updateAuras(arg1);
481     elseif event == "PLAYER_TARGET_CHANGED" then
482         updateAuras("target");
483     elseif event == "PLAYER_TOTEM_UPDATE" then
484         updateTotems(arg1);
485     elseif event == "INSTANCE_ENCOUNTER_ENGAGE_UNIT" then
486         for _, boss in pairs(bosses) do
487             updateAuras(boss);
488         end
489     elseif event == "PLAYER_SPECIALIZATION_CHANGED" then
490         currentSpec = GetSpecialization();
491         for _, frame in pairs(frames) do
492             if frame.auras then updateAuraFrame(frame) end
493             if frame.totems then
494                 for _, slot in pairs(frame.totems) do updateTotemFrame(frame, slot) end
495             end
496         end
497     elseif event == "PLAYER_LOGIN" then
498         initialize();
499     end
500 end);