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