8e1fcc638a3e41906232d61f53e0f3e798591a6b
[wowui.git] / kehys / updater.lua
1 -- updater.lua
2 -- 2019 Aleksi Blinnikka
3 --
4 -- Main update function for all frames. Gets around issues with events.
5 local _, addon = ...;
6 local unpack = unpack;
7 local min = math.min;
8 local format = string.format;
9 local UnitDebuff, UnitIsCharmed = UnitDebuff, UnitIsCharmed;
10 local UnitHealth, UnitHealthMax = UnitHealth, UnitHealthMax;
11 local UnitPower, UnitPowerMax = UnitPower, UnitPowerMax;
12 local UnitIsAFK, UnitIsDND = UnitIsAFK, UnitIsDND;
13 local UnitGetIncomingHeals, UnitGetTotalAbsorbs = UnitGetIncomingHeals, UnitGetTotalAbsorbs;
14 local UnitIsDeadOrGhost, UnitIsConnected = UnitIsDeadOrGhost, UnitIsConnected;
15 local UnitGetTotalHealAbsorbs = UnitGetTotalHealAbsorbs;
16 local InCombatLockdown, IsResting = InCombatLockdown, IsResting;
17
18 local dispelcolor = addon.Colors.OverlayColorDispel;
19 local charmcolor = addon.Colors.OverlayColorCharm;
20 local majorcolor = addon.Colors.OverlayColorAlert;
21 local healcolor = addon.Colors.OverlayColorHeal;
22
23 local ignoredAuras = {
24     [315176] = true, -- Grasping Tendrils
25 };
26
27 local powerColors = {
28     [Enum.PowerType.Mana] = {0.1, 0.5, 0.9},
29     [Enum.PowerType.Rage] = {1, 0, 0},
30     [Enum.PowerType.Pain] = {1, 0, 0},
31     [Enum.PowerType.Focus] = {1, 0.5, 0},
32     [Enum.PowerType.Energy] = {1, 0.8, 0},
33     [Enum.PowerType.Fury] = {0.8, 0.3, 0.9},
34     [Enum.PowerType.RunicPower] = {0.8, 0, 0.2},
35     [Enum.PowerType.LunarPower] = {0.3, 0.5, 0.9},
36     [Enum.PowerType.Maelstrom] = {0, 0.5, 1},
37     [Enum.PowerType.Insanity] = {0.4, 0, 0.8},
38 };
39
40 function addon.FrameUpdate(frame)
41     assert(type(frame) == "table", "FrameUpdate received invalid frame parameter!");
42
43     local unit = frame.displayed;
44     local width = frame.barwidth;
45     -- range check (doesn't have an event) frames can be marked constantly visible
46     if not frame.constant then
47         local inrange, checked = UnitInRange(unit);
48         if checked and not inrange then
49             frame:SetAlpha(0.55);
50         else
51             frame:SetAlpha(1);
52         end
53     end
54     -- states
55     if UnitIsDeadOrGhost(unit) then
56         frame.text:SetText("Dead");
57         if not frame.text:IsShown() then frame.text:Show() end
58         if frame.health:IsShown() then frame.health:Hide() end
59         if frame.shield:IsShown() then frame.shield:Hide() end
60         if frame.shieldhl:IsShown() then frame.shieldhl:Hide() end
61         if frame.healpred:IsShown() then frame.healpred:Hide() end
62         if frame.healabsorb:IsShown() then frame.healabsorb:Hide() end
63         frame.prev.health = nil;
64         frame.prev.hmax = nil;
65     elseif not UnitIsConnected(unit) then
66         frame.text:SetText("DC");
67         if not frame.text:IsShown() then frame.text:Show() end
68         if frame.health:IsShown() then frame.health:Hide() end
69         if frame.shield:IsShown() then frame.shield:Hide() end
70         if frame.shieldhl:IsShown() then frame.shieldhl:Hide() end
71         if frame.healpred:IsShown() then frame.healpred:Hide() end
72         if frame.healabsorb:IsShown() then frame.healabsorb:Hide() end
73         frame.prev.health = nil;
74         frame.prev.hmax = nil;
75     else
76         if UnitIsAFK(unit) then
77             frame.text.status = true;
78             frame.text:SetText("afk");
79             if not frame.text:IsShown() then frame.text:Show() end
80         elseif UnitIsDND(unit) then
81             frame.text.status = true;
82             frame.text:SetText("dnd");
83             if not frame.text:IsShown() then frame.text:Show() end
84         else
85             frame.text.status = false;
86             if frame.raid and frame.text:IsShown() then
87                 frame.text:Hide();
88             end
89         end
90         if not frame.raid and frame.unit == "player" then
91             if InCombatLockdown() then
92                 frame.status:SetTexCoord(0.5, 1, 0, 0.484375);
93                 frame.status:Show();
94             elseif IsResting() then
95                 frame.status:SetTexCoord(0, 0.5, 0, 0.421875);
96                 frame.status:Show();
97             elseif frame.status:IsShown() then
98                 frame.status:Hide();
99             end
100         end
101         -- health
102         local current, hmax = UnitHealth(unit), UnitHealthMax(unit);
103         if frame.prev.health ~= current or frame.prev.hmax ~= hmax then
104             if not frame.raid and not frame.text.status and frame.prev.htext ~= current then
105                 frame.prev.htext = current;
106                 if frame.boss then
107                     if hmax < current or hmax <= 1 then
108                         frame.text:SetText("100");
109                         if not frame.text:IsShown() then frame.text:Show() end
110                     elseif current <= 0 then
111                         if frame.text:IsShown() then frame.text:Hide() end
112                     else
113                         frame.text:SetFormattedText("%.1f", current/hmax*100);
114                         if not frame.text:IsShown() then frame.text:Show() end
115                     end
116                 else
117                     if current > 1000000000 then -- 1.0B
118                         frame.text:SetFormattedText("%.2fB", current / 1000000000);
119                     elseif current > 1000000 then -- 1.0M
120                         frame.text:SetFormattedText("%.2fM", current / 1000000);
121                     elseif current > 1000 then -- 1.0K
122                         frame.text:SetFormattedText("%.1fK", current / 1000);
123                     end
124                     if not frame.text:IsShown() then frame.text:Show() end
125                 end
126             end
127             frame.prev.health = current;
128             frame.prev.hmax = hmax;
129             if hmax < current or hmax <= 1 then
130                 frame.health:SetWidth(width);
131                 frame.health.width = width;
132                 if not frame.health:IsShown() then frame.health:Show() end
133             elseif current <= 0 then
134                 if frame.health:IsShown() then frame.health:Hide() end
135             else
136                 local w = current/hmax*width;
137                 frame.health:SetWidth(w);
138                 frame.health.width = w;
139                 if not frame.health:IsShown() then frame.health:Show() end
140             end
141         end
142         -- shield
143         local hwidth = frame.health.width;
144         current = UnitGetTotalAbsorbs(unit) or 0;
145         if current > 0 then
146             local space = width - hwidth;
147             current = (current / hmax) * width;
148             if space == 0 then
149                 if frame.shield:IsShown() then frame.shield:Hide() end
150                 if not frame.shieldhl:IsShown() then frame.shieldhl:Show() end
151             elseif space < current then
152                 frame.shield:SetWidth(space);
153                 if not frame.shield:IsShown() then frame.shield:Show() end
154                 if not frame.shieldhl:IsShown() then frame.shieldhl:Show() end
155             else
156                 frame.shield:SetWidth(current);
157                 if not frame.shield:IsShown() then frame.shield:Show() end
158                 if frame.shieldhl:IsShown() then frame.shieldhl:Hide() end
159             end
160         else
161             if frame.shield:IsShown() then frame.shield:Hide() end
162             if frame.shieldhl:IsShown() then frame.shieldhl:Hide() end
163         end
164         -- heal absorb
165         current = UnitGetTotalHealAbsorbs(unit) or 0;
166         if current > 0 then
167             current = (current / hmax) * width;
168             frame.healabsorb:SetWidth(min(hwidth, current));
169             if not frame.healabsorb:IsShown() then frame.healabsorb:Show() end
170         elseif frame.healabsorb:IsShown() then
171             frame.healabsorb:Hide();
172         end
173         -- heal prediction
174         current = UnitGetIncomingHeals(unit) or 0;
175         if current > 0 then
176             current = (current / hmax) * width;
177             -- at least 1 pixel prediction shown
178             frame.healpred:SetWidth(min(width - hwidth + 1, current));
179             if not frame.healpred:IsShown() then frame.healpred:Show() end
180         elseif frame.healpred:IsShown() then
181             frame.healpred:Hide();
182         end
183         -- mana, if present
184         if frame.mana ~= nil then
185             local current, max = UnitPower(unit), UnitPowerMax(unit);
186             local ptype = UnitPowerType(unit);
187             if frame.mana.ptype ~= ptype then
188                 frame.mana.ptype = ptype;
189                 frame.mana:SetVertexColor(unpack(powerColors[ptype]));
190             end
191             if frame.prev.mana ~= current or frame.prev.mmax ~= max then
192                 frame.prev.mana = current;
193                 frame.prev.mmax = max;
194                 if max < current or max <= 1 then
195                     frame.mana:SetWidth(width);
196                     frame.manatext:SetText("100");
197                     if not frame.mana:IsShown() then frame.mana:Show() end
198                     if not frame.manatext:IsShown() then frame.manatext:Show() end
199                 elseif current <= 0 then
200                     if frame.mana:IsShown() then frame.mana:Hide() end
201                     if frame.manatext:IsShown() then frame.manatext:Hide() end
202                 else
203                     local percent = current/max;
204                     frame.mana:SetWidth(percent*width);
205                     frame.manatext:SetText(format("%d", percent*100+0.5));
206                     if not frame.mana:IsShown() then frame.mana:Show() end
207                     if not frame.manatext:IsShown() then frame.manatext:Show() end
208                 end
209             end
210         end
211         -- forced aura updates
212         if frame.rounds ~= nil then
213             frame.rounds = frame.rounds + 1;
214             if (frame.rounds > 7) then
215                 frame.tankcd = {};
216                 frame.alert = {};
217                 frame.stacks = {};
218                 frame.heal = {};
219                 frame.buff1 = {};
220                 addon.SetAuras(frame.unit, frame.guid);
221                 frame.rounds = 0;
222             end
223         end
224         if frame.raid then
225             -- tank CD marker
226             if next(frame.tankcd) then
227                 if not frame.defensive:IsShown() then frame.defensive:Show() end
228             elseif frame.defensive:IsShown() then
229                 frame.defensive:Hide();
230             end
231             -- aura stacks
232             if next(frame.stacks) then
233                 local _, amount = next(frame.stacks);
234                 frame.stack:SetText(amount);
235                 if not frame.stack:IsShown() then frame.stack:Show() end
236             elseif frame.stack:IsShown() then
237                 frame.stack:Hide();
238             end
239             -- custom buff indicator 1
240             if next(frame.buff1) then
241                 if not frame.buffind1:IsShown() then frame.buffind1:Show() end
242             elseif frame.buffind1:IsShown() then
243                 frame.buffind1:Hide();
244             end
245             -- custom buff indicator 2
246             if next(frame.buff2) then
247                 if not frame.buffind2:IsShown() then frame.buffind2:Show() end
248             elseif frame.buffind2:IsShown() then
249                 frame.buffind2:Hide();
250             end
251             -- incoming ability
252             if next(frame.incoming) then
253                 if not frame.glow:IsShown() then frame.glow:Show() end
254             elseif frame.glow:IsShown() then
255                 frame.glow:Hide();
256             end
257             -- overlays
258             if next(frame.alert) then
259                 -- major
260                 if frame.overlay.color ~= majorcolor then
261                     frame.overlay:SetVertexColor(unpack(majorcolor));
262                     frame.overlay.color = majorcolor;
263                     if not frame.overlay:IsShown() then frame.overlay:Show() end
264                 end
265             else
266                 local _, _, _, _, _, _, _, _, _, spellid = UnitDebuff(unit, 1, "RAID");
267                 if UnitIsCharmed(unit) and frame.unit == frame.displayed then
268                     -- charmed
269                     if frame.overlay.color ~= charmcolor then
270                         frame.overlay:SetVertexColor(unpack(charmcolor));
271                         frame.overlay.color = charmcolor;
272                         if not frame.overlay:IsShown() then frame.overlay:Show() end
273                     end
274                 elseif spellid ~= nil and not ignoredAuras[spellid] then
275                     -- dispellable
276                     if frame.overlay.color ~= dispelcolor then
277                         frame.overlay:SetVertexColor(unpack(dispelcolor));
278                         frame.overlay.color = dispelcolor;
279                         if not frame.overlay:IsShown() then frame.overlay:Show() end
280                     end
281                 elseif next(frame.heal) then
282                     -- major heals needed
283                     if frame.overlay.color ~= healcolor then
284                         frame.overlay:SetVertexColor(unpack(healcolor));
285                         frame.overlay.color = healcolor;
286                         if not frame.overlay:IsShown() then frame.overlay:Show() end
287                     end
288                 else
289                     if frame.overlay.color ~= nil then
290                         frame.overlay.color = nil;
291                         if frame.overlay:IsShown() then frame.overlay:Hide() end
292                     end
293                 end
294             end
295         end
296     end
297 end