6e06bfb - Fix character settings loading
[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 overlayColorDispel = Settings.OverlayColorDispel;
26 local overlayColorCharm = Settings.OverlayColorCharm;
27 local overlayColorAlert = Settings.OverlayColorAlert;
28 local powerColors = Settings.PowerColors;
29 local width = 10;
30
31 local M = {};
32 OmaUFEvents = M;
33 function M.RegisterEvents(frame)
34     -- events are taken from FrameXML/CompactUnitFrame.lua
35     -- TODO raid marker support,
36     -- player flags support (/afk, /dnd)
37     local displayed = frame.unit ~= frame.displayed and frame.displayed or nil;
38     frame:RegisterUnitEvent("UNIT_HEALTH", frame.unit, displayed);
39     frame:RegisterUnitEvent("UNIT_HEALTH_FREQUENT", frame.unit, displayed);
40     frame:RegisterUnitEvent("UNIT_MAXHEALTH", frame.unit, displayed);
41     frame:RegisterUnitEvent("UNIT_POWER", frame.unit, displayed);
42     frame:RegisterUnitEvent("UNIT_MAXPOWER", frame.unit, displayed);
43     frame:RegisterUnitEvent("UNIT_DISPLAYPOWER", 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
150     local _, class = UnitClass(unit);
151     local color = RAID_CLASS_COLORS[class];
152     if color then frame.name:SetVertexColor(color.r, color.g, color.b) end
153 end
154
155 local function updateHealPred(frame, unit)
156     local incoming = UnitGetIncomingHeals(unit) or 0;
157     if incoming > 0 then
158         local max = frame.health.max;
159         local space = width - frame.health:GetWidth() + 1;
160         local pred = (incoming / max) * width;
161         frame.healpred:SetWidth(min(space, pred));
162         frame.healpred:Show();
163     else
164         frame.healpred:Hide();
165     end
166 end
167
168 local function updateShield(frame, unit)
169     local shield = UnitGetTotalAbsorbs(unit) or 0;
170     if shield > 0 then
171         local max = frame.health.max;
172         local space = width - frame.health:GetWidth();
173         shield = (shield / max) * width;
174         if space == 0 then
175             frame.shield:Hide();
176             frame.shieldhl:Show();
177         elseif space < shield then
178             frame.shield:SetWidth(space);
179             frame.shield:Show();
180             frame.shieldhl:Show();
181         else
182             frame.shield:SetWidth(shield);
183             frame.shield:Show();
184             frame.shieldhl:Hide();
185         end
186     else
187         frame.shield:Hide();
188         frame.shieldhl:Hide();
189     end
190 end
191
192 local function updateHealAbsorb(frame, unit)
193     local absorb = UnitGetTotalHealAbsorbs(unit) or 0;
194     if absorb > 0 then
195         local max = frame.health.max;
196         local space = frame.health:GetWidth();
197         absorb = (absorb / max) * width;
198         frame.healabsorb:SetWidth(min(space, absorb));
199         frame.healabsorb:Show();
200     else
201         frame.healabsorb:Hide();
202     end
203 end
204
205 local function updateAuras(frame, unit)
206     updateAuraFrames(frame, unit);
207     if UnitIsFriend("player", unit) and UnitDebuff(unit, 1, "RAID") ~= nil then
208         -- something dispellable
209         if frame.overlay.color ~= overlayColorDispel then
210             frame.overlay:SetVertexColor(unpack(overlayColorDispel));
211             frame.overlay:Show();
212             frame.overlay.color = overlayColorDispel;
213         end
214     -- don't overlay charmed when in vehicle
215     elseif UnitIsCharmed(unit) and not frame.inVehicle then
216         if frame.overlay.color ~= overlayColorCharm then
217             frame.overlay:SetVertexColor(unpack(overlayColorCharm));
218             frame.overlay:Show();
219             frame.overlay.color = overlayColorCharm;
220         end
221     else
222         if frame.overlay.color ~= nil then
223             frame.overlay:Hide();
224             frame.overlay.color = nil;
225         end
226     end
227 end
228
229 local function updateAggro(frame, unit)
230     local status = UnitThreatSituation(unit);
231     if status and status > 0 then
232         frame.base:SetVertexColor(GetThreatStatusColor(status));
233     else
234         frame.base:SetVertexColor(unpack(baseColor));
235     end
236 end
237
238 local function updateVehicle(frame)
239     local shouldTargetVehicle = UnitHasVehicleUI(frame.unit) and UnitTargetsVehicleInRaidUI(frame.unit) and UnitExists(frame.vehicle);
240     if shouldTargetVehicle then
241         if not frame.inVehicle then
242             frame.inVehicle = true;
243             frame.displayed = frame.vehicle;
244             registerEvents(frame);
245         end
246     elseif frame.inVehicle then
247         frame.inVehicle = false;
248         frame.displayed = frame.unit;
249         registerEvents(frame);
250     end
251 end
252
253 local function updateRole(frame, unit)
254     local role = UnitGroupRolesAssigned(unit);
255     if role == "HEALER" or role == "TANK" or role == "DAMAGER" then
256         frame.role:SetTexCoord(GetTexCoordsForRoleSmallCircle(role));
257         frame.role:Show();
258     else
259         frame.role:Hide();
260     end
261 end
262
263 local function updateLevelText(frame, unit, levelup)
264     if levelup then
265         -- PLAYER_LEVEL_UP
266         frame.level:SetText(levelup);
267     else
268         local level = UnitLevel(unit);
269         local class = UnitClassification(unit);
270         local leveltext, classtext;
271         if level < 0 then
272             if class == "worldboss" then
273                 leveltext = "Boss";
274             else
275                 leveltext = "??";
276             end
277         else
278             leveltext = level;
279         end
280         if class == "rareelite" then
281             classtext = " Rare Elite";
282         elseif class == "elite" then
283             classtext = " Elite";
284         elseif class == "rare" then
285             classtext = " Rare";
286         else
287             classtext = "";
288         end
289         frame.level:SetFormattedText("%s%s", leveltext, classtext);
290     end
291 end
292
293 local function updateStatus(frame, unit)
294     -- coords from PlayerFrame
295     if frame.inCombat or UnitAffectingCombat(unit) then
296         frame.status:SetTexCoord(0.5, 1, 0, 0.484375);
297         frame.status:Show();
298     elseif unit == "player" and IsResting() then
299         frame.status:SetTexCoord(0, 0.5, 0, 0.421875);
300         frame.status:Show();
301     else
302         frame.status:Hide();
303     end
304 end
305
306 local function updatePVP(frame, unit)
307     if UnitIsPVPFreeForAll(unit) then
308         frame.pvp:SetTexture("Interface\\TARGETINGFRAME\\UI-PVP-FFA");
309         frame.pvp:Show();
310     elseif UnitIsPVP(unit) then
311         local faction = UnitFactionGroup(unit);
312         if faction and faction ~= "Neutral" then
313             -- from PlayerFrame, mercenary checks
314             if UnitIsMercenary(unit) then
315                 if faction == "Horde" then
316                     faction = "Alliance";
317                 elseif faction == "Alliance" then
318                     faction = "Horde";
319                 end
320             end
321             frame.pvp:SetTexture("Interface\\TARGETINGFRAME\\UI-PVP-"..faction);
322             frame.pvp:Show();
323         else
324             frame.pvp:Hide();
325         end
326     else
327         frame.pvp:Hide();
328     end
329 end
330
331 local function updateLeaderIcon(frame, unit)
332     if UnitIsGroupLeader(frame.unit) then
333         if HasLFGRestrictions() then
334             frame.leader:SetTexture("Interface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES");
335             frame.leader:SetTexCoord(0, 0.296875, 0.015625, 0.3125);
336         else
337             frame.leader:SetTexture("Interface\\GROUPFRAME\\UI-Group-LeaderIcon");
338             frame.leader:SetTexCoord(0, 1, 0, 1);
339         end
340         frame.leader:Show();
341     elseif UnitIsGroupAssistant(frame.unit) then
342         frame.leader:SetTexture("Interface\\GROUPFRAME\\UI-Group-AssistantIcon");
343         frame.leader:SetTexCoord(0, 1, 0, 1);
344         frame.leader:Show();
345     else
346         frame.leader:Hide();
347     end
348 end
349
350 local eventFuncs = {
351     ["UNIT_HEALTH"] = function(frame)
352         updateHealth(frame, frame.displayed);
353         updateHealthText(frame, frame.displayed);
354         updateShield(frame, frame.displayed);
355         updateHealAbsorb(frame, frame.displayed);
356         -- no heal prediction update, that doesn't overflow too much
357     end,
358     ["UNIT_POWER"] = function(frame)
359         updatePower(frame, frame.displayed);
360         updatePowerText(frame, frame.displayed);
361     end,
362     ["UNIT_AURA"] = function(frame)
363         updateAuras(frame, frame.displayed);
364     end,
365     ["UNIT_HEAL_PREDICTION"] = function(frame)
366         updateHealPred(frame, frame.displayed);
367     end,
368     ["UNIT_ABSORB_AMOUNT_CHANGED"] = function(frame)
369         updateShield(frame, frame.displayed);
370     end,
371     ["UNIT_HEAL_ABSORB_AMOUNT_CHANGED"] = function(frame)
372         updateHealAbsorb(frame, frame.displayed);
373     end,
374     ["UNIT_THREAT_SITUATION_UPDATE"] = function(frame)
375         updateAggro(frame, frame.displayed);
376     end,
377     ["UNIT_MAXHEALTH"] = function(frame)
378         updateMaxHealth(frame, frame.displayed);
379         updateHealth(frame, frame.displayed);
380         updateHealthText(frame, frame.displayed);
381         updateShield(frame, frame.displayed);
382         updateHealAbsorb(frame, frame.displayed);
383     end,
384     ["UNIT_MAXPOWER"] = function(frame)
385         updateMaxPower(frame, frame.displayed);
386         updatePower(frame, frame.displayed);
387         updatePowerText(frame, frame.displayed);
388     end,
389     ["UNIT_DISPLAYPOWER"] = function(frame)
390         updatePowerColor(frame, frame.displayed);
391     end,
392     ["UNIT_NAME_UPDATE"] = function(frame)
393         updateName(frame, frame.displayed);
394     end,
395     ["UNIT_CONNECTION"] = function(frame)
396         updateHealthText(frame, frame.displayed);
397         updatePowerText(frame, frame.displayed);
398     end,
399     ["PLAYER_ROLES_ASSIGNED"] = function(frame)
400         updateRole(frame, frame.unit);
401     end,
402     ["UNIT_LEVEL"] = function(frame)
403         updateLevelText(frame, frame.unit);
404     end,
405     ["PLAYER_LEVEL_UP"] = function(frame, arg1)
406         updateLevelText(frame, frame.unit, arg1);
407     end,
408     ["PLAYER_UPDATE_RESTING"] = function(frame)
409         updateStatus(frame, frame.unit);
410     end,
411     ["PLAYER_REGEN_DISABLED"] = function(frame)
412         frame.inCombat = true;
413         updateStatus(frame, frame.unit);
414     end,
415     ["PLAYER_REGEN_ENABLED"] = function(frame)
416         frame.inCombat = false;
417         updateStatus(frame, frame.unit);
418     end,
419     ["UNIT_FACTION"] = function(frame)
420         updatePVP(frame, frame.unit);
421     end,
422     ["PARTY_LEADER_CHANGED"] = function(frame)
423         updateLeaderIcon(frame, frame.unit);
424     end,
425     ["UPDATE_ALL_BARS"] = function(frame)
426         updateVehicle(frame);
427         updateMaxHealth(frame, frame.displayed);
428         updateMaxPower(frame, frame.displayed);
429         updateHealth(frame, frame.displayed);
430         updateHealthText(frame, frame.displayed);
431         updatePower(frame, frame.displayed);
432         updatePowerText(frame, frame.displayed);
433         updateAuras(frame, frame.displayed);
434         updateShield(frame, frame.displayed);
435         updateHealPred(frame, frame.displayed);
436         updateHealAbsorb(frame, frame.displayed);
437         updatePowerColor(frame, frame.displayed);
438         updateAggro(frame, frame.displayed);
439         updateName(frame, frame.displayed);
440         updateRole(frame, frame.unit);
441         updateLevelText(frame, frame.unit);
442         updateStatus(frame, frame.unit);
443         updatePVP(frame, frame.unit);
444         updateLeaderIcon(frame, frame.unit);
445     end,
446 };
447 eventFuncs["UNIT_HEALTH_FREQUENT"] = eventFuncs["UNIT_HEALTH"];
448 eventFuncs["UNIT_ENTERED_VEHICLE"] = eventFuncs["UPDATE_ALL_BARS"];
449 eventFuncs["UNIT_EXITED_VEHICLE"] = eventFuncs["UPDATE_ALL_BARS"];
450 eventFuncs["UNIT_PET"] = eventFuncs["UPDATE_ALL_BARS"];
451 eventFuncs["GROUP_ROSTER_UPDATE"] = eventFuncs["UPDATE_ALL_BARS"];
452 eventFuncs["PLAYER_ENTERING_WORLD"] = eventFuncs["UPDATE_ALL_BARS"];
453 eventFuncs["PLAYER_TARGET_CHANGED"] = eventFuncs["UPDATE_ALL_BARS"];
454
455 function M.UnitEvent(self, event, arg1)
456     eventFuncs[event](self, arg1);
457 end
458
459 function M.LoadChar()
460     width = Settings.Character.Width;
461 end