d9a8d3f - Fix small issues, use StateDrivers instead of reimplementing
[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 = UnitName, UnitClass;
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 CTimerAfter = C_Timer.After;
18 local RAID_CLASS_COLORS = RAID_CLASS_COLORS;
19
20 local Frames = OmaFrames;
21 local Indicators = OmaIndicators;
22 local checkIndicators = Indicators.CheckIndicators;
23
24 local CFrame = CreateFrame("Frame", "OmaCFrame", UIParent);
25 local party = {};
26 local raid = {};
27 local updaters = {};
28
29 local width, height = Frames.Width, Frames.Height;
30 local anchorX, anchorY = Frames.AnchorX, Frames.AnchorY;
31 local baseColor = Frames.BaseColor;
32 local overlayColorDispel = Frames.OverlayColorDispel;
33 local overlayColorCharm = Frames.OverlayColorCharm;
34 local overlayColorAlert = Frames.OverlayColorAlert;
35 local powerColors = Frames.PowerColors;
36
37 local function updateHealth(frame, unit)
38     local current, max = UnitHealth(unit), frame.health.max;
39     local healthLost = max - current;
40     frame.health:Show();
41     -- sanity check, occasionally UnitHealthMax gives zero
42     if current > max then
43         -- somehow current health has gone over the maximum (missed maxhealth event)
44         frame.health.max = UnitHealthMax(unit);
45         max = frame.health.max;
46         if current > max then
47             -- error state, still over maximum
48             frame.health:SetWidth(width);
49             frame.text:Hide();
50             return;
51         end
52     elseif max > 0 then
53         frame.health:SetWidth(current/frame.health.max*width);
54     else
55         frame.health:SetWidth(width);
56         frame.text:Hide();
57         return;
58     end
59
60     if UnitIsDeadOrGhost(unit) then
61         frame.health:Hide();
62         if UnitHasIncomingResurrection(unit) then
63             frame.text:SetText("Rez");
64         else
65             frame.text:SetText("Dead");
66         end
67     elseif not UnitIsConnected(unit) then
68         frame.health:Hide();
69         frame.text:SetText("DC");
70     elseif healthLost > 0 then
71         if healthLost > 1200000000 then -- 1.2B
72             frame.text:SetFormattedText("-%.1fB", healthLost / 1000000000);
73         elseif healthLost > 1200000 then -- 1.2M
74             frame.text:SetFormattedText("-%.1fM", healthLost / 1000000);
75         elseif healthLost > 1000 then -- 1K
76             frame.text:SetFormattedText("-%dK", healthLost / 1000);
77         else
78             frame.text:SetFormattedText("-%d", healthLost)
79         end
80         frame.text:Show();
81     else
82         frame.text:Hide();
83     end
84 end
85
86 local function updateMaxHealth(frame, unit)
87     frame.health.max = UnitHealthMax(unit);
88     updateHealth(frame, unit);
89 end
90
91 local function updatePower(frame, unit)
92     local current, max = UnitPower(unit), frame.mana.max;
93     -- sanity check, occasionally UnitPowerMax gives zero
94     if current > max then
95         frame.mana.max = UnitPowerMax(unit);
96         max = frame.mana.max;
97         if current > max then
98             -- error
99             frame.mana:SetWidth(width);
100             return;
101         end
102     elseif max > 0 then
103         frame.mana:SetWidth(UnitPower(unit)/max*width);
104     else
105         frame.mana:SetWidth(width);
106     end
107 end
108
109 local function updateMaxPower(frame, unit)
110     frame.mana.max = UnitPowerMax(unit);
111     updatePower(frame, 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     elseif UnitIsCharmed(unit) then
184         if frame.overlay.color ~= overlayColorCharm then
185             frame.overlay:SetVertexColor(unpack(overlayColorCharm));
186             frame.overlay:Show();
187             frame.overlay.color = overlayColorCharm;
188         end
189     else
190         if frame.overlay.color ~= nil then
191             frame.overlay:Hide();
192             frame.overlay.color = nil;
193         end
194     end
195 end
196
197 local function updateAggro(frame, unit)
198     local status = UnitThreatSituation(unit);
199     if status and status > 0 then
200         frame.base:SetVertexColor(GetThreatStatusColor(status));
201     else
202         frame.base:SetVertexColor(unpack(baseColor));
203     end
204 end
205
206 local eventFuncs = {
207     ["UNIT_HEALTH"] = function(frame, unit)
208         updateHealth(frame, unit);
209         updateShield(frame, unit);
210         updateHealAbsorb(frame, unit);
211         -- no heal prediction update, that doesn't overflow too much
212     end,
213     ["UNIT_POWER"] = function(frame, unit)
214         updatePower(frame, unit);
215     end,
216     ["UNIT_AURA"] = function(frame, unit)
217         updateAuras(frame, unit);
218     end,
219     ["UNIT_HEAL_PREDICTION"] = function(frame, unit)
220         updateHealPred(frame, unit);
221     end,
222     ["UNIT_ABSORB_AMOUNT_CHANGED"] = function(frame, unit)
223         updateShield(frame, unit);
224     end,
225     ["UNIT_HEAL_ABSORB_AMOUNT_CHANGED"] = function(frame, unit)
226         updateHealAbsorb(frame, unit);
227     end,
228     ["UNIT_THREAT_SITUATION_UPDATE"] = function(frame, unit)
229         updateAggro(frame, unit);
230     end,
231     ["UNIT_MAXHEALTH"] = function(frame, unit)
232         updateMaxHealth(frame, unit);
233     end,
234     ["UNIT_MAXPOWER"] = function(frame, unit)
235         updateMaxPower(frame, unit);
236     end,
237     ["UNIT_DISPLAYPOWER"] = function(frame, unit)
238         updatePowerColor(frame, unit);
239     end,
240     ["UNIT_NAME_UPDATE"] = function(frame, unit)
241         updateName(frame, unit);
242     end,
243     ["UNIT_CONNECTION"] = function(frame, unit)
244         updateHealth(frame, unit);
245     end,
246     ["INCOMING_RESURRECT_CHANGED"] = function(frame, unit)
247         -- TODO have an icon
248         updateHealth(frame, unit);
249     end,
250     ["PARTY_MEMBER_ENABLE"] = function(frame)
251         -- new power info possibly (FrameXML/CompactUnitFrame.lua)
252         updateMaxPower(frame, frame.unit);
253         updatePowerColor(frame, frame.unit);
254     end,
255     ["UPDATE_ALL_BARS"] = function(frame, unit)
256         updateMaxHealth(frame, unit);
257         updateMaxPower(frame, unit);
258         --updateHealth(frame, unit); -- MaxHealth covers this
259         --updatePower(frame, unit); -- MaxPower covers this
260         updateAuras(frame, unit);
261         updateShield(frame, unit);
262         updateHealPred(frame, unit);
263         updateHealAbsorb(frame, unit);
264         updatePowerColor(frame, unit);
265         updateAggro(frame, unit);
266         updateName(frame, unit);
267     end,
268 };
269 eventFuncs["UNIT_HEALTH_FREQUENT"] = eventFuncs["UNIT_HEALTH"];
270 eventFuncs["PARTY_MEMBER_DISABLE"] = eventFuncs["PARTY_MEMBER_ENABLE"];
271 local function unitEvent(self, event, ...)
272     local arg1, arg2, arg3, arg4 = ...;
273     eventFuncs[event](self, arg1);
274 end
275
276 local function initialize()
277     CFrame:SetPoint("CENTER", nil, "CENTER", anchorX, anchorY);
278     CFrame:SetHeight((height+2)*8);
279     CFrame:SetWidth((width+2)*5+1); -- extra pixel for shield overflow
280     Frames.InitializeParty(CFrame, party, unitEvent);
281     Frames.InitializeRaid(CFrame, raid, unitEvent);
282 end
283
284 CFrame:RegisterEvent("ADDON_LOADED");
285 CFrame:RegisterEvent("PLAYER_LOGIN");
286 CFrame:RegisterEvent("PLAYER_ENTERING_WORLD");
287 CFrame:RegisterEvent("GROUP_ROSTER_UPDATE");
288 CFrame:RegisterEvent("PLAYER_REGEN_ENABLED");
289 CFrame:SetScript("OnEvent", function(self, event, ...)
290     if event == "GROUP_ROSTER_UPDATE" or event == "PLAYER_REGEN_ENABLED" or
291        event == "PLAYER_ENTERING_WORLD" then
292         --updateGroup();
293     elseif event == "PLAYER_LOGIN" then
294         initialize();
295     elseif event == "ADDON_LOADED" then
296         -- loaded
297     end
298 end);