ce916a686995ad3833ef74306561c7916f1e8133
[wowui.git] / Indicators.lua
1 local media = LibStub:GetLibrary("LibSharedMedia-3.0");
2 local f = RaidFrameCustomization.frames;
3 local positions = RaidFrameCustomization.positions;
4 local pad = 2;
5 local paddings = {
6     TOPLEFT = {pad, -pad},
7     TOPRIGHT = {-pad, -pad},
8     CENTER = {0, 0},
9     BOTTOMLEFT = {pad, pad},
10     BOTTOMRIGHT = {-pad, pad}
11 };
12 local watchedAuras; -- all watched auras
13 local indicatorAuras; -- watched auras per indicator
14 local auraFilters = {"HELPFUL", "HARMFUL"};
15 local DEFAULT_ICON = "Interface\\AddOns\\RaidFrameCustomization\\images\\rhomb";
16 local _;
17
18 -- global functions used every update
19 local GetTime = GetTime;
20 local UnitAura = UnitAura;
21 local UnitIsPlayer = UnitIsPlayer;
22 local UnitIsConnected = UnitIsConnected;
23 local UnitIsDeadOrGhost = UnitIsDeadOrGhost;
24 local CompactRaidFrameContainer_ApplyToFrames = CompactRaidFrameContainer_ApplyToFrames;
25
26 -- list of important auras TODO try to use spellIDs
27 local centerAuras = {
28     "Power Word: Shield"
29 };
30
31 local function configureIndicators(frame)
32     local frameName = frame:GetName();
33     if not f[frameName] then return end
34
35     local config = RaidFrameCustomization.db.profile;
36     local font = media and media:Fetch('font', config.indicatorFont) or STANDARD_TEXT_FONT;
37     for pos, ind in pairs(f[frameName]) do
38         ind.text:SetFont(font, config[pos]["textSize"]);
39         ind.text:SetTextColor(unpack(config[pos]["textColor"]));
40         ind.icon:SetWidth(config[pos]["iconSize"]);
41         ind.icon:SetHeight(config[pos]["iconSize"]);
42         ind.icon:SetTexture(DEFAULT_ICON);
43         ind.icon:SetVertexColor(unpack(config[pos]["iconColor"]));
44         if config[pos]["showIcon"] then
45             ind.icon:Show();
46         else
47             ind.icon:Hide();
48         end
49     end
50 end
51
52 -- Create the FontStrings used for indicators
53 local function setupCompactUnitFrame(frame)
54     local name = frame:GetName();
55     f[name] = {};
56     for _, pos in ipairs(positions) do
57         f[name][pos] = {};
58         f[name][pos].text = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall");
59         f[name][pos].text:SetPoint(pos, frame, pos, paddings[pos][1], paddings[pos][2]);
60         f[name][pos].icon = frame:CreateTexture(nil, "OVERLAY");
61         f[name][pos].icon:SetPoint(pos, frame, pos, paddings[pos][1], paddings[pos][2]);
62     end
63     configureIndicators(frame);
64 end
65
66 -- Get all unit auras TODO change to event driven, only remaining updating with timer
67 local function getAuras(unit)
68     local unitAuras = {};
69     local auraName, icon, count, expires, caster, debuffType, spellId;
70     local filter;
71
72     for _, filter in ipairs(auraFilters) do
73         local i = 1;
74         while true do
75             auraName, _, icon, count, debuffType, _, expires, caster, _, _, spellId = UnitAura(unit, i, filter);
76             if not spellId then break end
77             if watchedAuras[auraName] or watchedAuras[spellId] or watchedAuras[debuffType] then
78                 local aura = {};
79                 aura.auraName = auraName;
80                 aura.spellId = spellId;
81                 aura.count = count;
82                 aura.expires = expires;
83                 aura.mine = UnitIsPlayer(caster);
84                 aura.icon = icon;
85                 aura.debuffType = debuffType;
86                 table.insert(unitAuras, aura);
87             end
88             i = i + 1;
89         end
90     end
91     return unitAuras;
92 end
93
94 -- Check the indicators on a frame and update the times on them
95 local function updateIndicators(frame)
96     if not frame.unit then return end
97     local unit = frame.unit;
98     local frameName = frame:GetName();
99
100     -- Check if the indicator object exists, else create it
101     if not f[frameName] then setupCompactUnitFrame(frame) end
102     -- Hide if unit is dead/disconnected
103     if (not UnitIsConnected(unit)) or UnitIsDeadOrGhost(frame.displayedUnit) then
104         for _, ind in pairs(f[frameName]) do
105             ind.text:SetText("");
106             ind.icon:SetTexture("");
107         end
108         return;
109     end
110
111     local unitAuras = getAuras(unit);
112     for pos, ind in pairs(f[frameName]) do
113         -- try to find matching aura
114         local found, aura;
115         for _, aura in pairs(unitAuras) do
116             if indicatorAuras[pos][aura.auraName] or indicatorAuras[pos][aura.spellId] or
117                indicatorAuras[pos][aura.debuffType] then
118                 found = aura;
119                 -- break on first matching buff/debuff cast by me
120                 -- otherwise continue through
121                 if aura.mine then break end
122             end
123         end
124
125         local config = RaidFrameCustomization.db.profile[pos];
126         if found then
127             if config.mine and not found.mine then
128                 -- don't show
129                 ind.icon:SetTexture("");
130                 ind.text:SetText("");
131             else
132                 if config.showIcon and not config.useDefaultIcon then
133                     -- show icon TODO coloring
134                     ind.icon:SetTexture(found.icon);
135                 end
136                 if config.showText then
137                     -- show text
138                     local text;
139                     local remaining = found.expires - GetTime();
140                     if remaining > 60 then
141                         text = string.format("%dm", ceil(remaining/60));
142                     else
143                         text = string.format("%d", floor(remaining+0.5));
144                     end
145
146                     if config.stack and found.count > 0 then
147                         if text then
148                             text = found.count.."-"..text;
149                         else
150                             text = found.count;
151                         end
152                     end
153
154                     ind.text:SetText(text);
155                 end
156             end
157         else
158             -- not found, show nothing
159             ind.icon:SetTexture("");
160             ind.text:SetText("");
161         end
162     end
163 end
164
165 -- Update all indicators
166 function RaidFrameCustomization:UpdateAllIndicators()
167     CompactRaidFrameContainer_ApplyToFrames(CompactRaidFrameContainer, "normal", updateIndicators);
168 end
169
170 -- Used to update everything that is affected by the configuration
171 function RaidFrameCustomization:RefreshConfig()
172     self:OnDisable(); -- clear everything
173     if self.db.profile.enabled then
174         CompactRaidFrameContainer_ApplyToFrames(CompactRaidFrameContainer, "normal", configureIndicators);
175         -- Format aura strings
176         watchedAuras = {};
177         indicatorAuras = {};
178         for _, pos in ipairs(positions) do
179             indicatorAuras[pos] = {};
180             for _, aura in ipairs(self.db.profile[pos]["auras"]) do
181                 watchedAuras[aura] = true;
182                 indicatorAuras[pos][aura] = true;
183             end
184         end
185
186         if next(watchedAuras) ~= nil then
187             self.updateTimer = self:ScheduleRepeatingTimer("UpdateAllIndicators", 0.15);
188         end
189     end
190 end