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