306a575 - Optimizations to events, removal of heal prediction
[wowui.git] / OmaRF / Indicators.lua
1 -- Indicators.lua
2 local pairs, ipairs = pairs, ipairs;
3 local floor = math.floor;
4 local GetTime = GetTime;
5 local UnitExists = UnitExists;
6 local UnitAura = UnitAura;
7 local CreateFrame = CreateFrame;
8 local UnitIsDeadOrGhost, UnitIsConnected = UnitIsDeadOrGhost, UnitIsConnected;
9 local CTimerAfter = C_Timer.After;
10
11 local Settings = OmaRFSettings;
12 local majorAuras = Settings.MajorAuras;
13
14 local watchedAuras = {};
15 local updaters = {};
16 local updating = {};
17 local updateAuras;
18
19 local M = {};
20 OmaRFIndicators = M;
21 M.Class = {};
22
23 function M.SetupIndicators(frame, class)
24     frame.indBase = CreateFrame("Frame", nil, frame);
25     frame.indBase:SetAllPoints();
26     frame.indBase:Hide();
27     if M.Class[class] then
28         watchedAuras = M.Class[class].Auras;
29         frame.inds = M.Class[class].Setup(frame.indBase);
30     else
31         frame.inds = {};
32     end
33
34     frame.majorBase = CreateFrame("Frame", nil, frame);
35     frame.majorBase:SetPoint("TOPLEFT", frame, "TOPLEFT", 4, -10);
36     frame.majorBase:SetPoint("BOTTOMRIGHT");
37     frame.majors = {};
38     for i = 1,3 do
39         local tex = frame.majorBase:CreateTexture(nil, "OVERLAY");
40         tex = frame.majorBase:CreateTexture(nil, "OVERLAY");
41         if i == 1 then tex:SetPoint("TOPLEFT", frame.majorBase, "TOPLEFT");
42         else tex:SetPoint("TOPLEFT", frame.majors[i-1], "TOPRIGHT"); end
43         tex:SetWidth(20);
44         tex:SetHeight(20);
45         tex:Hide();
46         tex.text = frame.majorBase:CreateFontString(nil, "OVERLAY", "GameFontHighlight");
47         tex.text:SetPoint("CENTER", tex, "BOTTOMRIGHT", -2, 2);
48         tex.text:Hide();
49         tex.stack = frame.majorBase:CreateFontString(nil, "OVERLAY", "GameFontHighlight");
50         tex.stack:SetPoint("CENTER", tex, "TOPLEFT", 1, 0);
51         tex.stack:Hide();
52         tex.icon = true;
53         frame.majors[i] = tex;
54     end
55
56     frame.throttle = function()
57         frame.throttled = nil;
58         if UnitExists(frame.displayed) then
59             return updateAuras(frame, frame.displayed);
60         end
61     end;
62 end
63
64 local function remaining(text, expires, current)
65     if expires == 0 then
66         text:SetText("");
67         return false;
68     end
69     local remain = expires - current;
70     if remain > 8 then
71         text:SetText("");
72     else
73         text:SetText(floor(remain+0.5));
74     end
75     return true;
76 end
77
78 local function updateIndicators(frame)
79     local unit = frame.displayed;
80     if not frame:IsShown() or not UnitIsConnected(unit) or UnitIsDeadOrGhost(unit) then
81         updating[frame] = nil;
82         return;
83     end
84
85     local needUpdate = false;
86     local current = GetTime();
87     for _, ind in pairs(frame.inds) do
88         if ind.text and ind.text.expires ~= nil then
89             needUpdate = remaining(ind.text, ind.text.expires, current) or needUpdate;
90         end
91     end
92     for _, ind in pairs(frame.majors) do
93         if ind.text and ind.text.expires ~= nil then
94             needUpdate = remaining(ind.text, ind.text.expires, current) or needUpdate;
95         end
96     end
97     if needUpdate then
98         CTimerAfter(0.20, updaters[frame]);
99     else
100         updating[frame] = nil;
101     end
102 end
103
104 local function showInd(ind, expires, current, count, icon)
105     local needUpdate = false;
106     if ind.icon then
107         ind:SetTexture(icon);
108     end
109     if ind.text then
110         needUpdate = remaining(ind.text, expires, current);
111         ind.text.expires = expires;
112         ind.text:Show();
113     end
114     if ind.stack and count > 1 then
115         ind.stack:SetText(count);
116         ind.stack:Show();
117     end
118     ind:Show();
119     return needUpdate;
120 end
121
122 local function hideInd(ind)
123     if ind.text then
124         ind.text.expires = nil;
125         ind.text:Hide();
126     end
127     if ind.stack then ind.stack:Hide() end
128     ind:Hide();
129 end
130
131 function M.UpdateAuras(frame, unit)
132     local current = GetTime();
133     if frame.throttled then
134         return;
135     elseif frame.prevUpdate and current - frame.prevUpdate < 0.2 then
136         frame.throttled = true;
137         return CTimerAfter(0.2, frame.throttle);
138     end
139
140     frame.prevUpdate = current;
141     for _, ind in pairs(frame.inds) do
142         hideInd(ind);
143     end
144     local icon, count, expires, id;
145     local showInds, needUpdate = false, false;
146     local i = 1;
147     while true do
148         _, icon, count, _, _, expires, _, _, _, id = UnitAura(unit, i, "PLAYER HELPFUL");
149         if not id then break end
150         local pos = watchedAuras[id];
151         if pos then
152             needUpdate = showInd(frame.inds[pos], expires, current, count, icon) or needUpdate;
153             showInds = true;
154         end
155         i = i + 1;
156     end
157
158     if showInds then
159         frame.indBase:Show();
160         if needUpdate and not updating[frame] then
161             updating[frame] = true; -- race?
162             -- create a function for updating the indicator
163             local func = updaters[frame];
164             if not func then
165                 func = function() updateIndicators(frame) end;
166                 updaters[frame] = func;
167             end
168             CTimerAfter(0.20, func);
169         end
170     else
171         frame.indBase:Hide();
172     end
173 end
174 updateAuras = M.UpdateAuras;
175
176 function M.UpdateMajorAuras(frame, unit)
177     for _, ind in pairs(frame.majors) do
178         hideInd(ind);
179     end
180     if UnitIsDeadOrGhost(unit) then return end
181     local name, icon, count, expires, id;
182     local showMajors, needUpdate = false, false;
183     local majorPos = 1;
184     local alert = false; -- color the whole bar
185     local current = GetTime();
186     local i = 1;
187     while true do
188         name, icon, count, _, _, expires, _, _, _, id = UnitAura(unit, i, "HARMFUL");
189         if not id or majorPos > 3 then break end
190         local major = majorAuras[id] or majorAuras[name];
191         if major then
192             if not major.noicon then
193                 needUpdate = showInd(frame.majors[majorPos], expires, current, count, icon) or needUpdate;
194             end
195             if major.bar then alert = major.bar end
196             showMajors = true;
197             majorPos = majorPos + 1;
198         end
199         i = i + 1;
200     end
201
202     if showMajors then
203         frame.majorBase:Show();
204         if needUpdate and not updating[frame] then
205             updating[frame] = true; -- race?
206             -- create a function for updating the indicator
207             local func = updaters[frame];
208             if not func then
209                 func = function() updateIndicators(frame) end;
210                 updaters[frame] = func;
211             end
212             CTimerAfter(0.20, func);
213         end
214     else
215         frame.majorBase:Hide();
216     end
217
218     return alert;
219 end