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