1 -- ----------------------------------------------------------------------------
2 -- Raid Frame Indicators by Szandos, schyrio
3 -- ----------------------------------------------------------------------------
4 Indicators = LibStub( "AceAddon-3.0" ):NewAddon( "Indicators", "AceTimer-3.0");
5 local media = LibStub:GetLibrary("LibSharedMedia-3.0");
6 local f = {}; -- Indicator objects
9 local watchedAuras; -- all watched auras
10 local indicatorAuras; -- watched auras per indicator
11 local DEFAULT_ICON = "Interface\\AddOns\\RaidFrameCustomization\\images\\rhomb";
13 -- Hide buff/debuff icons
14 local function hideBlizzardBuffs(frame)
15 -- used in CompactUnitFrame_UpdateAuras (Buffs, Debuffs, DispellableDebuffs)
16 if frame.optionTable.displayBuffs then
17 frame.optionTable.displayBuffs = false
18 -- used in CompactUnitFrame_UpdateHealthColor, might not be set prior
19 frame.optionTable.healthBarColorOverride = CreateColor(0.4, 0.4, 0.4);
21 if frame.optionTable.displayDebuffs then frame.optionTable.displayDebuffs = false end
23 if frame.optionTable.displayDispelDebuffs then frame.optionTable.displayDispelDebuffs = true end
26 hooksecurefunc("CompactUnitFrame_SetOptionTable", hideBlizzardBuffs);
28 -- Set the appearance of the FontStrings
29 local function setupIndicatorAppearance(frame)
30 local frameName = frame:GetName();
31 if not f[frameName] then return end
32 local font = media and media:Fetch('font', Indicators.db.profile.indicatorFont) or STANDARD_TEXT_FONT;
36 f[frameName][i].text:SetFont(font, Indicators.db.profile["textSize"..i], "");
37 f[frameName][i].icon:SetWidth(Indicators.db.profile["iconSize"..i]);
38 f[frameName][i].icon:SetHeight(Indicators.db.profile["iconSize"..i]);
39 if Indicators.db.profile["showIcon"..i] then
40 f[frameName][i].icon:Show();
42 f[frameName][i].icon:Hide();
47 -- Create the FontStrings used for indicators
48 local function setupCompactUnitFrame(frame)
49 local frameName = frame:GetName();
52 "TOPLEFT", "TOPRIGHT", "CENTER", "BOTTOMLEFT", "BOTTOMRIGHT"
55 {pad, -pad}, {-pad, -pad}, {0, 0}, {pad, pad}, {-pad, pad}
62 f[frameName][i].text = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall");
63 f[frameName][i].text:SetPoint(positions[i], frame, positions[i], paddings[i][1], paddings[i][2]);
64 f[frameName][i].icon = frame:CreateTexture(nil, "OVERLAY");
65 f[frameName][i].icon:SetPoint(positions[i], frame, positions[i], paddings[i][1], paddings[i][2]);
67 setupIndicatorAppearance(frame);
71 local function getAuras(unit)
73 local unitDebuffs = {};
74 local auraName, icon, count, expires, caster, debuffType, spellId;
79 auraName, _, icon, count, _, _, expires, caster, _, _, spellId = UnitBuff(unit, i);
80 if not spellId then break end
81 if watchedAuras[auraName] or watchedAuras[spellId] then
82 -- possibly non-contiguous indexes, doesn't matter
84 unitBuffs[i].auraName = auraName;
85 unitBuffs[i].spellId = spellId;
86 unitBuffs[i].count = count;
87 unitBuffs[i].expires = expires;
88 unitBuffs[i].mine = UnitIsUnit(caster, "player");
89 unitBuffs[i].icon = icon;
94 -- Get all unit debuffs
97 auraName, _, icon, count, debuffType, _, expires, caster, _, _, spellId = UnitDebuff(unit, i);
98 if not spellId then break end
99 -- TODO debuffType check better
100 if watchedAuras[auraName] or watchedAuras[spellId] or watchedAuras[debuffType] then
102 unitDebuffs[i].auraName = auraName;
103 unitDebuffs[i].spellId = spellId;
104 unitDebuffs[i].count = count;
105 unitDebuffs[i].expires = expires;
106 unitDebuffs[i].mine = UnitIsUnit(caster, "player");
107 unitDebuffs[i].icon = icon;
108 unitDebuffs[i].debuffType = debuffType;
113 return unitBuffs, unitDebuffs;
116 -- Check the indicators on a frame and update the times on them
117 local function updateIndicator(frame)
118 if not frame.unit then return end
119 local unit = frame.unit;
120 local frameName = frame:GetName();
121 local currentTime = GetTime();
124 -- Check if the indicator object exists, else create it
125 if not f[frameName] then setupCompactUnitFrame(frame) end
126 -- Hide if unit is dead/disconnected
127 if (not UnitIsConnected(unit)) or UnitIsDeadOrGhost(frame.displayedUnit) then
129 f[frameName][i].text:SetText("");
130 f[frameName][i].icon:SetTexture("");
135 local unitBuffs, unitDebuffs = getAuras(unit);
137 -- try to find matching aura
139 for _, aura in pairs(unitBuffs) do
140 if indicatorAuras[i][aura.auraName] or indicatorAuras[i][aura.spellId] then
142 -- break on first matching buff cast by me
143 -- otherwise continue through
144 if aura.mine then break end
148 -- search debuffs if buff was not found
149 for _, aura in pairs(unitDebuffs) do
150 if indicatorAuras[i][aura.auraName] or indicatorAuras[i][aura.spellId] or
151 indicatorAuras[i][aura.debuffType] then
153 -- break on first matching debuff cast by me
154 -- otherwise continue through
155 if aura.mine then break end
161 if Indicators.db.profile["mine"..i] and not found.mine then
163 f[frameName][i].icon:SetTexture("");
164 f[frameName][i].text:SetText("");
166 if Indicators.db.profile["showIcon"..i] then
167 -- show icon TODO coloring
168 if Indicators.db.profile["useDefaultIcon"..i] then
169 f[frameName][i].icon:SetTexture(DEFAULT_ICON);
171 f[frameName][i].icon:SetTexture(found.icon);
174 -- TODO make show text into general setting
175 -- under which you can select what text to show
176 if Indicators.db.profile["showText"..i] then
179 local remaining = found.expires - currentTime;
180 if remaining > 60 then
181 text = string.format("%dm", ceil(remaining/60));
183 text = string.format("%d", floor(remaining+0.5));
186 if Indicators.db.profile["stack"..i] and found.count > 0 then
188 text = found.count.."-"..text;
195 f[frameName][i].text:SetTextColor(
196 Indicators.db.profile["color"..i].r,
197 Indicators.db.profile["color"..i].g,
198 Indicators.db.profile["color"..i].b,
199 Indicators.db.profile["color"..i].a
201 if Indicators.db.profile["debuffColor"..i] then
202 if found.debuffType then
203 if found.debuffType == "Curse" then
204 f[frameName][i].text:SetTextColor(0.6,0,1,1);
205 elseif found.debuffType == "Disease" then
206 f[frameName][i].text:SetTextColor(0.6,0.4,0,1);
207 elseif found.debuffType == "Magic" then
208 f[frameName][i].text:SetTextColor(0.2,0.6,1,1);
209 elseif found.debuffType == "Poison" then
210 f[frameName][i].text:SetTextColor(0,0.6,0,1);
215 f[frameName][i].text:SetText(text);
219 -- not found, show nothing
220 f[frameName][i].icon:SetTexture("");
221 f[frameName][i].text:SetText("");
226 -- Update all indicators
227 function Indicators:UpdateAllIndicators()
228 CompactRaidFrameContainer_ApplyToFrames(CompactRaidFrameContainer, "normal", updateIndicator);
231 -- Used to update everything that is affected by the configuration
232 function Indicators:RefreshConfig()
234 CompactRaidFrameContainer_ApplyToFrames(CompactRaidFrameContainer, "normal", setupIndicatorAppearance);
236 -- Format aura strings
241 indicatorAuras[i] = {};
242 for auraName in string.gmatch(Indicators.db.profile["auras"..i], "[^\n]+") do -- Grab each line
243 auraName = string.gsub(auraName, "^%s*(.-)%s*$", "%1"); -- Strip any whitespaces
244 if tonumber(auraName) then
245 watchedAuras[tonumber(auraName)] = true;
246 indicatorAuras[i][tonumber(auraName)] = true;
248 watchedAuras[auraName] = true;
249 indicatorAuras[i][auraName] = true;
254 self:CancelAllTimers();
255 if next(watchedAuras) ~= nil then
256 self.updateTimer = self:ScheduleRepeatingTimer("UpdateAllIndicators", 0.11);
260 function Indicators:OnInitialize()
261 -- Set up config pane
264 -- Register callbacks for profile switching
265 self.db.RegisterCallback(self, "OnProfileChanged", "RefreshConfig");
266 self.db.RegisterCallback(self, "OnProfileCopied", "RefreshConfig");
267 self.db.RegisterCallback(self, "OnProfileReset", "RefreshConfig");
270 function Indicators:OnEnable()
271 if self.db.profile.enabled then
272 Indicators:RefreshConfig();
276 function Indicators:OnDisable()
279 self:CancelAllTimers();
280 -- Hide all indicators
281 for frameName, _ in pairs(f) do
283 f[frameName][i].text:SetText("");
284 f[frameName][i].icon:SetTexture("");
290 DEFAULT_CHAT_FRAME:AddMessage("Indicators: ".. tostring(s));