pfUI:RegisterModule("buff", function () -- Hide Blizz BuffFrame:Hide() BuffFrame:UnregisterAllEvents() TemporaryEnchantFrame:Hide() TemporaryEnchantFrame:UnregisterAllEvents() local br, bg, bb, ba = GetStringColor(pfUI_config.appearance.border.color) -- Player aura slot ids, enumerated once per refresh pass (ScanPlayerAuraSlots) -- and read per button by its aura index: one GetAuraSlots walk per range -- instead of a by-index walk per button. local helpfulSlots, harmfulSlots = {}, {} local function ScanPlayerAuraSlots() ScanAuraSlots("player", "HELPFUL", helpfulSlots, 32) ScanAuraSlots("player", "HARMFUL", harmfulSlots, 16) end -- weapon enchants report only their time left, and no event as their -- charges and time run down, so the OnUpdate below refreshes their buttons -- too: the charges, and the timer when the enchant was renewed local function RefreshWeaponButton(buff, timeleft, charges) timeleft = (timeleft or 0) / 1000 charges = charges or 0 buff.stacks:SetText(charges > 1 and charges or "") local expires = GetTime() + timeleft if timeleft <= 0 then buff.cd:Clear() buff.cdExpires = nil elseif not buff.cdExpires or abs(buff.cdExpires - expires) > 1 then SetCooldownTimeLeft(buff.cd, timeleft) buff.cdExpires = expires end buff.cdStart = nil end local function RefreshBuffButton(buff) if buff.btype == "HELPFUL" then if C.buffs.separateweapons == "1" then buff.id = buff.gid - (buff.weapon ~= nil and buff.gid or 0) else buff.id = buff.gid - pfUI.buff.wepbuffs.count end else buff.id = buff.gid end if not buff.backdrop then CreateBackdrop(buff) CreateBackdropShadow(buff) end local slots = buff.btype == "HELPFUL" and helpfulSlots or harmfulSlots local name, icon, count, dispelType, duration, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAuraBySlot("player", slots[buff.id]) --detect weapon buffs if buff.btype == "HELPFUL" and ((C.buffs.separateweapons == "0" and buff.gid <= pfUI.buff.wepbuffs.count) or (pfUI.buff.wepbuffs.count > 0 and buff.weapon ~= nil)) then local mh, mhtime, mhcharge, oh, ohtime, ohcharge = GetWeaponEnchantInfo() if pfUI.buff.wepbuffs.count == 2 then if buff.gid == 1 then buff.mode = "MAINHAND" else buff.mode = "OFFHAND" end else if C.buffs.separateweapons == "0" then buff.mode = mh and "MAINHAND" or oh and "OFFHAND" else if buff.gid == 1 then buff.mode = mh and "MAINHAND" or oh and "OFFHAND" else buff:Hide() return end end end -- Set Weapon Texture, Border and Timer if buff.mode == "MAINHAND" then buff.texture:SetTexture(GetInventoryItemTexture("player", 16)) buff.backdrop:SetBackdropBorderColor(GetItemQualityColor(GetInventoryItemQuality("player", 16) or 1)) RefreshWeaponButton(buff, mhtime, mhcharge) elseif buff.mode == "OFFHAND" then buff.texture:SetTexture(GetInventoryItemTexture("player", 17)) buff.backdrop:SetBackdropBorderColor(GetItemQualityColor(GetInventoryItemQuality("player", 17) or 1)) RefreshWeaponButton(buff, ohtime, ohcharge) end elseif name and (( buff.btype == "HARMFUL" and C.buffs.debuffs == "1" ) or ( buff.btype == "HELPFUL" and C.buffs.buffs == "1" )) then -- Set Buff Texture and Border buff.mode = buff.btype buff.spellId = spellId buff.texture:SetTexture(icon) buff.stacks:SetText((count or 0) > 1 and count or "") if buff.btype == "HARMFUL" then local dispelColor = C_UnitAuras.GetAuraDispelTypeColor(dispelType) buff.backdrop:SetBackdropBorderColor(dispelColor:GetRGBA()) else buff.backdrop:SetBackdropBorderColor(br,bg,bb,ba) end -- start the timer from the aura's duration; permanent auras have none local start, dur = GetAuraTimer(duration, expirationTime) if dur <= 0 then buff.cd:Clear() buff.cdStart = nil elseif buff.cdStart == nil or abs(buff.cdStart - start) > .5 or buff.cdDuration ~= dur then CooldownFrame_SetTimer(buff.cd, start, dur, 1) buff.cdStart = start buff.cdDuration = dur end buff.cdExpires = nil else buff:Hide() return end buff:Show() end local function CreateBuffButton(i, btype, weapon) local buttonName, buttonParent if btype == "HELPFUL" then if weapon == 1 then buttonName = "pfWepBuffFrame" .. i buttonParent = pfUI.buff.wepbuffs else buttonName = "pfBuffFrameBuff" .. i buttonParent = pfUI.buff.buffs end else buttonName = "pfDebuffFrameBuff" .. i buttonParent = pfUI.buff.debuffs end local buff = CreateFrame("Button", buttonName, buttonParent) buff.texture = buff:CreateTexture("BuffIcon" .. i, "BACKGROUND") buff.texture:SetTexCoord(.07,.93,.07,.93) buff.texture:SetAllPoints(buff) -- overlay lifts the stacks above the sweep: a child frame renders above -- all of its parent's own regions, so they would dim under the dark -- wedge otherwise buff.overlay = CreateFrame("Frame", nil, buff) buff.overlay:SetAllPoints(buff) buff.stacks = buff.overlay:CreateFontString(nil, "OVERLAY", "GameFontNormal") buff.stacks:SetTextColor(1,1,1,1) buff.stacks:SetJustifyH("RIGHT") buff.stacks:SetJustifyV("BOTTOM") buff.stacks:SetAllPoints(buff) -- the sweep and the duration numbers (the template covers the button and -- starts hidden); RefreshBuffButton starts its timer buff.cd = CreateCooldown(buff, "buff", buttonName .. "Cooldown") buff.cd:SetFrameLevel(buff:GetFrameLevel() + 1) buff.overlay:SetFrameLevel(buff:GetFrameLevel() + 2) buff:RegisterForClicks("RightButtonUp") buff.weapon = weapon buff.btype = btype buff.gid = i -- PERF: OnUpdate moved to consolidated parent frame handler (see pfUI.buff:SetScript("OnUpdate")) -- Individual buff frames no longer have their own OnUpdate buff:SetScript("OnEnter", function() GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT") if this.mode == this.btype then GameTooltip:SetUnitAura("player", this.id, this.btype) if IsShiftKeyDown() then local aura = C_UnitAuras.GetAuraDataByIndex("player", this.id, this.btype) local playerlist = aura and GetUnbuffedRoster(aura.name) or "" if strlen(playerlist) > 0 then GameTooltip:AddLine(" ") GameTooltip:AddLine(T["Unbuffed"] .. ":", .3, 1, .8) GameTooltip:AddLine(playerlist,1,1,1,1) GameTooltip:Show() end end elseif this.mode == "MAINHAND" then GameTooltip:SetInventoryItem("player", 16) elseif this.mode == "OFFHAND" then GameTooltip:SetInventoryItem("player", 17) end end) buff:SetScript("OnLeave", GameTooltip_Hide) buff:SetScript("OnClick", function() if this.spellId then C_Spell.CancelSpellByID(this.spellId) end end) RefreshBuffButton(buff) return buff end pfUI.buff = CreateFrame("Frame", "pfGlobalBuffFrame", UIParent) pfUI.buff:RegisterEvent("PLAYER_AURAS_CHANGED") pfUI.buff:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") pfUI.buff:RegisterUnitEvent("UNIT_MODEL_CHANGED", "player") pfUI.buff:RegisterEvent("BUFF_UPDATE_DURATION_SELF") pfUI.buff:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF") pfUI.buff:SetScript("OnEvent", function() if C.buffs.weapons == "1" then local mh, mhtime, mhcharge, oh, ohtime, ohcharge = GetWeaponEnchantInfo() pfUI.buff.wepbuffs.count = (mh and 1 or 0) + (oh and 1 or 0) else pfUI.buff.wepbuffs.count = 0 end ScanPlayerAuraSlots() for i=1,32 do RefreshBuffButton(pfUI.buff.buffs.buttons[i]) end for i=1,16 do RefreshBuffButton(pfUI.buff.debuffs.buttons[i]) end if C.buffs.separateweapons == "1" then for i=1,2 do RefreshBuffButton(pfUI.buff.wepbuffs.buttons[i]) end end end) -- the buttons that can show a weapon enchant: the first two buffs, or the -- separate weapon buttons local weaponButtons -- weapon enchants, see RefreshWeaponButton pfUI.buff:SetScript("OnUpdate", function() local now = GetTime() if not this.nextUpdate then this.nextUpdate = now + 0.1 end if this.nextUpdate > now then return end this.nextUpdate = now + 0.1 local _, mhtime, mhcharge, _, ohtime, ohcharge = GetWeaponEnchantInfo() for _, buff in ipairs(weaponButtons) do if buff:IsShown() then if buff.mode == "MAINHAND" then RefreshWeaponButton(buff, mhtime, mhcharge) elseif buff.mode == "OFFHAND" then RefreshWeaponButton(buff, ohtime, ohcharge) end end end end) -- CreateBuffButton refreshes each new button from the slot buffers ScanPlayerAuraSlots() -- Weapon Buffs pfUI.buff.wepbuffs = CreateFrame("Frame", "pfWepBuffFrame", UIParent) pfUI.buff.wepbuffs.count = 0 pfUI.buff.wepbuffs.buttons = {} for i=1,2 do pfUI.buff.wepbuffs.buttons[i] = CreateBuffButton(i, "HELPFUL", 1) end -- Buff Frame pfUI.buff.buffs = CreateFrame("Frame", "pfBuffFrame", UIParent) pfUI.buff.buffs.buttons = {} for i=1,32 do pfUI.buff.buffs.buttons[i] = CreateBuffButton(i, "HELPFUL") end -- Debuffs pfUI.buff.debuffs = CreateFrame("Frame", "pfDebuffFrame", UIParent) pfUI.buff.debuffs.buttons = {} for i=1,16 do pfUI.buff.debuffs.buttons[i] = CreateBuffButton(i, "HARMFUL") end weaponButtons = { pfUI.buff.buffs.buttons[1], pfUI.buff.buffs.buttons[2], pfUI.buff.wepbuffs.buttons[1], pfUI.buff.wepbuffs.buttons[2], } -- config loading function pfUI.buff:UpdateConfigBuffButton(buff) local fontsize = C.buffs.fontsize == "-1" and C.global.font_size or C.buffs.fontsize local buffSize, buffSpacing = tonumber(C.buffs.size), tonumber(C.buffs.spacing) local rowextra = C.buffs.textinside == "1" and 0 or GetCooldownFontSize(buffSize) * 1.5 local rowcount, relFrame, offsetX, offsetY if buff.btype == "HELPFUL" then if buff.weapon == 1 and C.buffs.separateweapons == "1" then rowcount = floor((buff.gid-1) / tonumber(C.buffs.wepbuffrowsize)) relFrame = pfUI.buff.wepbuffs offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.wepbuffrowsize))*(buffSize+2*buffSpacing) offsetY = -(rowcount) * (rowextra+buffSize+2*buffSpacing) else rowcount = floor((buff.gid-1) / tonumber(C.buffs.buffrowsize)) relFrame = pfUI.buff.buffs offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.buffrowsize))*(buffSize+2*buffSpacing) offsetY = -(rowcount) * (rowextra+buffSize+2*buffSpacing) end else rowcount = floor((buff.gid-1) / tonumber(C.buffs.debuffrowsize)) relFrame = pfUI.buff.debuffs offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.debuffrowsize))*(buffSize+2*buffSpacing) offsetY = -(rowcount) * (rowextra+buffSize+2*buffSpacing) end buff:SetSize(buffSize, buffSize) buff:ClearAllPoints() buff:SetPoint("TOPRIGHT", relFrame, "TOPRIGHT",offsetX, offsetY) buff.stacks:SetFont(pfUI.font_default, fontsize+1, "OUTLINE") -- the duration numbers sit inside the icon or below it local countdown = buff.cd:GetCountdownFontString() countdown:ClearAllPoints() if C.buffs.textinside == "1" then countdown:SetPoint("CENTER", buff, "CENTER", 0, 0) else countdown:SetPoint("TOP", buff, "BOTTOM", 0, -3) end end function pfUI.buff:UpdateConfig() local spacing = tonumber(C.buffs.spacing) local cell = tonumber(C.buffs.size) + 2 * spacing local rowextra = C.buffs.textinside == "1" and 0 or GetCooldownFontSize(tonumber(C.buffs.size)) * 1.5 local function SizeBuffFrame(frame, rowsize, count) rowsize = tonumber(rowsize) frame:SetSize(rowsize * cell, ceil(count / rowsize) * (rowextra + cell)) end SizeBuffFrame(pfUI.buff.buffs, C.buffs.buffrowsize, 32) pfUI.buff.buffs:SetPoint("TOPRIGHT", pfUI.minimap or UIParent, "TOPLEFT", -4 * spacing, 0) UpdateMovable(pfUI.buff.buffs) SizeBuffFrame(pfUI.buff.debuffs, C.buffs.debuffrowsize, 16) pfUI.buff.debuffs:SetPoint("TOPRIGHT", pfUI.buff.buffs, "BOTTOMRIGHT", 0, 0) UpdateMovable(pfUI.buff.debuffs) if C.buffs.separateweapons == "1" then pfUI.buff.wepbuffs:ClearAllPoints() SizeBuffFrame(pfUI.buff.wepbuffs, C.buffs.wepbuffrowsize, 2) pfUI.buff.wepbuffs:SetPoint("TOPRIGHT", pfUI.buff.debuffs, "BOTTOMRIGHT", 0, 0) pfUI.buff.wepbuffs:Show() UpdateMovable(pfUI.buff.wepbuffs) else pfUI.buff.wepbuffs:Hide() RemoveMovable(pfUI.buff.wepbuffs) end for i=1,32 do pfUI.buff:UpdateConfigBuffButton(pfUI.buff.buffs.buttons[i]) end for i=1,16 do pfUI.buff:UpdateConfigBuffButton(pfUI.buff.debuffs.buttons[i]) end for i=1,2 do pfUI.buff:UpdateConfigBuffButton(pfUI.buff.wepbuffs.buttons[i]) end pfUI.buff:GetScript("OnEvent")() end pfUI.buff:UpdateConfig() end)