7cd9b15 - Add pet frame, remove mana bars from non-healers
[wowui.git] / OmaUF / Events.lua
1 -- Events.lua
2 -- TODO -- recheck these, pvp functions not added yet
3 local _;
4 local unpack = unpack;
5 local ssub = string.sub;
6 local min = math.min;
7 local ceil = math.ceil;
8 local UnitName, UnitClass, UnitExists = UnitName, UnitClass, UnitExists;
9 local UnitDebuff, UnitIsCharmed, UnitIsFriend = UnitDebuff, UnitIsCharmed, UnitIsFriend;
10 local UnitPower, UnitPowerMax, UnitPowerType = UnitPower, UnitPowerMax, UnitPowerType;
11 local UnitHealth, UnitHealthMax = UnitHealth, UnitHealthMax;
12 local UnitGetIncomingHeals, UnitGetTotalAbsorbs = UnitGetIncomingHeals, UnitGetTotalAbsorbs;
13 local UnitThreatSituation, GetThreatStatusColor = UnitThreatSituation, GetThreatStatusColor;
14 local UnitIsDeadOrGhost, UnitIsConnected = UnitIsDeadOrGhost, UnitIsConnected;
15 local UnitGetTotalHealAbsorbs = UnitGetTotalHealAbsorbs;
16 local UnitHasVehicleUI, UnitTargetsVehicleInRaidUI = UnitHasVehicleUI, UnitTargetsVehicleInRaidUI;
17 local UnitGroupRolesAssigned = UnitGroupRolesAssigned;
18 local UnitLevel, UnitClassification = UnitLevel, UnitClassification;
19 local RAID_CLASS_COLORS = RAID_CLASS_COLORS;
20
21 local updateAuraFrames = OmaUFAuras.UpdateAuras;
22
23 local Settings = OmaUFSettings;
24 local baseColor = Settings.BaseColor;
25 local healthColor = Settings.HealthColor;
26 local powerColors = Settings.PowerColors;
27
28 local M = {};
29 OmaUFEvents = M;
30 function M.RegisterEvents(frame)
31     -- events are taken from FrameXML/CompactUnitFrame.lua
32     -- TODO raid marker support,
33     -- player flags support (/afk, /dnd)
34     local displayed = frame.unit ~= frame.displayed and frame.displayed or nil;
35     frame:RegisterUnitEvent("UNIT_HEALTH", frame.unit, displayed);
36     frame:RegisterUnitEvent("UNIT_HEALTH_FREQUENT", frame.unit, displayed);
37     frame:RegisterUnitEvent("UNIT_MAXHEALTH", frame.unit, displayed);
38     frame:RegisterUnitEvent("UNIT_POWER", frame.unit, displayed);
39     frame:RegisterUnitEvent("UNIT_MAXPOWER", frame.unit, displayed);
40     frame:RegisterUnitEvent("UNIT_DISPLAYPOWER", frame.unit, displayed);
41     frame:RegisterUnitEvent("UNIT_POWER_BAR_SHOW", frame.unit, displayed);
42     frame:RegisterUnitEvent("UNIT_POWER_BAR_HIDE", frame.unit, displayed);
43     frame:RegisterUnitEvent("UNIT_NAME_UPDATE", frame.unit, displayed);
44     frame:RegisterUnitEvent("UNIT_AURA", frame.unit, displayed);
45     frame:RegisterUnitEvent("UNIT_HEAL_PREDICTION", frame.unit, displayed);
46     frame:RegisterUnitEvent("UNIT_ABSORB_AMOUNT_CHANGED", frame.unit, displayed);
47     frame:RegisterUnitEvent("UNIT_HEAL_ABSORB_AMOUNT_CHANGED", frame.unit, displayed);
48     frame:RegisterUnitEvent("UNIT_THREAT_SITUATION_UPDATE", frame.unit, displayed);
49     frame:RegisterUnitEvent("UNIT_CONNECTION", frame.unit, displayed);
50     frame:RegisterUnitEvent("UNIT_FACTION", frame.unit, displayed);
51 end
52 local registerEvents = M.RegisterEvents;
53
54 local function updateHealth(frame, unit)
55     local current, max = UnitHealth(unit), frame.health.max;
56     frame.health:Show();
57     -- sanity check, occasionally UnitHealthMax gives zero
58     if current > max then
59         -- somehow current health has gone over the maximum (missed maxhealth event)
60         frame.health.max = UnitHealthMax(unit);
61         max = frame.health.max;
62         if current > max then
63             -- error state, still over maximum
64             frame.health:SetWidth(frame.width);
65             return;
66         end
67     elseif max > 0 then
68         frame.health:SetWidth(current/frame.health.max*frame.width);
69     else
70         frame.health:SetWidth(frame.width);
71         return;
72     end
73
74     if UnitIsDeadOrGhost(unit) then
75         frame.health:Hide();
76     end
77 end
78
79 local function updateHealthText(frame, unit)
80     local current, max = UnitHealth(unit), frame.health.max;
81     if UnitIsDeadOrGhost(unit) then
82         frame.healthText:SetText("Dead");
83         frame.healthText:Show();
84     elseif not UnitIsConnected(unit) then
85         frame.healthText:SetText("DC");
86         frame.healthText:Show();
87     elseif max > 0 then
88         if current > 1200000000 then -- 1.2B
89             frame.healthText:SetFormattedText("%.1fB", current / 1000000000);
90         elseif current > 1200000 then -- 1.2M
91             frame.healthText:SetFormattedText("%.1fM", current / 1000000);
92         elseif current > 1000 then -- 1K
93             frame.healthText:SetFormattedText("%.1fK", current / 1000);
94         else
95             frame.healthText:SetFormattedText("%d", current)
96         end
97         frame.healthText:Show();
98     else
99         frame.healthText:Hide();
100     end
101 end
102
103 local function updateMaxHealth(frame, unit)
104     frame.health.max = UnitHealthMax(unit);
105 end
106
107 local function updatePower(frame, unit)
108     local current, max = UnitPower(unit), frame.mana.max;
109     -- sanity check, occasionally UnitPowerMax gives zero
110     if current == 0 then
111         frame.mana:Hide();
112         return;
113     elseif current > max then
114         frame.mana:Show();
115         frame.mana.max = UnitPowerMax(unit);
116         max = frame.mana.max;
117         if current > max then
118             -- error
119             frame.mana:SetWidth(frame.width);
120             return;
121         end
122     end
123     if max > 0 then
124         frame.mana:SetWidth(UnitPower(unit)/max*frame.width);
125         frame.mana:Show();
126     else
127         frame.mana:SetWidth(frame.width);
128         frame.mana:Show();
129     end
130 end
131
132 local function updatePowerText(frame, unit)
133     local current, max = UnitPower(unit), frame.mana.max;
134     if UnitIsDeadOrGhost(unit) or not UnitIsConnected(unit) then
135         frame.healthText:Hide();
136     elseif max > 0 and current > 0 and current < max then
137         frame.manaText:SetText(ceil(current/max*100));
138         frame.manaText:Show();
139     else
140         frame.manaText:Hide();
141     end
142 end
143
144 local function updateMaxPower(frame, unit)
145     frame.mana.max = UnitPowerMax(unit);
146 end
147
148 local function updatePowerColor(frame, unit)
149     frame.mana:SetVertexColor(unpack(powerColors[UnitPowerType(unit)]));
150 end
151
152 local function updateName(frame, unit)
153     if not frame.name then return end
154     local name = UnitName(unit);
155     if not name then return end
156     frame.name:SetText(ssub(name, 1, 10));
157 end
158
159 local function updateHealPred(frame, unit)
160     local incoming = UnitGetIncomingHeals(unit) or 0;
161     if incoming > 0 then
162         local max = frame.health.max;
163         local space = frame.width - frame.health:GetWidth() + 1;
164         local pred = (incoming / max) * frame.width;
165         frame.healpred:SetWidth(min(space, pred));
166         frame.healpred:Show();
167     else
168         frame.healpred:Hide();
169     end
170 end
171
172 local function updateShield(frame, unit)
173     local shield = UnitGetTotalAbsorbs(unit) or 0;
174     if shield > 0 then
175         local max = frame.health.max;
176         local space = frame.width - frame.health:GetWidth();
177         shield = (shield / max) * frame.width;
178         if space == 0 then
179             frame.shield:Hide();
180             frame.shieldhl:Show();
181         elseif space < shield then
182             frame.shield:SetWidth(space);
183             frame.shield:Show();
184             frame.shieldhl:Show();
185         else
186             frame.shield:SetWidth(shield);
187             frame.shield:Show();
188             frame.shieldhl:Hide();
189         end
190     else
191         frame.shield:Hide();
192         frame.shieldhl:Hide();
193     end
194 end
195
196 local function updateHealAbsorb(frame, unit)
197     local absorb = UnitGetTotalHealAbsorbs(unit) or 0;
198     if absorb > 0 then
199         local max = frame.health.max;
200         local space = frame.health:GetWidth();
201         absorb = (absorb / max) * frame.width;
202         frame.healabsorb:SetWidth(min(space, absorb));
203         frame.healabsorb:Show();
204     else
205         frame.healabsorb:Hide();
206     end
207 end
208
209 local function updateAuras(frame, unit)
210     updateAuraFrames(frame, unit);
211 end
212
213 local function updateAggro(frame, unit)
214     local status = UnitThreatSituation(unit);
215     if status and status > 0 then
216         frame.base:SetVertexColor(GetThreatStatusColor(status));
217     else
218         frame.base:SetVertexColor(unpack(baseColor));
219     end
220 end
221
222 local function updateVehicle(frame)
223     local shouldTargetVehicle = UnitHasVehicleUI(frame.unit) and UnitTargetsVehicleInRaidUI(frame.unit) and UnitExists(frame.vehicle);
224     if shouldTargetVehicle then
225         if not frame.inVehicle then
226             frame.inVehicle = true;
227             frame.displayed = frame.vehicle;
228             registerEvents(frame);
229         end
230     elseif frame.inVehicle then
231         frame.inVehicle = false;
232         frame.displayed = frame.unit;
233         registerEvents(frame);
234     end
235 end
236
237 local function updateRole(frame, unit)
238     local role = UnitGroupRolesAssigned(unit);
239     if role == "HEALER" or role == "TANK" or role == "DAMAGER" then
240         frame.role:SetTexCoord(GetTexCoordsForRoleSmallCircle(role));
241         frame.role:Show();
242     else
243         frame.role:Hide();
244     end
245 end
246
247 local function updateLevelText(frame, unit, levelup)
248     if not frame.level then return end
249     if levelup then
250         -- PLAYER_LEVEL_UP
251         frame.level:SetText(levelup);
252     else
253         local level = UnitLevel(unit);
254         local class = UnitClassification(unit);
255         local leveltext, classtext;
256         if level < 0 then
257             if class == "worldboss" then
258                 leveltext = "Boss";
259             else
260                 leveltext = "??";
261             end
262         else
263             leveltext = level;
264         end
265         if class == "rareelite" then
266             classtext = " Rare Elite";
267         elseif class == "elite" then
268             classtext = " Elite";
269         elseif class == "rare" then
270             classtext = " Rare";
271         else
272             classtext = "";
273         end
274         frame.level:SetFormattedText("%s%s", leveltext, classtext);
275     end
276 end
277
278 local function updateStatus(frame, unit)
279     -- coords from PlayerFrame
280     if frame.inCombat or UnitAffectingCombat(unit) then
281         frame.status:SetTexCoord(0.5, 1, 0, 0.484375);
282         frame.status:Show();
283     elseif unit == "player" and IsResting() then
284         frame.status:SetTexCoord(0, 0.5, 0, 0.421875);
285         frame.status:Show();
286     else
287         frame.status:Hide();
288     end
289 end
290
291 local function updatePVP(frame, unit)
292     if UnitIsPVPFreeForAll(unit) then
293         frame.pvp:SetTexture("Interface\\TARGETINGFRAME\\UI-PVP-FFA");
294         frame.pvp:Show();
295     elseif UnitIsPVP(unit) then
296         local faction = UnitFactionGroup(unit);
297         if faction and faction ~= "Neutral" then
298             -- from PlayerFrame, mercenary checks
299             if UnitIsMercenary(unit) then
300                 if faction == "Horde" then
301                     faction = "Alliance";
302                 elseif faction == "Alliance" then
303                     faction = "Horde";
304                 end
305             end
306             frame.pvp:SetTexture("Interface\\TARGETINGFRAME\\UI-PVP-"..faction);
307             frame.pvp:Show();
308         else
309             frame.pvp:Hide();
310         end
311     else
312         frame.pvp:Hide();
313     end
314 end
315
316 local function updateLeaderIcon(frame, unit)
317     if UnitIsGroupLeader(frame.unit) then
318         if HasLFGRestrictions() then
319             frame.leader:SetTexture("Interface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES");
320             frame.leader:SetTexCoord(0, 0.296875, 0.015625, 0.3125);
321         else
322             frame.leader:SetTexture("Interface\\GROUPFRAME\\UI-Group-LeaderIcon");
323             frame.leader:SetTexCoord(0, 1, 0, 1);
324         end
325         frame.leader:Show();
326     elseif UnitIsGroupAssistant(frame.unit) then
327         frame.leader:SetTexture("Interface\\GROUPFRAME\\UI-Group-AssistantIcon");
328         frame.leader:SetTexCoord(0, 1, 0, 1);
329         frame.leader:Show();
330     else
331         frame.leader:Hide();
332     end
333 end
334
335 local function updateHealthColor(frame, unit)
336     if not UnitPlayerControlled(unit) and UnitIsTapDenied(unit) then
337         frame.health:SetVertexColor(0.5, 0.5, 0.5);
338     elseif UnitIsPlayer(unit) then
339         local _, class = UnitClass(unit);
340         local color = RAID_CLASS_COLORS[class];
341         if color then
342             frame.health:SetVertexColor(color.r, color.g, color.b)
343         else
344             frame.health:SetVertexColor(unpack(healthColor))
345         end
346     elseif UnitPlayerControlled(unit) then
347         frame.health:SetVertexColor(0, 1, 0);
348     else
349         frame.health:SetVertexColor(UnitSelectionColor(unit));
350     end
351 end
352
353 local eventFuncs = {
354     ["UNIT_HEALTH"] = function(frame)
355         updateHealth(frame, frame.displayed);
356         updateHealthText(frame, frame.displayed);
357         updateShield(frame, frame.displayed);
358         updateHealAbsorb(frame, frame.displayed);
359         -- no heal prediction update, that doesn't overflow too much
360     end,
361     ["UNIT_POWER"] = function(frame)
362         updatePower(frame, frame.displayed);
363         updatePowerText(frame, frame.displayed);
364     end,
365     ["UNIT_AURA"] = function(frame)
366         updateAuras(frame, frame.displayed);
367     end,
368     ["UNIT_HEAL_PREDICTION"] = function(frame)
369         updateHealPred(frame, frame.displayed);
370     end,
371     ["UNIT_ABSORB_AMOUNT_CHANGED"] = function(frame)
372         updateShield(frame, frame.displayed);
373     end,
374     ["UNIT_HEAL_ABSORB_AMOUNT_CHANGED"] = function(frame)
375         updateHealAbsorb(frame, frame.displayed);
376     end,
377     ["UNIT_THREAT_SITUATION_UPDATE"] = function(frame)
378         updateAggro(frame, frame.displayed);
379     end,
380     ["UNIT_MAXHEALTH"] = function(frame)
381         updateMaxHealth(frame, frame.displayed);
382         updateHealth(frame, frame.displayed);
383         updateHealthText(frame, frame.displayed);
384         updateShield(frame, frame.displayed);
385         updateHealAbsorb(frame, frame.displayed);
386     end,
387     ["UNIT_MAXPOWER"] = function(frame)
388         updateMaxPower(frame, frame.displayed);
389         updatePower(frame, frame.displayed);
390         updatePowerText(frame, frame.displayed);
391     end,
392     ["UNIT_DISPLAYPOWER"] = function(frame)
393         updatePowerColor(frame, frame.displayed);
394         updateMaxPower(frame, frame.displayed);
395         updatePower(frame, frame.displayed);
396         updatePowerText(frame, frame.displayed);
397     end,
398     ["UNIT_NAME_UPDATE"] = function(frame)
399         updateName(frame, frame.displayed);
400         updateHealthColor(frame, frame.displayed);
401     end,
402     ["UNIT_CONNECTION"] = function(frame)
403         updateHealthText(frame, frame.displayed);
404         updatePowerText(frame, frame.displayed);
405     end,
406     ["PLAYER_ROLES_ASSIGNED"] = function(frame)
407         updateRole(frame, frame.unit);
408     end,
409     ["UNIT_LEVEL"] = function(frame)
410         updateLevelText(frame, frame.unit);
411     end,
412     ["PLAYER_LEVEL_UP"] = function(frame, arg1)
413         updateLevelText(frame, frame.unit, arg1);
414     end,
415     ["PLAYER_UPDATE_RESTING"] = function(frame)
416         updateStatus(frame, frame.unit);
417     end,
418     ["PLAYER_REGEN_DISABLED"] = function(frame)
419         frame.inCombat = true;
420         updateStatus(frame, frame.unit);
421     end,
422     ["PLAYER_REGEN_ENABLED"] = function(frame)
423         frame.inCombat = false;
424         updateStatus(frame, frame.unit);
425     end,
426     ["UNIT_FACTION"] = function(frame)
427         updatePVP(frame, frame.unit);
428         updateHealthColor(frame, frame.displayed);
429     end,
430     ["PARTY_LEADER_CHANGED"] = function(frame)
431         updateLeaderIcon(frame, frame.unit);
432     end,
433     ["UPDATE_ALL_BARS"] = function(frame)
434         updateVehicle(frame);
435         updateMaxHealth(frame, frame.displayed);
436         updateMaxPower(frame, frame.displayed);
437         updateHealth(frame, frame.displayed);
438         updateHealthText(frame, frame.displayed);
439         updatePower(frame, frame.displayed);
440         updatePowerText(frame, frame.displayed);
441         updateAuras(frame, frame.displayed);
442         updateShield(frame, frame.displayed);
443         updateHealPred(frame, frame.displayed);
444         updateHealAbsorb(frame, frame.displayed);
445         updatePowerColor(frame, frame.displayed);
446         updateAggro(frame, frame.displayed);
447         updateName(frame, frame.displayed);
448         updateRole(frame, frame.unit);
449         updateLevelText(frame, frame.unit);
450         updateStatus(frame, frame.unit);
451         updatePVP(frame, frame.unit);
452         updateLeaderIcon(frame, frame.unit);
453         updateHealthColor(frame, frame.displayed);
454     end,
455 };
456 eventFuncs["UNIT_HEALTH_FREQUENT"] = eventFuncs["UNIT_HEALTH"];
457 eventFuncs["UNIT_POWER_BAR_SHOW"] = eventFuncs["UNIT_DISPLAYPOWER"];
458 eventFuncs["UNIT_POWER_BAR_HIDE"] = eventFuncs["UNIT_DISPLAYPOWER"];
459 eventFuncs["UNIT_ENTERED_VEHICLE"] = eventFuncs["UPDATE_ALL_BARS"];
460 eventFuncs["UNIT_EXITED_VEHICLE"] = eventFuncs["UPDATE_ALL_BARS"];
461 eventFuncs["UNIT_PET"] = eventFuncs["UPDATE_ALL_BARS"];
462 eventFuncs["GROUP_ROSTER_UPDATE"] = eventFuncs["UPDATE_ALL_BARS"];
463 eventFuncs["PLAYER_ENTERING_WORLD"] = eventFuncs["UPDATE_ALL_BARS"];
464 eventFuncs["PLAYER_TARGET_CHANGED"] = eventFuncs["UPDATE_ALL_BARS"];
465
466 function M.UnitEvent(self, event, arg1)
467     eventFuncs[event](self, arg1);
468 end