e2567ab - Dispellable debuff priority higher
[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         ["Gedren"] = {
135             {
136                 totems = {1}, -- Efflorescence
137                 x = 660,
138                 y = 440,
139                 width = 80,
140                 height = 80,
141             },
142         },
143         ["Gazden"] = {
144             {
145                 unit = "player",
146                 auras = {"Tidal Waves"},
147                 auraFilter = "PLAYER HELPFUL",
148                 x = 660,
149                 y = 440,
150                 width = 40,
151                 height = 40,
152             },
153             {
154                 unit = "player",
155                 auras = {"Healing Rain"},
156                 auraFilter = "PLAYER HELPFUL",
157                 x = 700,
158                 y = 440,
159                 width = 40,
160                 height = 40,
161             },
162         },
163     },
164 };
165
166 -- settings entry:
167 -- unit = unitID where to check auras, not required for totem checkers
168 -- spec = player spec index to show frame, if nil show always
169 -- auras = list of auras to track, in priority order
170 -- auraFilter = filter for UnitAura
171 -- totems = list of totem slots to track, in priority order, only checked if auras is nil
172 -- x = x position relative to UIParent bottom left
173 -- y = y position relative to UIParent bottom left
174 -- width = width
175 -- height = height
176
177 -- global frames' settings
178 local settings = {
179     {
180         unit = "player",
181         auras = {"Innervate"},
182         auraFilter = "HELPFUL",
183         x = 570,
184         y = 440,
185         width = 80,
186         height = 80,
187     },
188     {
189         unit = "player",
190         auras = {
191             "Delusions", "Entropic Blast", "Necrotic Embrace", "Flametouched", "Shadowtouched",
192             "Blazing Eruption", "Shattering Scream", "Consuming Hunger", "Unstable Soul",
193             "Time Bomb", "Broken Shard", "Demolished", "Fetid Rot", "Roiling Deceit",
194             "Putrid Blood", "Endemic Virus", "Lingering Infection", "Gigavolt Charge",
195             "Crackling Lightning", "Storm's Wail", "Death's Door", "Deathly Withering",
196             "Chilling Touch", "Volatile Charge", "Liquid Gold", "Drained Soul"
197         },
198         auraFilter = "HARMFUL",
199         x = 660,
200         y = 530,
201         width = 80,
202         height = 80,
203     },
204 };
205
206 local frames = {};
207 local units = {}; -- mapping from unitID to frames
208 local totems = {}; -- mapping to frames with totems
209 local bosses = {};
210 for i = 1, MAX_BOSS_FRAMES do
211     bosses[i] = "boss"..i;
212 end
213 local currentSpec = 0; -- 0 is invalid
214
215 local Indicators = CreateFrame("Frame", "OmaTMW", UIParent);
216
217 local function updateAuraFrame(frame)
218     local unit = frame.unit;
219     if UnitExists(unit) and (not frame.spec or frame.spec == currentSpec) then
220         local name, icon, count, duration, expires;
221         local auraFilter = frame.auraFilter;
222         local i = 1;
223         while true do
224             name, icon, count, _, duration, expires = UnitAura(unit, i, auraFilter);
225             if not name then break end
226             -- possible improvement to add spellID as an option
227             if frame.auras[name] then
228                 if count > 1 then
229                     frame.stack:SetText(count);
230                     frame.stack:Show();
231                 else
232                     frame.stack:Hide();
233                 end
234                 if expires > 0 then
235                     frame.cd:SetCooldown(expires - duration, duration);
236                     frame.cd:Show();
237                 else
238                     frame.cd:Hide();
239                 end
240                 frame.icon:SetTexture(icon);
241                 frame:Show();
242                 return;
243             end
244             i = i + 1;
245         end
246     end
247     frame:Hide();
248 end
249
250 local function updateAuras(unit)
251     if units[unit] then
252         for _, i in pairs(units[unit]) do
253             updateAuraFrame(frames[i]);
254         end
255     end
256 end
257
258 local function updateTotemFrame(frame, slot)
259     local _, name, start, duration, icon = GetTotemInfo(slot);
260     if name ~= "" then
261         frame.cd:SetCooldown(start, duration);
262         frame.cd:Show();
263         frame.icon:SetTexture(icon);
264         frame:Show();
265     else
266         frame:Hide();
267     end
268 end
269
270 local function updateTotems(slot)
271     if totems[slot] then
272         for _, i in pairs(totems[slot]) do
273             updateTotemFrame(frames[i], slot);
274         end
275     end
276 end
277
278 local function createTMW(name, config, parent)
279     local frame = CreateFrame("Frame", name, parent);
280     frame:SetPoint("TOPLEFT", parent, "BOTTOMLEFT", config.x, config.y+config.height);
281     frame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", config.x+config.width, config.y);
282     frame.unit = config.unit;
283     frame.spec = config.spec;
284     if config.auras then
285         frame.auras = {};
286         for _, v in pairs(config.auras) do
287             frame.auras[v] = true;
288         end
289     end
290     frame.auraFilter = config.auraFilter;
291     frame.totems = config.totems;
292     frame:Hide();
293     frame.base = frame:CreateTexture(nil, "BACKGROUND");
294     frame.base:SetAllPoints();
295     frame.base:SetColorTexture(0, 0, 0, 0.6);
296     frame.icon = frame:CreateTexture(nil, "ARTWORK");
297     frame.icon:SetPoint("TOPLEFT", frame.base, "TOPLEFT", 1, -1);
298     frame.icon:SetPoint("BOTTOMRIGHT", frame.base, "BOTTOMRIGHT", -1, 1);
299     frame.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93);
300     frame.stack = frame:CreateFontString(nil, "OVERLAY", "NumberFontNormalLarge");
301     frame.stack:SetPoint("TOPLEFT");
302     frame.stack:Hide();
303     frame.cd = CreateFrame("Cooldown", name.."CD", frame, "CooldownFrameTemplate");
304     frame.cd:SetReverse(true);
305     frame.cd:SetAllPoints();
306     return frame;
307 end
308
309 local function initialize()
310     Indicators:SetFrameStrata("LOW");
311     Indicators:SetPoint("BOTTOMLEFT");
312     Indicators:SetWidth(1);
313     Indicators:SetHeight(1);
314     currentSpec = GetSpecialization();
315     local name, realm = UnitFullName("player");
316     if chars[realm] and chars[realm][name] then
317         for _, config in pairs(chars[realm][name]) do
318             table.insert(settings, config)
319         end
320     end
321     for i, config in pairs(settings) do
322         if config.unit then
323             if not units[config.unit] then units[config.unit] = {} end
324             table.insert(units[config.unit], i);
325         end
326         if config.totems then
327             for _, slot in pairs(config.totems) do
328                 if not totems[slot] then totems[slot] = {} end
329                 table.insert(totems[slot], i);
330             end
331         end
332         frames[i] = createTMW("OmaTMW"..i, config, Indicators);
333     end
334
335     for _, frame in pairs(frames) do
336         if frame.auras then updateAuraFrame(frame) end
337         if frame.totems then
338             for _, slot in pairs(frame.totems) do updateTotemFrame(frame, slot) end
339         end
340     end
341 end
342
343 Indicators:RegisterEvent("UNIT_AURA");
344 Indicators:RegisterEvent("PLAYER_TARGET_CHANGED");
345 Indicators:RegisterEvent("PLAYER_TOTEM_UPDATE");
346 Indicators:RegisterEvent("INSTANCE_ENCOUNTER_ENGAGE_UNIT");
347 Indicators:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED");
348 Indicators:RegisterEvent("PLAYER_LOGIN");
349 Indicators:SetScript("OnEvent", function(self, event, arg1)
350     if event == "UNIT_AURA" then
351         updateAuras(arg1);
352     elseif event == "PLAYER_TARGET_CHANGED" then
353         updateAuras("target");
354     elseif event == "PLAYER_TOTEM_UPDATE" then
355         updateTotems(arg1);
356     elseif event == "INSTANCE_ENCOUNTER_ENGAGE_UNIT" then
357         for _, boss in pairs(bosses) do
358             updateAuras(boss);
359         end
360     elseif event == "PLAYER_SPECIALIZATION_CHANGED" then
361         currentSpec = GetSpecialization();
362         for _, frame in pairs(frames) do
363             if frame.auras then updateAuraFrame(frame) end
364             if frame.totems then
365                 for _, slot in pairs(frame.totems) do updateTotemFrame(frame, slot) end
366             end
367         end
368     elseif event == "PLAYER_LOGIN" then
369         initialize();
370     end
371 end);