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