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