7b95a32 - Add role icon
[wowui.git] / OmaRF / CFrame.lua
1 -- CFrame.lua
2 local _;
3 local unpack, pairs, ipairs = unpack, pairs, ipairs;
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 UnitInParty, UnitInRaid = UnitInParty, UnitInRaid;
11 local UnitGetIncomingHeals, UnitGetTotalAbsorbs = UnitGetIncomingHeals, UnitGetTotalAbsorbs;
12 local UnitThreatSituation, GetThreatStatusColor = UnitThreatSituation, GetThreatStatusColor;
13 local UnitIsDeadOrGhost, UnitIsConnected = UnitIsDeadOrGhost, UnitIsConnected;
14 local UnitHasIncomingResurrection = UnitHasIncomingResurrection;
15 local UnitGetTotalHealAbsorbs = UnitGetTotalHealAbsorbs;
16 local IsInGroup, IsInRaid = IsInGroup, IsInRaid;
17 local UnitHasVehicleUI, UnitTargetsVehicleInRaidUI = UnitHasVehicleUI, UnitTargetsVehicleInRaidUI;
18 local UnitGroupRolesAssigned = UnitGroupRolesAssigned;
19 local CTimerAfter = C_Timer.After;
20 local RAID_CLASS_COLORS = RAID_CLASS_COLORS;
21
22 local Frames = OmaFrames;
23 local registerEvents = OmaFrames.RegisterEvents;
24 local Indicators = OmaIndicators;
25 local checkIndicators = Indicators.CheckIndicators;
26
27 local CFrame = CreateFrame("Frame", "OmaCFrame", UIParent);
28 local party = {};
29 local raid = {};
30 local updaters = {};
31
32 local width, height = Frames.Width, Frames.Height;
33 local anchorX, anchorY = Frames.AnchorX, Frames.AnchorY;
34 local baseColor = Frames.BaseColor;
35 local overlayColorDispel = Frames.OverlayColorDispel;
36 local overlayColorCharm = Frames.OverlayColorCharm;
37 local overlayColorAlert = Frames.OverlayColorAlert;
38 local powerColors = Frames.PowerColors;
39
40 local function updateHealth(frame, unit)
41     local current, max = UnitHealth(unit), frame.health.max;
42     frame.health:Show();
43     -- sanity check, occasionally UnitHealthMax gives zero
44     if current > max then
45         -- somehow current health has gone over the maximum (missed maxhealth event)
46         frame.health.max = UnitHealthMax(unit);
47         max = frame.health.max;
48         if current > max then
49             -- error state, still over maximum
50             frame.health:SetWidth(width);
51             return;
52         end
53     elseif max > 0 then
54         frame.health:SetWidth(current/frame.health.max*width);
55     else
56         frame.health:SetWidth(width);
57         return;
58     end
59
60     if UnitIsDeadOrGhost(unit) then
61         frame.health:Hide();
62     end
63 end
64
65 local function updateText(frame, unit)
66     local current, max = UnitHealth(unit), frame.health.max;
67     local healthLost = max - current;
68     if UnitIsDeadOrGhost(unit) then
69         if UnitHasIncomingResurrection(unit) then
70             frame.text:SetText("Rez");
71         else
72             frame.text:SetText("Dead");
73         end
74     elseif not UnitIsConnected(unit) then
75         frame.text:SetText("DC");
76     elseif healthLost > 0 then
77         if healthLost > 1200000000 then -- 1.2B
78             frame.text:SetFormattedText("-%.1fB", healthLost / 1000000000);
79         elseif healthLost > 1200000 then -- 1.2M
80             frame.text:SetFormattedText("-%.1fM", healthLost / 1000000);
81         elseif healthLost > 1000 then -- 1K
82             frame.text:SetFormattedText("-%dK", healthLost / 1000);
83         else
84             frame.text:SetFormattedText("-%d", healthLost)
85         end
86         frame.text:Show();
87     else
88         frame.text:Hide();
89     end
90 end
91
92 local function updateMaxHealth(frame, unit)
93     frame.health.max = UnitHealthMax(unit);
94 end
95
96 local function updatePower(frame, unit)
97     local current, max = UnitPower(unit), frame.mana.max;
98     -- sanity check, occasionally UnitPowerMax gives zero
99     if current > max then
100         frame.mana.max = UnitPowerMax(unit);
101         max = frame.mana.max;
102         if current > max then
103             -- error
104             frame.mana:SetWidth(width);
105             return;
106         end
107     elseif max > 0 then
108         frame.mana:SetWidth(UnitPower(unit)/max*width);
109     else
110         frame.mana:SetWidth(width);
111     end
112 end
113
114 local function updateMaxPower(frame, unit)
115     frame.mana.max = UnitPowerMax(unit);
116 end
117
118 local function updatePowerColor(frame, unit)
119     frame.mana:SetVertexColor(unpack(powerColors[UnitPowerType(unit)]));
120 end
121
122 local function updateName(frame, unit)
123     local name = UnitName(unit);
124     if not name then return end
125     frame.name:SetText(ssub(name, 1, 6));
126
127     local _, class = UnitClass(unit);
128     local color = RAID_CLASS_COLORS[class];
129     if color then frame.name:SetVertexColor(color.r, color.g, color.b) end
130 end
131
132 local function updateHealPred(frame, unit)
133     local incoming = UnitGetIncomingHeals(unit) or 0;
134     if incoming > 0 then
135         local max = frame.health.max;
136         local space = width - frame.health:GetWidth() + 1;
137         local pred = (incoming / max) * width;
138         frame.healpred:SetWidth(min(space, pred));
139         frame.healpred:Show();
140     else
141         frame.healpred:Hide();
142     end
143 end
144
145 local function updateShield(frame, unit)
146     local shield = UnitGetTotalAbsorbs(unit) or 0;
147     if shield > 0 then
148         local max = frame.health.max;
149         local space = width - frame.health:GetWidth();
150         shield = (shield / max) * width;
151         if space < shield then
152             frame.shield:SetWidth(space);
153             frame.shieldhl:Show();
154         else
155             frame.shield:SetWidth(shield);
156             frame.shieldhl:Hide();
157         end
158         frame.shield:Show();
159     else
160         frame.shield:Hide();
161         frame.shieldhl:Hide();
162     end
163 end
164
165 local function updateHealAbsorb(frame, unit)
166     local absorb = UnitGetTotalHealAbsorbs(unit) or 0;
167     if absorb > 0 then
168         local max = frame.health.max;
169         local space = frame.health:GetWidth();
170         absorb = (absorb / max) * width;
171         frame.healabsorb:SetWidth(min(space, absorb));
172         frame.healabsorb:Show();
173     else
174         frame.healabsorb:Hide();
175     end
176 end
177
178 local function updateAuras(frame, unit)
179     checkIndicators(frame, unit);
180     if UnitDebuff(unit, 1, "RAID") ~= nil then
181         -- something dispellable
182         if frame.overlay.color ~= overlayColorDispel then
183             frame.overlay:SetVertexColor(unpack(overlayColorDispel));
184             frame.overlay:Show();
185             frame.overlay.color = overlayColorDispel;
186         end
187     -- don't overlay charmed when in vehicle
188     elseif UnitIsCharmed(unit) and unit == frame.unit then
189         if frame.overlay.color ~= overlayColorCharm then
190             frame.overlay:SetVertexColor(unpack(overlayColorCharm));
191             frame.overlay:Show();
192             frame.overlay.color = overlayColorCharm;
193         end
194     else
195         if frame.overlay.color ~= nil then
196             frame.overlay:Hide();
197             frame.overlay.color = nil;
198         end
199     end
200 end
201
202 local function updateAggro(frame, unit)
203     local status = UnitThreatSituation(unit);
204     if status and status > 0 then
205         frame.base:SetVertexColor(GetThreatStatusColor(status));
206     else
207         frame.base:SetVertexColor(unpack(baseColor));
208     end
209 end
210
211 local function updateVehicle(frame)
212     local shouldTargetVehicle = UnitHasVehicleUI(frame.unit) and UnitTargetsVehicleInRaidUI(frame.unit) and UnitExists(frame.vehicle);
213     if shouldTargetVehicle then
214         if not frame.inVehicle then
215             frame.inVehicle = true;
216             frame.displayed = frame.vehicle;
217             registerEvents(frame);
218         end
219     elseif frame.inVehicle then
220         frame.inVehicle = false;
221         frame.displayed = frame.unit;
222         registerEvents(frame);
223     end
224 end
225
226 local function updateRole(frame, unit)
227     local role = UnitGroupRolesAssigned(unit);
228     if role == "HEALER" then
229         frame.role:SetTexCoord(0.75, 1, 0, 1);
230         frame.role:Show();
231     elseif role == "TANK" then
232         frame.role:SetTexCoord(0.5, 0.75, 0, 1);
233         frame.role:Show();
234     else
235         frame.role:Hide();
236     end
237 end
238
239 local eventFuncs = {
240     ["UNIT_HEALTH"] = function(frame)
241         updateHealth(frame, frame.displayed);
242         updateText(frame, frame.displayed);
243         updateShield(frame, frame.displayed);
244         updateHealAbsorb(frame, frame.displayed);
245         -- no heal prediction update, that doesn't overflow too much
246     end,
247     ["UNIT_POWER"] = function(frame)
248         updatePower(frame, frame.displayed);
249     end,
250     ["UNIT_AURA"] = function(frame)
251         updateAuras(frame, frame.displayed);
252     end,
253     ["UNIT_HEAL_PREDICTION"] = function(frame)
254         updateHealPred(frame, frame.displayed);
255     end,
256     ["UNIT_ABSORB_AMOUNT_CHANGED"] = function(frame)
257         updateShield(frame, frame.displayed);
258     end,
259     ["UNIT_HEAL_ABSORB_AMOUNT_CHANGED"] = function(frame)
260         updateHealAbsorb(frame, frame.displayed);
261     end,
262     ["UNIT_THREAT_SITUATION_UPDATE"] = function(frame)
263         updateAggro(frame, frame.displayed);
264     end,
265     ["UNIT_MAXHEALTH"] = function(frame)
266         updateMaxHealth(frame, frame.displayed);
267         updateHealth(frame, frame.displayed);
268         updateText(frame, frame.displayed);
269         updateShield(frame, frame.displayed);
270         updateHealAbsorb(frame, frame.displayed);
271     end,
272     ["UNIT_MAXPOWER"] = function(frame)
273         updateMaxPower(frame, frame.displayed);
274         updatePower(frame, frame.displayed);
275     end,
276     ["UNIT_DISPLAYPOWER"] = function(frame)
277         updatePowerColor(frame, frame.displayed);
278     end,
279     ["UNIT_NAME_UPDATE"] = function(frame)
280         updateName(frame, frame.displayed);
281     end,
282     ["UNIT_CONNECTION"] = function(frame)
283         updateText(frame, frame.displayed);
284     end,
285     ["INCOMING_RESURRECT_CHANGED"] = function(frame)
286         -- TODO have an icon
287         updateText(frame, frame.displayed);
288     end,
289     ["PARTY_MEMBER_ENABLE"] = function(frame)
290         -- new power info possibly (FrameXML/CompactUnitFrame.lua)
291         updateMaxPower(frame, frame.displayed);
292         updatePowerColor(frame, frame.displayed);
293     end,
294     ["PLAYER_ROLES_ASSIGNED"] = function(frame)
295         updateRole(frame, frame.unit);
296     end,
297     ["UPDATE_ALL_BARS"] = function(frame)
298         updateVehicle(frame);
299         updateMaxHealth(frame, frame.displayed);
300         updateMaxPower(frame, frame.displayed);
301         updateHealth(frame, frame.displayed);
302         updateText(frame, frame.displayed);
303         updatePower(frame, frame.displayed);
304         updateAuras(frame, frame.displayed);
305         updateShield(frame, frame.displayed);
306         updateHealPred(frame, frame.displayed);
307         updateHealAbsorb(frame, frame.displayed);
308         updatePowerColor(frame, frame.displayed);
309         updateAggro(frame, frame.displayed);
310         updateName(frame, frame.displayed);
311         updateRole(frame, frame.unit);
312     end,
313 };
314 eventFuncs["UNIT_HEALTH_FREQUENT"] = eventFuncs["UNIT_HEALTH"];
315 eventFuncs["PARTY_MEMBER_DISABLE"] = eventFuncs["PARTY_MEMBER_ENABLE"];
316 eventFuncs["UNIT_ENTERED_VEHICLE"] = eventFuncs["UPDATE_ALL_BARS"];
317 eventFuncs["UNIT_EXITED_VEHICLE"] = eventFuncs["UPDATE_ALL_BARS"];
318 eventFuncs["UNIT_PET"] = eventFuncs["UPDATE_ALL_BARS"];
319 eventFuncs["GROUP_ROSTER_UPDATE"] = eventFuncs["UPDATE_ALL_BARS"];
320 --local function unitEvent(self, event, ...)
321 local function unitEvent(self, event)
322     --local arg1, arg2, arg3, arg4 = ...;
323     eventFuncs[event](self);
324 end
325
326 local function initialize()
327     CFrame:SetPoint("CENTER", nil, "CENTER", anchorX, anchorY);
328     CFrame:SetHeight((height+2)*8);
329     CFrame:SetWidth((width+2)*5+1); -- extra pixel for shield overflow
330     Frames.InitializeParty(CFrame, party, unitEvent);
331     Frames.InitializeRaid(CFrame, raid, unitEvent);
332 end
333
334 CFrame:RegisterEvent("ADDON_LOADED");
335 CFrame:RegisterEvent("PLAYER_LOGIN");
336 CFrame:RegisterEvent("PLAYER_ENTERING_WORLD");
337 CFrame:RegisterEvent("GROUP_ROSTER_UPDATE");
338 CFrame:RegisterEvent("PLAYER_REGEN_ENABLED");
339 CFrame:SetScript("OnEvent", function(self, event, ...)
340     if event == "GROUP_ROSTER_UPDATE" or event == "PLAYER_REGEN_ENABLED" or
341        event == "PLAYER_ENTERING_WORLD" then
342         --updateGroup();
343     elseif event == "PLAYER_LOGIN" then
344         initialize();
345     elseif event == "ADDON_LOADED" then
346         -- loaded
347     end
348 end);