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