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