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