faeff8013b834599ea0e2f7cb7f5ea6e8c505626
[wowui.git] / OmaRF / Events.lua
1 -- Events.lua
2 local _;
3 local unpack = unpack;
4 local ssub = string.sub;
5 local min = math.min;
6 local UnitName, UnitClass, UnitExists = UnitName, UnitClass, UnitExists;
7 local UnitDebuff, UnitIsCharmed = UnitDebuff, UnitIsCharmed;
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 UnitHasIncomingResurrection = UnitHasIncomingResurrection;
14 local UnitGetTotalHealAbsorbs = UnitGetTotalHealAbsorbs;
15 local UnitHasVehicleUI, UnitTargetsVehicleInRaidUI = UnitHasVehicleUI, UnitTargetsVehicleInRaidUI;
16 local GetReadyCheckTimeLeft, GetReadyCheckStatus = GetReadyCheckTimeLeft, GetReadyCheckStatus;
17 local UnitGroupRolesAssigned = UnitGroupRolesAssigned;
18 local RAID_CLASS_COLORS = RAID_CLASS_COLORS;
19 local READY_CHECK_READY_TEXTURE = READY_CHECK_READY_TEXTURE;
20 local READY_CHECK_NOT_READY_TEXTURE = READY_CHECK_NOT_READY_TEXTURE;
21 local READY_CHECK_WAITING_TEXTURE = READY_CHECK_WAITING_TEXTURE;
22
23 local checkIndicators = OmaRFIndicators.CheckIndicators;
24
25 local Settings = OmaRFSettings;
26 local baseColor = Settings.BaseColor;
27 local overlayColorDispel = Settings.OverlayColorDispel;
28 local overlayColorCharm = Settings.OverlayColorCharm;
29 local overlayColorAlert = Settings.OverlayColorAlert;
30 local powerColors = Settings.PowerColors;
31 local width = 10;
32
33 local M = {};
34 OmaRFEvents = M;
35 function M.RegisterEvents(frame)
36     -- events are taken from FrameXML/CompactUnitFrame.lua
37     -- TODO raid marker support,
38     -- player flags support (/afk, /dnd)
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     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_NAME_UPDATE", frame.unit, displayed);
47     frame:RegisterUnitEvent("UNIT_AURA", frame.unit, displayed);
48     frame:RegisterUnitEvent("UNIT_HEAL_PREDICTION", frame.unit, displayed);
49     frame:RegisterUnitEvent("UNIT_ABSORB_AMOUNT_CHANGED", frame.unit, displayed);
50     frame:RegisterUnitEvent("UNIT_HEAL_ABSORB_AMOUNT_CHANGED", frame.unit, displayed);
51     frame:RegisterUnitEvent("UNIT_THREAT_SITUATION_UPDATE", frame.unit, displayed);
52     frame:RegisterUnitEvent("UNIT_CONNECTION", frame.unit, displayed);
53     frame:RegisterUnitEvent("INCOMING_RESURRECT_CHANGED", frame.unit, displayed);
54 end
55 local registerEvents = M.RegisterEvents;
56
57 local function updateHealth(frame, unit)
58     local current, max = UnitHealth(unit), frame.health.max;
59     frame.health:Show();
60     -- sanity check, occasionally UnitHealthMax gives zero
61     if current > max then
62         -- somehow current health has gone over the maximum (missed maxhealth event)
63         frame.health.max = UnitHealthMax(unit);
64         max = frame.health.max;
65         if current > max then
66             -- error state, still over maximum
67             frame.health:SetWidth(width);
68             return;
69         end
70     elseif max > 0 then
71         frame.health:SetWidth(current/frame.health.max*width);
72     else
73         frame.health:SetWidth(width);
74         return;
75     end
76
77     if UnitIsDeadOrGhost(unit) then
78         frame.health:Hide();
79     end
80 end
81
82 local function updateText(frame, unit)
83     local current, max = UnitHealth(unit), frame.health.max;
84     local healthLost = max - current;
85     if UnitIsDeadOrGhost(unit) then
86         frame.text:SetText("Dead");
87         frame.text:Show();
88     elseif not UnitIsConnected(unit) then
89         frame.text:SetText("DC");
90         frame.text:Show();
91     elseif healthLost > 0 then
92         if healthLost > 1200000000 then -- 1.2B
93             frame.text:SetFormattedText("-%.1fB", healthLost / 1000000000);
94         elseif healthLost > 1200000 then -- 1.2M
95             frame.text:SetFormattedText("-%.1fM", healthLost / 1000000);
96         elseif healthLost > 1000 then -- 1K
97             frame.text:SetFormattedText("-%dK", healthLost / 1000);
98         else
99             frame.text:SetFormattedText("-%d", healthLost)
100         end
101         frame.text:Show();
102     else
103         frame.text:Hide();
104     end
105 end
106
107 local function updateIncomingRes(frame, unit)
108     if UnitHasIncomingResurrection(unit) then
109         frame.rez:Show();
110     else
111         frame.rez:Hide();
112     end
113 end
114
115 local function updateMaxHealth(frame, unit)
116     frame.health.max = UnitHealthMax(unit);
117 end
118
119 local function updatePower(frame, unit)
120     local current, max = UnitPower(unit), frame.mana.max;
121     -- sanity check, occasionally UnitPowerMax gives zero
122     if current > max then
123         frame.mana.max = UnitPowerMax(unit);
124         max = frame.mana.max;
125         if current > max then
126             -- error
127             frame.mana:SetWidth(width);
128             return;
129         end
130     elseif max > 0 then
131         frame.mana:SetWidth(UnitPower(unit)/max*width);
132     else
133         frame.mana:SetWidth(width);
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, 6));
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 < shield then
175             frame.shield:SetWidth(space);
176             frame.shieldhl:Show();
177         else
178             frame.shield:SetWidth(shield);
179             frame.shieldhl:Hide();
180         end
181         frame.shield:Show();
182     else
183         frame.shield:Hide();
184         frame.shieldhl:Hide();
185     end
186 end
187
188 local function updateHealAbsorb(frame, unit)
189     local absorb = UnitGetTotalHealAbsorbs(unit) or 0;
190     if absorb > 0 then
191         local max = frame.health.max;
192         local space = frame.health:GetWidth();
193         absorb = (absorb / max) * width;
194         frame.healabsorb:SetWidth(min(space, absorb));
195         frame.healabsorb:Show();
196     else
197         frame.healabsorb:Hide();
198     end
199 end
200
201 local function updateAuras(frame, unit)
202     checkIndicators(frame, unit);
203     if UnitDebuff(unit, 1, "RAID") ~= nil then
204         -- something dispellable
205         if frame.overlay.color ~= overlayColorDispel then
206             frame.overlay:SetVertexColor(unpack(overlayColorDispel));
207             frame.overlay:Show();
208             frame.overlay.color = overlayColorDispel;
209         end
210     -- don't overlay charmed when in vehicle
211     elseif UnitIsCharmed(unit) and unit == frame.unit then
212         if frame.overlay.color ~= overlayColorCharm then
213             frame.overlay:SetVertexColor(unpack(overlayColorCharm));
214             frame.overlay:Show();
215             frame.overlay.color = overlayColorCharm;
216         end
217     else
218         if frame.overlay.color ~= nil then
219             frame.overlay:Hide();
220             frame.overlay.color = nil;
221         end
222     end
223 end
224
225 local function updateAggro(frame, unit)
226     local status = UnitThreatSituation(unit);
227     if status and status > 0 then
228         frame.base:SetVertexColor(GetThreatStatusColor(status));
229     else
230         frame.base:SetVertexColor(unpack(baseColor));
231     end
232 end
233
234 local function updateVehicle(frame)
235     local shouldTargetVehicle = UnitHasVehicleUI(frame.unit) and UnitTargetsVehicleInRaidUI(frame.unit) and UnitExists(frame.vehicle);
236     if shouldTargetVehicle then
237         if not frame.inVehicle then
238             frame.inVehicle = true;
239             frame.displayed = frame.vehicle;
240             registerEvents(frame);
241         end
242     elseif frame.inVehicle then
243         frame.inVehicle = false;
244         frame.displayed = frame.unit;
245         registerEvents(frame);
246     end
247 end
248
249 local function updateRole(frame, unit)
250     local role = UnitGroupRolesAssigned(unit);
251     if role == "HEALER" then
252         frame.role:SetTexCoord(0.75, 1, 0, 1);
253         frame.role:Show();
254     elseif role == "TANK" then
255         frame.role:SetTexCoord(0.5, 0.75, 0, 1);
256         frame.role:Show();
257     else
258         frame.role:Hide();
259     end
260 end
261
262 local function updateReadyCheck(frame, unit)
263     if GetReadyCheckTimeLeft() <= 0 then return end
264     local status = GetReadyCheckStatus(unit);
265     if status == "ready" then
266         frame.ready:SetTexture(READY_CHECK_READY_TEXTURE);
267         frame.ready:Show()
268     elseif status == "notready" then
269         frame.ready:SetTexture(READY_CHECK_NOT_READY_TEXTURE);
270         frame.ready:Show()
271     elseif status == "waiting" then
272         frame.ready:SetTexture(READY_CHECK_WAITING_TEXTURE);
273         frame.ready:Show()
274     else
275         frame.ready:Hide()
276     end
277 end
278
279 local eventFuncs = {
280     ["UNIT_HEALTH"] = function(frame)
281         updateHealth(frame, frame.displayed);
282         updateText(frame, frame.displayed);
283         updateShield(frame, frame.displayed);
284         updateHealAbsorb(frame, frame.displayed);
285         -- no heal prediction update, that doesn't overflow too much
286     end,
287     ["UNIT_POWER"] = function(frame)
288         updatePower(frame, frame.displayed);
289     end,
290     ["UNIT_AURA"] = function(frame)
291         updateAuras(frame, frame.displayed);
292     end,
293     ["UNIT_HEAL_PREDICTION"] = function(frame)
294         updateHealPred(frame, frame.displayed);
295     end,
296     ["UNIT_ABSORB_AMOUNT_CHANGED"] = function(frame)
297         updateShield(frame, frame.displayed);
298     end,
299     ["UNIT_HEAL_ABSORB_AMOUNT_CHANGED"] = function(frame)
300         updateHealAbsorb(frame, frame.displayed);
301     end,
302     ["UNIT_THREAT_SITUATION_UPDATE"] = function(frame)
303         updateAggro(frame, frame.displayed);
304     end,
305     ["UNIT_MAXHEALTH"] = function(frame)
306         updateMaxHealth(frame, frame.displayed);
307         updateHealth(frame, frame.displayed);
308         updateText(frame, frame.displayed);
309         updateShield(frame, frame.displayed);
310         updateHealAbsorb(frame, frame.displayed);
311     end,
312     ["UNIT_MAXPOWER"] = function(frame)
313         updateMaxPower(frame, frame.displayed);
314         updatePower(frame, frame.displayed);
315     end,
316     ["UNIT_DISPLAYPOWER"] = function(frame)
317         updatePowerColor(frame, frame.displayed);
318     end,
319     ["UNIT_NAME_UPDATE"] = function(frame)
320         updateName(frame, frame.displayed);
321     end,
322     ["UNIT_CONNECTION"] = function(frame)
323         updateText(frame, frame.displayed);
324     end,
325     ["INCOMING_RESURRECT_CHANGED"] = function(frame)
326         updateIncomingRes(frame, frame.unit);
327     end,
328     ["PARTY_MEMBER_ENABLE"] = function(frame)
329         -- new power info possibly (FrameXML/CompactUnitFrame.lua)
330         updateMaxPower(frame, frame.displayed);
331         updatePowerColor(frame, frame.displayed);
332     end,
333     ["PLAYER_ROLES_ASSIGNED"] = function(frame)
334         updateRole(frame, frame.unit);
335     end,
336     ["READY_CHECK"] = function(frame)
337         updateReadyCheck(frame, frame.unit);
338     end,
339     ["UPDATE_ALL_BARS"] = function(frame)
340         updateVehicle(frame);
341         updateMaxHealth(frame, frame.displayed);
342         updateMaxPower(frame, frame.displayed);
343         updateHealth(frame, frame.displayed);
344         updateText(frame, frame.displayed);
345         updatePower(frame, frame.displayed);
346         updateAuras(frame, frame.displayed);
347         updateShield(frame, frame.displayed);
348         updateHealPred(frame, frame.displayed);
349         updateHealAbsorb(frame, frame.displayed);
350         updatePowerColor(frame, frame.displayed);
351         updateIncomingRes(frame, frame.unit);
352         updateReadyCheck(frame, frame.unit);
353         updateAggro(frame, frame.displayed);
354         updateName(frame, frame.displayed);
355         updateRole(frame, frame.unit);
356     end,
357 };
358 eventFuncs["UNIT_HEALTH_FREQUENT"] = eventFuncs["UNIT_HEALTH"];
359 eventFuncs["PARTY_MEMBER_DISABLE"] = eventFuncs["PARTY_MEMBER_ENABLE"];
360 eventFuncs["READY_CHECK_CONFIRM"] = eventFuncs["READY_CHECK"];
361 eventFuncs["READY_CHECK_FINISHED"] = eventFuncs["READY_CHECK"];
362 eventFuncs["UNIT_ENTERED_VEHICLE"] = eventFuncs["UPDATE_ALL_BARS"];
363 eventFuncs["UNIT_EXITED_VEHICLE"] = eventFuncs["UPDATE_ALL_BARS"];
364 eventFuncs["UNIT_PET"] = eventFuncs["UPDATE_ALL_BARS"];
365 eventFuncs["GROUP_ROSTER_UPDATE"] = eventFuncs["UPDATE_ALL_BARS"];
366 eventFuncs["PLAYER_ENTERING_WORLD"] = eventFuncs["UPDATE_ALL_BARS"];
367
368 function M.UnitEvent(self, event)
369     eventFuncs[event](self);
370 end
371
372 function M.LoadChar()
373     width = Settings.Character.Width;
374 end