-- ---------------------------------------------------------------------------- -- Raid Frame Indicators by Szandos, schyrio -- ---------------------------------------------------------------------------- Indicators = LibStub( "AceAddon-3.0" ):NewAddon( "Indicators", "AceTimer-3.0"); local media = LibStub:GetLibrary("LibSharedMedia-3.0"); local f = {}; -- Indicator objects local pad = 2; local _; local watchedAuras; -- all watched auras local indicatorAuras; -- watched auras per indicator local DEFAULT_ICON = "Interface\\AddOns\\RaidFrameCustomization\\images\\rhomb"; -- Hide buff/debuff icons local function hideBlizzardBuffs(frame) -- used in CompactUnitFrame_UpdateAuras (Buffs, Debuffs, DispellableDebuffs) if frame.optionTable.displayBuffs then frame.optionTable.displayBuffs = false -- used in CompactUnitFrame_UpdateHealthColor, might not be set prior frame.optionTable.healthBarColorOverride = CreateColor(0.4, 0.4, 0.4); end if frame.optionTable.displayDebuffs then frame.optionTable.displayDebuffs = false end -- TODO if frame.optionTable.displayDispelDebuffs then frame.optionTable.displayDispelDebuffs = true end end hooksecurefunc("CompactUnitFrame_SetOptionTable", hideBlizzardBuffs); -- Set the appearance of the FontStrings local function setupIndicatorAppearance(frame) local frameName = frame:GetName(); if not f[frameName] then return end local font = media and media:Fetch('font', Indicators.db.profile.indicatorFont) or STANDARD_TEXT_FONT; local i; for i = 1, 5 do f[frameName][i].text:SetFont(font, Indicators.db.profile["textSize"..i], ""); f[frameName][i].icon:SetWidth(Indicators.db.profile["iconSize"..i]); f[frameName][i].icon:SetHeight(Indicators.db.profile["iconSize"..i]); if Indicators.db.profile["showIcon"..i] then f[frameName][i].icon:Show(); else f[frameName][i].icon:Hide(); end end end -- Create the FontStrings used for indicators local function setupCompactUnitFrame(frame) local frameName = frame:GetName(); local i; local positions = { "TOPLEFT", "TOPRIGHT", "CENTER", "BOTTOMLEFT", "BOTTOMRIGHT" }; local paddings = { {pad, -pad}, {-pad, -pad}, {0, 0}, {pad, pad}, {-pad, pad} }; -- Create indicators f[frameName] = {}; for i = 1, 5 do f[frameName][i] = {}; f[frameName][i].text = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall"); f[frameName][i].text:SetPoint(positions[i], frame, positions[i], paddings[i][1], paddings[i][2]); f[frameName][i].icon = frame:CreateTexture(nil, "OVERLAY"); f[frameName][i].icon:SetPoint(positions[i], frame, positions[i], paddings[i][1], paddings[i][2]); end setupIndicatorAppearance(frame); end -- Get all unit auras local function getAuras(unit) local unitBuffs = {}; local unitDebuffs = {}; local auraName, icon, count, expires, caster, debuffType, spellId; local i = 1; -- Get all unit buffs while true do auraName, _, icon, count, _, _, expires, caster, _, _, spellId = UnitBuff(unit, i); if not spellId then break end if watchedAuras[auraName] or watchedAuras[spellId] then -- possibly non-contiguous indexes, doesn't matter unitBuffs[i] = {}; unitBuffs[i].auraName = auraName; unitBuffs[i].spellId = spellId; unitBuffs[i].count = count; unitBuffs[i].expires = expires; unitBuffs[i].mine = UnitIsUnit(caster, "player"); unitBuffs[i].icon = icon; end i = i + 1; end -- Get all unit debuffs i = 1; while true do auraName, _, icon, count, debuffType, _, expires, caster, _, _, spellId = UnitDebuff(unit, i); if not spellId then break end -- TODO debuffType check better if watchedAuras[auraName] or watchedAuras[spellId] or watchedAuras[debuffType] then unitDebuffs[i] = {}; unitDebuffs[i].auraName = auraName; unitDebuffs[i].spellId = spellId; unitDebuffs[i].count = count; unitDebuffs[i].expires = expires; unitDebuffs[i].mine = UnitIsUnit(caster, "player"); unitDebuffs[i].icon = icon; unitDebuffs[i].debuffType = debuffType; end i = i + 1; end return unitBuffs, unitDebuffs; end -- Check the indicators on a frame and update the times on them local function updateIndicator(frame) if not frame.unit then return end local unit = frame.unit; local frameName = frame:GetName(); local currentTime = GetTime(); local i; -- Check if the indicator object exists, else create it if not f[frameName] then setupCompactUnitFrame(frame) end -- Hide if unit is dead/disconnected if (not UnitIsConnected(unit)) or UnitIsDeadOrGhost(frame.displayedUnit) then for i = 1, 5 do f[frameName][i].text:SetText(""); f[frameName][i].icon:SetTexture(""); end return; end local unitBuffs, unitDebuffs = getAuras(unit); for i = 1, 5 do -- try to find matching aura local found, aura; for _, aura in pairs(unitBuffs) do if indicatorAuras[i][aura.auraName] or indicatorAuras[i][aura.spellId] then found = aura; -- break on first matching buff cast by me -- otherwise continue through if aura.mine then break end end end if not found then -- search debuffs if buff was not found for _, aura in pairs(unitDebuffs) do if indicatorAuras[i][aura.auraName] or indicatorAuras[i][aura.spellId] or indicatorAuras[i][aura.debuffType] then found = aura; -- break on first matching debuff cast by me -- otherwise continue through if aura.mine then break end end end end if found then if Indicators.db.profile["mine"..i] and not found.mine then -- don't show f[frameName][i].icon:SetTexture(""); f[frameName][i].text:SetText(""); else if Indicators.db.profile["showIcon"..i] then -- show icon TODO coloring if Indicators.db.profile["useDefaultIcon"..i] then f[frameName][i].icon:SetTexture(DEFAULT_ICON); else f[frameName][i].icon:SetTexture(found.icon); end end -- TODO make show text into general setting -- under which you can select what text to show if Indicators.db.profile["showText"..i] then -- show text local text; local remaining = found.expires - currentTime; if remaining > 60 then text = string.format("%dm", ceil(remaining/60)); else text = string.format("%d", floor(remaining+0.5)); end if Indicators.db.profile["stack"..i] and found.count > 0 then if text then text = found.count.."-"..text; else text = found.count; end end -- colors f[frameName][i].text:SetTextColor( Indicators.db.profile["color"..i].r, Indicators.db.profile["color"..i].g, Indicators.db.profile["color"..i].b, Indicators.db.profile["color"..i].a ); if Indicators.db.profile["debuffColor"..i] then if found.debuffType then if found.debuffType == "Curse" then f[frameName][i].text:SetTextColor(0.6,0,1,1); elseif found.debuffType == "Disease" then f[frameName][i].text:SetTextColor(0.6,0.4,0,1); elseif found.debuffType == "Magic" then f[frameName][i].text:SetTextColor(0.2,0.6,1,1); elseif found.debuffType == "Poison" then f[frameName][i].text:SetTextColor(0,0.6,0,1); end end end f[frameName][i].text:SetText(text); end end else -- not found, show nothing f[frameName][i].icon:SetTexture(""); f[frameName][i].text:SetText(""); end end end -- Update all indicators function Indicators:UpdateAllIndicators() CompactRaidFrameContainer_ApplyToFrames(CompactRaidFrameContainer, "normal", updateIndicator); end -- Used to update everything that is affected by the configuration function Indicators:RefreshConfig() local i; CompactRaidFrameContainer_ApplyToFrames(CompactRaidFrameContainer, "normal", setupIndicatorAppearance); -- Format aura strings watchedAuras = {}; indicatorAuras = {}; local auraName, i for i = 1, 5 do indicatorAuras[i] = {}; for auraName in string.gmatch(Indicators.db.profile["auras"..i], "[^\n]+") do -- Grab each line auraName = string.gsub(auraName, "^%s*(.-)%s*$", "%1"); -- Strip any whitespaces if tonumber(auraName) then watchedAuras[tonumber(auraName)] = true; indicatorAuras[i][tonumber(auraName)] = true; else watchedAuras[auraName] = true; indicatorAuras[i][auraName] = true; end end end self:CancelAllTimers(); if next(watchedAuras) ~= nil then self.updateTimer = self:ScheduleRepeatingTimer("UpdateAllIndicators", 0.11); end end function Indicators:OnInitialize() -- Set up config pane self:SetupOptions(); -- Register callbacks for profile switching self.db.RegisterCallback(self, "OnProfileChanged", "RefreshConfig"); self.db.RegisterCallback(self, "OnProfileCopied", "RefreshConfig"); self.db.RegisterCallback(self, "OnProfileReset", "RefreshConfig"); end function Indicators:OnEnable() if self.db.profile.enabled then Indicators:RefreshConfig(); end end function Indicators:OnDisable() local i; -- Stop update self:CancelAllTimers(); -- Hide all indicators for frameName, _ in pairs(f) do for i = 1, 5 do f[frameName][i].text:SetText(""); f[frameName][i].icon:SetTexture(""); end end end function dPrint(s) DEFAULT_CHAT_FRAME:AddMessage("Indicators: ".. tostring(s)); end