1b92b724297e333c485e90b81fb1912399b3aa0c
[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"},
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"
296         },
297         auraFilter = "HARMFUL",
298         x = 660,
299         y = 530,
300         width = 80,
301         height = 80,
302     },
303     {
304         unit = "player",
305         auras = {
306             "Adaptive Membrane"
307         },
308         auraFilter = "HARMFUL",
309         x = 660,
310         y = 780,
311         width = 160,
312         height = 160,
313     },
314 };
315
316 local frames = {};
317 local units = {}; -- mapping from unitID to frames
318 local totems = {}; -- mapping to frames with totems
319 local bosses = {};
320 for i = 1, MAX_BOSS_FRAMES do
321     bosses[i] = "boss"..i;
322 end
323 local currentSpec = 0; -- 0 is invalid
324
325 local Indicators = CreateFrame("Frame", "OmaTMW", UIParent);
326
327 local function updateAuraFrame(frame)
328     local unit = frame.unit;
329     if UnitExists(unit) and (not frame.spec or frame.spec == currentSpec) then
330         local name, icon, count, duration, expires;
331         local auraFilter = frame.auraFilter;
332         local i = 1;
333         while true do
334             name, icon, count, _, duration, expires = UnitAura(unit, i, auraFilter);
335             if not name then break end
336             -- possible improvement to add spellID as an option
337             if frame.auras[name] and not frame.invert then
338                 if count > 1 then
339                     frame.stack:SetText(count);
340                     frame.stack:Show();
341                 else
342                     frame.stack:Hide();
343                 end
344                 if expires > 0 then
345                     frame.cd:SetCooldown(expires - duration, duration);
346                     frame.cd:Show();
347                 else
348                     frame.cd:Hide();
349                 end
350                 frame.icon:SetTexture(icon);
351                 frame:Show();
352                 return;
353             elseif frame.auras[name] and frame.invert then
354                 frame.icon:SetTexture(icon);
355                 frame:Hide();
356                 return;
357             end
358             i = i + 1;
359         end
360         if frame.invert then
361             frame:Show(); -- not functional without first hiding once to get SetTexture
362             return;
363         end
364     end
365     frame:Hide();
366 end
367
368 local function updateAuras(unit)
369     if units[unit] then
370         for _, i in pairs(units[unit]) do
371             updateAuraFrame(frames[i]);
372         end
373     end
374 end
375
376 local function updateTotemFrame(frame, slot)
377     local _, name, start, duration, icon = GetTotemInfo(slot);
378     if name ~= "" then
379         frame.cd:SetCooldown(start, duration);
380         frame.cd:Show();
381         frame.icon:SetTexture(icon);
382         frame:Show();
383     else
384         frame:Hide();
385     end
386 end
387
388 local function updateTotems(slot)
389     if totems[slot] then
390         for _, i in pairs(totems[slot]) do
391             updateTotemFrame(frames[i], slot);
392         end
393     end
394 end
395
396 local function createTMW(name, config, parent)
397     local frame = CreateFrame("Frame", name, parent);
398     frame:SetPoint("TOPLEFT", parent, "BOTTOMLEFT", config.x, config.y+config.height);
399     frame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", config.x+config.width, config.y);
400     frame.unit = config.unit;
401     frame.spec = config.spec;
402     if config.auras then
403         frame.auras = {};
404         for _, v in pairs(config.auras) do
405             frame.auras[v] = true;
406         end
407     end
408     frame.auraFilter = config.auraFilter;
409     frame.totems = config.totems;
410     frame.invert = config.invert;
411     frame:Hide();
412     frame.base = frame:CreateTexture(nil, "BACKGROUND");
413     frame.base:SetAllPoints();
414     frame.base:SetColorTexture(0, 0, 0, 0.6);
415     frame.icon = frame:CreateTexture(nil, "ARTWORK");
416     frame.icon:SetPoint("TOPLEFT", frame.base, "TOPLEFT", 1, -1);
417     frame.icon:SetPoint("BOTTOMRIGHT", frame.base, "BOTTOMRIGHT", -1, 1);
418     frame.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93);
419     frame.stack = frame:CreateFontString(nil, "OVERLAY", "NumberFontNormalLarge");
420     frame.stack:SetPoint("TOPLEFT");
421     frame.stack:Hide();
422     frame.cd = CreateFrame("Cooldown", name.."CD", frame, "CooldownFrameTemplate");
423     frame.cd:SetReverse(true);
424     frame.cd:SetAllPoints();
425     return frame;
426 end
427
428 local function initialize()
429     Indicators:SetFrameStrata("LOW");
430     Indicators:SetPoint("BOTTOMLEFT");
431     Indicators:SetWidth(1);
432     Indicators:SetHeight(1);
433     currentSpec = GetSpecialization();
434     local name, realm = UnitFullName("player");
435     if chars[realm] and chars[realm][name] then
436         for _, config in pairs(chars[realm][name]) do
437             table.insert(settings, config)
438         end
439     end
440     for i, config in pairs(settings) do
441         if config.unit then
442             if not units[config.unit] then units[config.unit] = {} end
443             table.insert(units[config.unit], i);
444         end
445         if config.totems then
446             for _, slot in pairs(config.totems) do
447                 if not totems[slot] then totems[slot] = {} end
448                 table.insert(totems[slot], i);
449             end
450         end
451         frames[i] = createTMW("OmaTMW"..i, config, Indicators);
452     end
453
454     for _, frame in pairs(frames) do
455         if frame.auras then updateAuraFrame(frame) end
456         if frame.totems then
457             for _, slot in pairs(frame.totems) do updateTotemFrame(frame, slot) end
458         end
459     end
460 end
461
462 Indicators:RegisterEvent("UNIT_AURA");
463 Indicators:RegisterEvent("PLAYER_TARGET_CHANGED");
464 Indicators:RegisterEvent("PLAYER_TOTEM_UPDATE");
465 Indicators:RegisterEvent("INSTANCE_ENCOUNTER_ENGAGE_UNIT");
466 Indicators:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED");
467 Indicators:RegisterEvent("PLAYER_LOGIN");
468 Indicators:SetScript("OnEvent", function(self, event, arg1)
469     if event == "UNIT_AURA" then
470         updateAuras(arg1);
471     elseif event == "PLAYER_TARGET_CHANGED" then
472         updateAuras("target");
473     elseif event == "PLAYER_TOTEM_UPDATE" then
474         updateTotems(arg1);
475     elseif event == "INSTANCE_ENCOUNTER_ENGAGE_UNIT" then
476         for _, boss in pairs(bosses) do
477             updateAuras(boss);
478         end
479     elseif event == "PLAYER_SPECIALIZATION_CHANGED" then
480         currentSpec = GetSpecialization();
481         for _, frame in pairs(frames) do
482             if frame.auras then updateAuraFrame(frame) end
483             if frame.totems then
484                 for _, slot in pairs(frame.totems) do updateTotemFrame(frame, slot) end
485             end
486         end
487     elseif event == "PLAYER_LOGIN" then
488         initialize();
489     end
490 end);