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