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