9713040 - Incremental refactor of raid frames
[wowui.git] / kehys / events.lua
1 -- events.lua
2 -- 2019 Aleksi Blinnikka
3 local _;
4 local unpack = unpack;
5 local ssub = string.sub;
6 local min = math.min;
7 local UnitName, UnitClass, UnitExists = UnitName, UnitClass, UnitExists;
8 local UnitDebuff, UnitIsCharmed = UnitDebuff, UnitIsCharmed;
9 local UnitHealth, UnitHealthMax = UnitHealth, UnitHealthMax;
10 local UnitIsAFK, UnitIsDND = UnitIsAFK, UnitIsDND;
11 local UnitGetIncomingHeals, UnitGetTotalAbsorbs = UnitGetIncomingHeals, UnitGetTotalAbsorbs;
12 local UnitThreatSituation, GetThreatStatusColor = UnitThreatSituation, GetThreatStatusColor;
13 local UnitIsDeadOrGhost, UnitIsConnected = UnitIsDeadOrGhost, UnitIsConnected;
14 local UnitGetTotalHealAbsorbs = UnitGetTotalHealAbsorbs;
15 local UnitHasVehicleUI, UnitTargetsVehicleInRaidUI = UnitHasVehicleUI, UnitTargetsVehicleInRaidUI;
16 local GetReadyCheckTimeLeft, GetReadyCheckStatus = GetReadyCheckTimeLeft, GetReadyCheckStatus;
17 local UnitGroupRolesAssigned = UnitGroupRolesAssigned;
18 local GetRaidTargetIndex, SetRaidTargetIconTexture = GetRaidTargetIndex, SetRaidTargetIconTexture;
19 local RAID_CLASS_COLORS = RAID_CLASS_COLORS;
20 local READY_CHECK_READY_TEXTURE = READY_CHECK_READY_TEXTURE;
21 local READY_CHECK_NOT_READY_TEXTURE = READY_CHECK_NOT_READY_TEXTURE;
22 local READY_CHECK_WAITING_TEXTURE = READY_CHECK_WAITING_TEXTURE;
23
24 local _, addon = ...;
25 local baseColor = {0, 0, 0};
26 local overlayColorDispel = {1, 0.5, 0, 0.5};
27 local overlayColorCharm = {0.8, 0, 1, 0.5};
28 local overlayColorAlert = {1, 0, 0, 0.5};
29 local width = 80;
30
31 function addon.RegisterEvents(frame)
32     frame:RegisterEvent("PLAYER_ENTERING_WORLD");
33     frame:RegisterEvent("PLAYER_ROLES_ASSIGNED");
34     frame:RegisterEvent("READY_CHECK");
35     frame:RegisterEvent("READY_CHECK_FINISHED");
36     frame:RegisterEvent("GROUP_ROSTER_UPDATE");
37     frame:RegisterEvent("RAID_TARGET_UPDATE");
38     if frame.unit == "focus" then frame:RegisterEvent("PLAYER_FOCUS_CHANGED") end
39 end
40
41 function addon.RegisterUnitEvents(frame)
42     -- events are taken from FrameXML/CompactUnitFrame.lua
43     local displayed = frame.unit ~= frame.displayed and frame.displayed or nil;
44     frame:RegisterUnitEvent("UNIT_HEALTH_FREQUENT", frame.unit, displayed);
45     frame:RegisterUnitEvent("UNIT_MAXHEALTH", 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("PLAYER_FLAGS_CHANGED", frame.unit, displayed);
54     frame:RegisterUnitEvent("READY_CHECK_CONFIRM", frame.unit, displayed);
55     frame:RegisterUnitEvent("UNIT_ENTERED_VEHICLE", frame.unit, displayed);
56     frame:RegisterUnitEvent("UNIT_EXITED_VEHICLE", frame.unit, displayed);
57     frame:RegisterUnitEvent("UNIT_PET", frame.unit, displayed);
58 end
59 local registerUnitEvents = addon.RegisterUnitEvents;
60
61 local function updateText(frame, unit)
62     if UnitIsDeadOrGhost(unit) then
63         frame.dead = true;
64         frame.text:SetText("Dead");
65         frame.text:Show();
66     elseif not UnitIsConnected(unit) then
67         frame.text:SetText("DC");
68         frame.text:Show();
69     elseif UnitIsAFK(unit) then
70         frame.text:SetText("afk");
71         frame.text:Show();
72     elseif UnitIsDND(unit) then
73         frame.text:SetText("dnd");
74         frame.text:Show();
75     else
76         frame.text:Hide();
77     end
78 end
79
80 local function updateMaxHealth(frame, unit)
81     frame.health.max = UnitHealthMax(unit);
82 end
83
84 local function updateHealth(frame, unit)
85     local current, max = UnitHealth(unit), frame.health.max;
86     if current > max or max <= 0 then
87         -- somehow current health has gone over the maximum (missed maxhealth event possibly)
88         -- just put health bar full and update max health for next event
89         frame.health:SetWidth(width);
90         frame.health.width = width;
91         updateMaxHealth(frame, unit);
92         frame.health:Show();
93     elseif current <= 0 or UnitIsDeadOrGhost(unit) then
94         frame.health:Hide();
95         return updateText(frame, unit); -- update death
96     else
97         local w = current/max*width;
98         frame.health:SetWidth(w);
99         frame.health.width = w;
100         frame.health:Show();
101     end
102
103     if frame.dead and current > 0 then
104         frame.dead = nil;
105         updateText(frame, unit); -- update revive
106     end
107 end
108
109 local function updateName(frame, unit)
110     local name = UnitName(unit);
111     if not name then return end
112     name = ssub(name, 1, 6);
113     if frame.unit == unit then
114         frame.name:SetText(name);
115     else
116         frame.name:SetFormattedText("-%s", name);
117     end
118
119     local _, class = UnitClass(unit);
120     local color = RAID_CLASS_COLORS[class];
121     if color then frame.name:SetVertexColor(color.r, color.g, color.b) end
122 end
123
124 local function updateHealPred(frame, unit)
125     local incoming = UnitGetIncomingHeals(unit) or 0;
126     if incoming > 0 then
127         incoming = (incoming / frame.health.max) * width;
128         -- always at least 1 pixel space for heal prediction
129         frame.healpred:SetWidth(min(width - frame.health.width + 1, incoming));
130         frame.healpred:Show();
131     else
132         frame.healpred:Hide();
133     end
134 end
135
136 local function updateShield(frame, unit)
137     local shield = UnitGetTotalAbsorbs(unit) or 0;
138     if shield > 0 then
139         local space = width - frame.health.width;
140         shield = (shield / frame.health.max) * width;
141         if space == 0 then
142             frame.shield:Hide();
143             frame.shieldhl:Show();
144         elseif space < shield then
145             frame.shield:SetWidth(space);
146             frame.shield:Show();
147             frame.shieldhl:Show();
148         else
149             frame.shield:SetWidth(shield);
150             frame.shield:Show();
151             frame.shieldhl:Hide();
152         end
153     else
154         frame.shield:Hide();
155         frame.shieldhl:Hide();
156     end
157 end
158
159 local function updateHealAbsorb(frame, unit)
160     local absorb = UnitGetTotalHealAbsorbs(unit) or 0;
161     if absorb > 0 then
162         absorb = (absorb / frame.health.max) * width;
163         frame.healabsorb:SetWidth(min(frame.health.width, absorb));
164         frame.healabsorb:Show();
165     else
166         frame.healabsorb:Hide();
167     end
168 end
169
170 local function updateAuras(frame, unit)
171     -- don't overlay charmed when in vehicle
172     if UnitIsCharmed(unit) and unit == frame.unit then
173         if frame.overlay.color ~= overlayColorCharm then
174             frame.overlay:SetVertexColor(unpack(overlayColorCharm));
175             frame.overlay.color = overlayColorCharm;
176             frame.overlay:Show();
177         end
178     elseif UnitDebuff(unit, 1, "RAID") ~= nil then
179         -- something dispellable
180         if frame.overlay.color ~= overlayColorDispel then
181             frame.overlay:SetVertexColor(unpack(overlayColorDispel));
182             frame.overlay.color = overlayColorDispel;
183             frame.overlay:Show();
184         end
185     else
186         if frame.overlay.color ~= nil then
187             frame.overlay.color = nil;
188             frame.overlay:Hide();
189         end
190     end
191 end
192
193 local function updateAggro(frame, unit)
194     local status = UnitThreatSituation(unit);
195     if status and status > 0 then
196         frame.base:SetVertexColor(GetThreatStatusColor(status));
197     else
198         frame.base:SetVertexColor(unpack(baseColor));
199     end
200 end
201
202 local function updateVehicle(frame)
203     local shouldTargetVehicle = UnitHasVehicleUI(frame.unit) and
204         UnitTargetsVehicleInRaidUI(frame.unit) and UnitExists(frame.vehicle);
205     if shouldTargetVehicle then
206         if not frame.inVehicle then
207             frame.inVehicle = true;
208             frame.displayed = frame.vehicle;
209             registerUnitEvents(frame);
210         end
211     elseif frame.inVehicle then
212         frame.inVehicle = false;
213         frame.displayed = frame.unit;
214         registerUnitEvents(frame);
215     end
216 end
217
218 local function updateRole(frame, unit)
219     local role = UnitGroupRolesAssigned(unit);
220     if role == "HEALER" then
221         frame.role:SetTexCoord(0.75, 1, 0, 1);
222         frame.role:Show();
223     elseif role == "TANK" then
224         frame.role:SetTexCoord(0.5, 0.75, 0, 1);
225         frame.role:Show();
226     else
227         frame.role:Hide();
228     end
229 end
230
231 local function updateReadyCheck(frame, unit)
232     local status = GetReadyCheckStatus(unit);
233     if status == "ready" then
234         frame.ready:SetTexture(READY_CHECK_READY_TEXTURE);
235         frame.ready:Show()
236     elseif status == "notready" then
237         frame.ready:SetTexture(READY_CHECK_NOT_READY_TEXTURE);
238         frame.ready:Show()
239     elseif status == "waiting" then
240         frame.ready:SetTexture(READY_CHECK_WAITING_TEXTURE);
241         frame.ready:Show()
242     else
243         frame.ready:Hide()
244     end
245 end
246
247 local function updateRaidMarker(frame, unit)
248     local index = GetRaidTargetIndex(unit);
249     if index then
250         SetRaidTargetIconTexture(frame.targeticon, index);
251         frame.targeticon:Show();
252     else
253         frame.targeticon:Hide();
254     end
255 end
256
257 local eventFuncs = {
258     ["UNIT_HEALTH"] = function(frame)
259         updateHealth(frame, frame.displayed);
260         updateShield(frame, frame.displayed);
261         updateHealAbsorb(frame, frame.displayed);
262         -- no heal prediction update, that doesn't overflow too much
263     end,
264     ["UNIT_AURA"] = function(frame)
265         updateAuras(frame, frame.displayed);
266     end,
267     ["UNIT_HEAL_PREDICTION"] = function(frame)
268         updateHealPred(frame, frame.displayed);
269     end,
270     ["UNIT_ABSORB_AMOUNT_CHANGED"] = function(frame)
271         updateShield(frame, frame.displayed);
272     end,
273     ["UNIT_HEAL_ABSORB_AMOUNT_CHANGED"] = function(frame)
274         updateHealAbsorb(frame, frame.displayed);
275     end,
276     ["UNIT_THREAT_SITUATION_UPDATE"] = function(frame)
277         updateAggro(frame, frame.displayed);
278     end,
279     ["UNIT_MAXHEALTH"] = function(frame)
280         updateMaxHealth(frame, frame.displayed);
281         updateHealth(frame, frame.displayed);
282         updateShield(frame, frame.displayed);
283         updateHealAbsorb(frame, frame.displayed);
284     end,
285     ["UNIT_NAME_UPDATE"] = function(frame)
286         updateName(frame, frame.unit);
287     end,
288     ["UNIT_CONNECTION"] = function(frame)
289         updateText(frame, frame.displayed);
290     end,
291     ["PLAYER_ROLES_ASSIGNED"] = function(frame)
292         updateRole(frame, frame.unit);
293     end,
294     ["READY_CHECK"] = function(frame)
295         updateReadyCheck(frame, frame.unit);
296     end,
297     ["RAID_TARGET_UPDATE"] = function(frame)
298         updateRaidMarker(frame, frame.displayed);
299     end,
300     ["UPDATE_ALL_BARS"] = function(frame)
301         updateRole(frame, frame.unit);
302         updateVehicle(frame);
303         updateMaxHealth(frame, frame.displayed);
304         updateHealth(frame, frame.displayed);
305         updateText(frame, frame.displayed);
306         updateAuras(frame, frame.displayed);
307         updateShield(frame, frame.displayed);
308         updateHealPred(frame, frame.displayed);
309         updateHealAbsorb(frame, frame.displayed);
310         updateAggro(frame, frame.displayed);
311         updateName(frame, frame.unit);
312         updateReadyCheck(frame, frame.unit);
313         updateRaidMarker(frame, frame.displayed);
314     end,
315 };
316 eventFuncs["UNIT_HEALTH_FREQUENT"] = eventFuncs["UNIT_HEALTH"];
317 eventFuncs["PLAYER_FLAGS_CHANGED"] = eventFuncs["UNIT_CONNECTION"];
318 eventFuncs["READY_CHECK_CONFIRM"] = eventFuncs["READY_CHECK"];
319 eventFuncs["READY_CHECK_FINISHED"] = eventFuncs["READY_CHECK"];
320 eventFuncs["UNIT_ENTERED_VEHICLE"] = eventFuncs["UPDATE_ALL_BARS"];
321 eventFuncs["UNIT_EXITED_VEHICLE"] = eventFuncs["UPDATE_ALL_BARS"];
322 eventFuncs["UNIT_PET"] = eventFuncs["UPDATE_ALL_BARS"];
323 eventFuncs["GROUP_ROSTER_UPDATE"] = eventFuncs["UPDATE_ALL_BARS"];
324 eventFuncs["PLAYER_ENTERING_WORLD"] = eventFuncs["UPDATE_ALL_BARS"];
325 eventFuncs["PLAYER_FOCUS_CHANGED"] = eventFuncs["UPDATE_ALL_BARS"];
326
327 function addon.UnitEvent(self, event)
328     return eventFuncs[event](self);
329 end