ac5ca8a - Add leader icon, aura frame stub
[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 Settings = OmaUFSettings;
22 local baseColor = Settings.BaseColor;
23 local overlayColorDispel = Settings.OverlayColorDispel;
24 local overlayColorCharm = Settings.OverlayColorCharm;
25 local overlayColorAlert = Settings.OverlayColorAlert;
26 local powerColors = Settings.PowerColors;
27 local width = 10;
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_NAME_UPDATE", frame.unit, displayed);
43     frame:RegisterUnitEvent("UNIT_AURA", frame.unit, displayed);
44     frame:RegisterUnitEvent("UNIT_HEAL_PREDICTION", frame.unit, displayed);
45     frame:RegisterUnitEvent("UNIT_ABSORB_AMOUNT_CHANGED", frame.unit, displayed);
46     frame:RegisterUnitEvent("UNIT_HEAL_ABSORB_AMOUNT_CHANGED", frame.unit, displayed);
47     frame:RegisterUnitEvent("UNIT_THREAT_SITUATION_UPDATE", frame.unit, displayed);
48     frame:RegisterUnitEvent("UNIT_CONNECTION", frame.unit, displayed);
49     frame:RegisterUnitEvent("UNIT_FACTION", frame.unit, displayed);
50 end
51 local registerEvents = M.RegisterEvents;
52
53 local function updateHealth(frame, unit)
54     local current, max = UnitHealth(unit), frame.health.max;
55     frame.health:Show();
56     -- sanity check, occasionally UnitHealthMax gives zero
57     if current > max then
58         -- somehow current health has gone over the maximum (missed maxhealth event)
59         frame.health.max = UnitHealthMax(unit);
60         max = frame.health.max;
61         if current > max then
62             -- error state, still over maximum
63             frame.health:SetWidth(width);
64             return;
65         end
66     elseif max > 0 then
67         frame.health:SetWidth(current/frame.health.max*width);
68     else
69         frame.health:SetWidth(width);
70         return;
71     end
72
73     if UnitIsDeadOrGhost(unit) then
74         frame.health:Hide();
75     end
76 end
77
78 local function updateHealthText(frame, unit)
79     local current, max = UnitHealth(unit), frame.health.max;
80     if UnitIsDeadOrGhost(unit) then
81         frame.healthText:SetText("Dead");
82         frame.healthText:Show();
83     elseif not UnitIsConnected(unit) then
84         frame.healthText:SetText("DC");
85         frame.healthText:Show();
86     elseif max > 0 and current < max then
87         frame.healthText:SetText(ceil(current/max*100));
88         frame.healthText:Show();
89     else
90         frame.healthText:Hide();
91     end
92 end
93
94 local function updateMaxHealth(frame, unit)
95     frame.health.max = UnitHealthMax(unit);
96 end
97
98 local function updatePower(frame, unit)
99     local current, max = UnitPower(unit), frame.mana.max;
100     -- sanity check, occasionally UnitPowerMax gives zero
101     if current == 0 then
102         frame.mana:Hide();
103         return;
104     elseif current > max then
105         frame.mana:Show();
106         frame.mana.max = UnitPowerMax(unit);
107         max = frame.mana.max;
108         if current > max then
109             -- error
110             frame.mana:SetWidth(width);
111             return;
112         end
113     end
114     if max > 0 then
115         frame.mana:SetWidth(UnitPower(unit)/max*width);
116         frame.mana:Show();
117     else
118         frame.mana:SetWidth(width);
119         frame.mana:Show();
120     end
121 end
122
123 local function updatePowerText(frame, unit)
124     local current, max = UnitPower(unit), frame.mana.max;
125     if UnitIsDeadOrGhost(unit) or not UnitIsConnected(unit) then
126         frame.healthText:Hide();
127     elseif max > 0 and current > 0 and current < max then
128         frame.manaText:SetText(ceil(current/max*100));
129         frame.manaText:Show();
130     else
131         frame.manaText:Hide();
132     end
133 end
134
135 local function updateMaxPower(frame, unit)
136     frame.mana.max = UnitPowerMax(unit);
137 end
138
139 local function updatePowerColor(frame, unit)
140     frame.mana:SetVertexColor(unpack(powerColors[UnitPowerType(unit)]));
141 end
142
143 local function updateName(frame, unit)
144     local name = UnitName(unit);
145     if not name then return end
146     frame.name:SetText(ssub(name, 1, 10));
147
148     local _, class = UnitClass(unit);
149     local color = RAID_CLASS_COLORS[class];
150     if color then frame.name:SetVertexColor(color.r, color.g, color.b) end
151 end
152
153 local function updateHealPred(frame, unit)
154     local incoming = UnitGetIncomingHeals(unit) or 0;
155     if incoming > 0 then
156         local max = frame.health.max;
157         local space = width - frame.health:GetWidth() + 1;
158         local pred = (incoming / max) * width;
159         frame.healpred:SetWidth(min(space, pred));
160         frame.healpred:Show();
161     else
162         frame.healpred:Hide();
163     end
164 end
165
166 local function updateShield(frame, unit)
167     local shield = UnitGetTotalAbsorbs(unit) or 0;
168     if shield > 0 then
169         local max = frame.health.max;
170         local space = width - frame.health:GetWidth();
171         shield = (shield / max) * width;
172         if space == 0 then
173             frame.shield:Hide();
174             frame.shieldhl:Show();
175         elseif space < shield then
176             frame.shield:SetWidth(space);
177             frame.shield:Show();
178             frame.shieldhl:Show();
179         else
180             frame.shield:SetWidth(shield);
181             frame.shield:Show();
182             frame.shieldhl:Hide();
183         end
184     else
185         frame.shield:Hide();
186         frame.shieldhl:Hide();
187     end
188 end
189
190 local function updateHealAbsorb(frame, unit)
191     local absorb = UnitGetTotalHealAbsorbs(unit) or 0;
192     if absorb > 0 then
193         local max = frame.health.max;
194         local space = frame.health:GetWidth();
195         absorb = (absorb / max) * width;
196         frame.healabsorb:SetWidth(min(space, absorb));
197         frame.healabsorb:Show();
198     else
199         frame.healabsorb:Hide();
200     end
201 end
202
203 local function updateAuras(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 unit == frame.unit 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 eventFuncs = {
348     ["UNIT_HEALTH"] = function(frame)
349         updateHealth(frame, frame.displayed);
350         updateHealthText(frame, frame.displayed);
351         updateShield(frame, frame.displayed);
352         updateHealAbsorb(frame, frame.displayed);
353         -- no heal prediction update, that doesn't overflow too much
354     end,
355     ["UNIT_POWER"] = function(frame)
356         updatePower(frame, frame.displayed);
357         updatePowerText(frame, frame.displayed);
358     end,
359     ["UNIT_AURA"] = function(frame)
360         updateAuras(frame, frame.displayed);
361     end,
362     ["UNIT_HEAL_PREDICTION"] = function(frame)
363         updateHealPred(frame, frame.displayed);
364     end,
365     ["UNIT_ABSORB_AMOUNT_CHANGED"] = function(frame)
366         updateShield(frame, frame.displayed);
367     end,
368     ["UNIT_HEAL_ABSORB_AMOUNT_CHANGED"] = function(frame)
369         updateHealAbsorb(frame, frame.displayed);
370     end,
371     ["UNIT_THREAT_SITUATION_UPDATE"] = function(frame)
372         updateAggro(frame, frame.displayed);
373     end,
374     ["UNIT_MAXHEALTH"] = function(frame)
375         updateMaxHealth(frame, frame.displayed);
376         updateHealth(frame, frame.displayed);
377         updateHealthText(frame, frame.displayed);
378         updateShield(frame, frame.displayed);
379         updateHealAbsorb(frame, frame.displayed);
380     end,
381     ["UNIT_MAXPOWER"] = function(frame)
382         updateMaxPower(frame, frame.displayed);
383         updatePower(frame, frame.displayed);
384         updatePowerText(frame, frame.displayed);
385     end,
386     ["UNIT_DISPLAYPOWER"] = function(frame)
387         updatePowerColor(frame, frame.displayed);
388     end,
389     ["UNIT_NAME_UPDATE"] = function(frame)
390         updateName(frame, frame.displayed);
391     end,
392     ["UNIT_CONNECTION"] = function(frame)
393         updateHealthText(frame, frame.displayed);
394         updatePowerText(frame, frame.displayed);
395     end,
396     ["PLAYER_ROLES_ASSIGNED"] = function(frame)
397         updateRole(frame, frame.unit);
398     end,
399     ["UNIT_LEVEL"] = function(frame)
400         updateLevelText(frame, frame.unit);
401     end,
402     ["PLAYER_LEVEL_UP"] = function(frame, arg1)
403         updateLevelText(frame, frame.unit, arg1);
404     end,
405     ["PLAYER_UPDATE_RESTING"] = function(frame)
406         updateStatus(frame, frame.unit);
407     end,
408     ["PLAYER_REGEN_DISABLED"] = function(frame)
409         frame.inCombat = true;
410         updateStatus(frame, frame.unit);
411     end,
412     ["PLAYER_REGEN_ENABLED"] = function(frame)
413         frame.inCombat = false;
414         updateStatus(frame, frame.unit);
415     end,
416     ["UNIT_FACTION"] = function(frame)
417         updatePVP(frame, frame.unit);
418     end,
419     ["PARTY_LEADER_CHANGED"] = function(frame)
420         updateLeaderIcon(frame, frame.unit);
421     end,
422     ["UPDATE_ALL_BARS"] = function(frame)
423         updateVehicle(frame);
424         updateMaxHealth(frame, frame.displayed);
425         updateMaxPower(frame, frame.displayed);
426         updateHealth(frame, frame.displayed);
427         updateHealthText(frame, frame.displayed);
428         updatePower(frame, frame.displayed);
429         updatePowerText(frame, frame.displayed);
430         updateAuras(frame, frame.displayed);
431         updateShield(frame, frame.displayed);
432         updateHealPred(frame, frame.displayed);
433         updateHealAbsorb(frame, frame.displayed);
434         updatePowerColor(frame, frame.displayed);
435         updateAggro(frame, frame.displayed);
436         updateName(frame, frame.displayed);
437         updateRole(frame, frame.unit);
438         updateLevelText(frame, frame.unit);
439         updateStatus(frame, frame.unit);
440         updatePVP(frame, frame.unit);
441         updateLeaderIcon(frame, frame.unit);
442     end,
443 };
444 eventFuncs["UNIT_HEALTH_FREQUENT"] = eventFuncs["UNIT_HEALTH"];
445 eventFuncs["UNIT_ENTERED_VEHICLE"] = eventFuncs["UPDATE_ALL_BARS"];
446 eventFuncs["UNIT_EXITED_VEHICLE"] = eventFuncs["UPDATE_ALL_BARS"];
447 eventFuncs["UNIT_PET"] = eventFuncs["UPDATE_ALL_BARS"];
448 eventFuncs["GROUP_ROSTER_UPDATE"] = eventFuncs["UPDATE_ALL_BARS"];
449 eventFuncs["PLAYER_ENTERING_WORLD"] = eventFuncs["UPDATE_ALL_BARS"];
450 eventFuncs["PLAYER_TARGET_CHANGED"] = eventFuncs["UPDATE_ALL_BARS"];
451
452 function M.UnitEvent(self, event, arg1)
453     eventFuncs[event](self, arg1);
454 end
455
456 function M.LoadChar()
457     width = Settings.Character.Width;
458 end