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