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