-- load pfUI environment setfenv(1, pfUI:GetEnvironment()) pfUI.uf = CreateFrame("Frame", nil, UIParent) pfUI.uf:SetScript("OnUpdate", function() if InCombatLockdown and not InCombatLockdown() then for frame in pairs(pfUI.uf.delayed) do frame:UpdateVisibility() pfUI.uf.delayed[frame] = nil end end end) pfUI.uf.frames = {} pfUI.uf.delayed = {} -- slash command to toggle unitframe test mode _G.SLASH_PFTEST1, _G.SLASH_PFTEST2 = "/pftest", "/pfuftest" _G.SlashCmdList.PFTEST = function() pfUI.uf.showall = not pfUI.uf.showall end local scanner local glow = { edgeFile = pfUI.media["img:glow"], edgeSize = 8, insets = {left = 0, right = 0, top = 0, bottom = 0}, } local glow2 = { edgeFile = pfUI.media["img:glow2"], edgeSize = 8, insets = {left = 0, right = 0, top = 0, bottom = 0}, } local maxdurations = {} local function BuffOnUpdate() if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .2 end local timeleft = GetPlayerBuffTimeLeft(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HELPFUL")) local texture = GetPlayerBuffTexture(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HELPFUL")) local start = 0 if timeleft > 0 then if not maxdurations[texture] then maxdurations[texture] = timeleft elseif maxdurations[texture] and maxdurations[texture] < timeleft then maxdurations[texture] = timeleft end start = GetTime() + timeleft - maxdurations[texture] end CooldownFrame_SetTimer(this.cd, start, maxdurations[texture], timeleft > 0 and 1 or 0) end local function TargetBuffOnUpdate() local name, rank, icon, count, duration, timeleft = _G.UnitBuff("target", this.id) if duration and timeleft then CooldownFrame_SetTimer(this.cd, GetTime() + timeleft - duration, duration, 1) else CooldownFrame_SetTimer(this.cd, 0, 0, 0) end end local function BuffOnEnter() local parent = this:GetParent() if not parent.label then return end GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT") if parent.label == "player" then GameTooltip:SetPlayerBuff(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HELPFUL")) else GameTooltip:SetUnitBuff(parent.label .. parent.id, this.id) end if IsShiftKeyDown() then local texture = parent.label == "player" and GetPlayerBuffTexture(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HELPFUL")) or UnitBuff(parent.label .. parent.id, this.id) local playerlist = "" local first = true if UnitInRaid("player") then for i=1,40 do local unitstr = "raid" .. i if not UnitHasBuff(unitstr, texture) and UnitName(unitstr) then playerlist = playerlist .. ( not first and ", " or "") .. GetUnitColor(unitstr) .. UnitName(unitstr) .. "|r" first = nil end end else if not UnitHasBuff("player", texture) then playerlist = playerlist .. ( not first and ", " or "") .. GetUnitColor("player") .. UnitName("player") .. "|r" first = nil end for i=1,4 do local unitstr = "party" .. i if not UnitHasBuff(unitstr, texture) and UnitName(unitstr) then playerlist = playerlist .. ( not first and ", " or "") .. GetUnitColor(unitstr) .. UnitName(unitstr) .. "|r" first = nil end end end 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 end local function BuffOnLeave() GameTooltip:Hide() end local function BuffOnClick() if this:GetParent().label == "player" then CancelPlayerBuff(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HELPFUL")) end end local function DebuffOnUpdate() if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .2 end local timeleft = GetPlayerBuffTimeLeft(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HARMFUL")) local texture = GetPlayerBuffTexture(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HARMFUL")) local start = 0 if timeleft > 0 then if not maxdurations[texture] then maxdurations[texture] = timeleft elseif maxdurations[texture] and maxdurations[texture] < timeleft then maxdurations[texture] = timeleft end start = GetTime() + timeleft - maxdurations[texture] end CooldownFrame_SetTimer(this.cd, start, maxdurations[texture], timeleft > 0 and 1 or 0) end local function DebuffOnEnter() if not this:GetParent().label then return end GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT") if this:GetParent().label == "player" then GameTooltip:SetPlayerBuff(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HARMFUL")) else GameTooltip:SetUnitDebuff(this:GetParent().label .. this:GetParent().id, this.id) end end local function DebuffOnLeave() GameTooltip:Hide() end local function DebuffOnClick() if this:GetParent().label == "player" then CancelPlayerBuff(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HARMFUL")) end end local visibilityscan = CreateFrame("Frame", "pfUnitFrameVisibility", UIParent) visibilityscan.frames = {} visibilityscan:SetScript("OnUpdate", function() if ( this.limit or 1) > GetTime() then return else this.limit = GetTime() + .2 end for frame in pairs(this.frames) do frame:UpdateVisibility() end end) local aggrodata = { } function pfUI.api.UnitHasAggro(unit) if aggrodata[unit] and GetTime() < aggrodata[unit].check + 1 then return aggrodata[unit].state end aggrodata[unit] = aggrodata[unit] or { } aggrodata[unit].check = GetTime() aggrodata[unit].state = 0 if UnitExists(unit) and UnitIsFriend(unit, "player") then for u in pairs(pfValidUnits) do local t = u .. "target" local tt = t .. "target" if UnitExists(t) and UnitIsUnit(t, unit) and UnitCanAttack(u, unit) then aggrodata[unit].state = aggrodata[unit].state + 1 end if UnitExists(tt) and UnitIsUnit(tt, unit) and UnitCanAttack(t, unit) then aggrodata[unit].state = aggrodata[unit].state + 1 end end end return aggrodata[unit].state end pfUI.uf.glow = CreateFrame("Frame") pfUI.uf.glow:SetScript("OnUpdate", function() local fpsmod = GetFramerate() / 30 if not this.val or this.val >= .8 then this.mod = -0.01 / fpsmod elseif this.val <= .4 then this.mod = 0.01 / fpsmod end this.val = this.val + this.mod end) pfUI.uf.glow.mod = 0 pfUI.uf.glow.val = 0 function pfUI.uf.glow.UpdateGlowAnimation() this:SetAlpha(pfUI.uf.glow.val) end local detect_icon, detect_name function pfUI.uf:DetectBuff(name, id) if not name or not id then return end -- skip here if disabled if pfUI_config.unitframes.buffdetect == "0" then return UnitBuff(name, id) end -- clear previously assigned detect_icon, detect_name = nil, nil -- register tooltip scanner scanner = scanner or libtipscan:GetScanner("unitframes") -- make sure the icon cache exists pfUI_cache.buff_icons = pfUI_cache.buff_icons or {} -- check the regular way detect_icon = UnitBuff(name, id) if detect_icon then if not L["icons"][detect_name] and not pfUI_cache.buff_icons[detect_icon] then -- read buff name and cache it scanner:SetUnitBuff(name, id) detect_name = scanner:Line(1) if detect_name then pfUI_cache.buff_icons[detect_icon] = detect_name end end -- return the regular function return UnitBuff(name, id) end -- try to guess the buff based on tooltips and icon caches scanner:SetUnitBuff(name, id) detect_name = scanner:Line(1) if detect_name then -- try to find the spell icon in locales if L["icons"][detect_name] then return "Interface\\Icons\\" .. L["icons"][detect_name], 1 end -- try to find the spell icon in caches for icon, name in pairs(pfUI_cache.buff_icons) do if name == detect_name then return icon, 1 end end -- return fallback image return "interface\\icons\\inv_misc_questionmark", 1 end -- nothing found return nil end function pfUI.uf:UpdateVisibility() local self = self or this -- we're infight, delay the update if InCombatLockdown and InCombatLockdown() then pfUI.uf.delayed[self] = true return end -- cache result of strsub to avoid repeating calls if not self.cache_raid then if strsub(self:GetName(),0,6) == "pfRaid" then self.cache_raid = tonumber(strsub(self:GetName(),7,8)) else self.cache_raid = 0 end end -- show groupframes as raid if self.cache_raid > 0 then local id = self.cache_raid -- always show self in raidframes if not UnitInRaid("player") and GetNumPartyMembers() == 0 and C.unitframes.selfinraid == "1" and id == 1 then self.id = "" self.label = "player" -- use raidframes for groups elseif not UnitInRaid("player") and GetNumPartyMembers() > 0 and C.unitframes.raidforgroup == "1" then if id == 1 then self.id = "" self.label = "player" elseif id <= 5 then self.id = id - 1 self.label = "party" end -- reset to regular raid unitstrings elseif self.label == "party" or self.label == "player" then self.id = id self.label = "raid" end end -- display every unit as player while pfUI.uf.showall is set if pfUI.uf.showall then self._label = self._label or self.label self._id = self._id or self.id self.label, self.id = "player", "" elseif not pfUI.uf.showall and self._label and self._id then self.label, self.id = self._label, self._id self._label, self._id = nil, nil end local unitstr = string.format("%s%s", self.label or "", self.id or "") local visibility = string.format("[target=%s,exists] show; hide", unitstr) if pfUI.unlock and pfUI.unlock:IsShown() then -- display during unlock mode visibility = "show" self.visible = true elseif self.config.visible == "0" then -- frame shall not be visible visibility = "hide" self.visible = nil elseif C["unitframes"]["group"]["hide_in_raid"] == "1" and self.label and strsub(self.label,0,5) == "party" and UnitInRaid("player") then -- hide group while in raid and option is set visibility = "hide" self.visible = nil elseif ( self.fname == "Group0" or self.fname == "PartyPet0" or self.fname == "Party0Target" ) and (GetNumPartyMembers() <= 0 or (C["unitframes"]["group"]["hide_in_raid"] == "1" and UnitInRaid("player"))) then -- hide self in group if solo or hide in raid is set visibility = "hide" self.visible = nil end -- tbc visibility if pfUI.client > 11200 then self:SetAttribute("unit", unitstr) -- update visibility condition on change if self.visibilitycondition ~= visibility then RegisterStateDriver(self, 'visibility', visibility) self.visibilitycondition = visibility self.visible = true end return end -- vanilla visibility if self.unitname and self.unitname ~= "focus" and self.unitname ~= "focustarget" then self:Show() elseif visibility == "hide" then self:Hide() elseif visibility == "show" then self:Show() else if UnitName(unitstr) then -- hide existing but too far away pet and pets of old group members if self.label == "partypet" then if not UnitIsVisible(unitstr) or not UnitExists("party" .. self.id) then self:Hide() return end elseif self.label == "pettarget" then if not UnitIsVisible(unitstr) or not UnitExists("pet") then self:Hide() return end end self:Show() else self.lastUnit = nil self:Hide() end end end function pfUI.uf:UpdateFrameSize() local rawborder, default_border = GetBorderSize("unitframes") local spacing = self.config.pspace * GetPerfectPixel() local width = self.config.width local height = self.config.height local pheight = self.config.pheight local ptwidth = self.config.portraitwidth local ptheight = self.config.portraitheight local real_height = height + spacing + pheight + 2*default_border if spacing ~= abs(spacing) and abs(spacing) > tonumber(pheight) then real_height = height spacing = 0 end local portrait = 0 if self.config.portrait == "left" or self.config.portrait == "right" then if ptwidth == "-1" and ptheight == "-1" then -- align portrait size to frame self.portrait:SetWidth(real_height) self.portrait:SetHeight(real_height) portrait = real_height + spacing + 2*default_border else -- use custom portrait size self.portrait:SetWidth(ptwidth) self.portrait:SetHeight(ptheight) portrait = ptwidth + spacing + 2*default_border end end self:SetWidth(width + portrait) self:SetHeight(real_height) end function pfUI.uf:UpdateConfig() local f = self local C = pfUI_config local rawborder, default_border = GetBorderSize("unitframes") local spacing = f.config.pspace * GetPerfectPixel() local cooldown_text = tonumber(f.config.cooldown_text) local cooldown_anim = tonumber(f.config.cooldown_anim) local relative_point = "BOTTOM" if f.config.panchor == "TOPLEFT" then relative_point = "BOTTOMLEFT" elseif f.config.panchor == "TOPRIGHT" then relative_point = "BOTTOMRIGHT" end f.dispellable = nil f.indicators = nil f.indicator_custom = nil f.alpha_visible = tonumber(f.config.alpha_visible) f.alpha_outrange = tonumber(f.config.alpha_outrange) f.alpha_offline = tonumber(f.config.alpha_offline) f:SetFrameStrata("MEDIUM") f.glow:SetFrameStrata("BACKGROUND") f.glow:SetFrameLevel(0) f.glow:SetBackdrop(glow2) f.glow:SetPoint("TOPLEFT", f, "TOPLEFT", -6 - default_border,6 + default_border) f.glow:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 6 + default_border,-6 - default_border) f.glow:SetScript("OnUpdate", pfUI.uf.glow.UpdateGlowAnimation) f.glow:Hide() f.combat:SetWidth(tonumber(f.config.squaresize)) f.combat:SetHeight(tonumber(f.config.squaresize)) f.combat:ClearAllPoints() f.combat:SetPoint(f.config.squarepos, 0, 0) f.combat:Hide() f.hp:ClearAllPoints() f.hp:SetPoint("TOP", 0, 0) f.hp:SetWidth(f.config.width) f.hp:SetHeight(f.config.height) if tonumber(f.config.height) < 0 then f.hp:Hide() end pfUI.api.CreateBackdrop(f.hp, default_border) f.hp.bar:SetStatusBarTexture(pfUI.media[f.config.bartexture]) f.hp.bar:SetAllPoints(f.hp) if f.config.verticalbar == "1" then f.hp.bar:SetOrientation("VERTICAL") else f.hp.bar:SetOrientation("HORIZONTAL") end local custombg = f.config.defcolor == "0" and f.config.custombg or C.unitframes.custombg local custombgcolor = f.config.defcolor == "0" and f.config.custombgcolor or C.unitframes.custombgcolor if custombg == "1" then local cr, cg, cb, ca = GetStringColor(custombgcolor) cr, cg, cb, ca = tonumber(cr), tonumber(cg), tonumber(cb), tonumber(ca) f.hp.bar:SetStatusBarBackgroundTexture(cr,cg,cb,ca) end f.power:ClearAllPoints() f.power:SetPoint(f.config.panchor, f.hp, relative_point, f.config.poffx, -2 * default_border - spacing + f.config.poffy * GetPerfectPixel()) f.power:SetWidth((f.config.pwidth ~= "-1" and f.config.pwidth or f.config.width)) f.power:SetHeight(f.config.pheight) if tonumber(f.config.pheight) < 0 then f.power:Hide() end pfUI.api.CreateBackdrop(f.power, default_border) f.power.bar:SetStatusBarTexture(pfUI.media[f.config.pbartexture]) f.power.bar:SetAllPoints(f.power) local custompbg = f.config.defcolor == "0" and f.config.custompbg or C.unitframes.custompbg local custompbgcolor = f.config.defcolor == "0" and f.config.custompbgcolor or C.unitframes.custompbgcolor if custompbg == "1" then local cr, cg, cb, ca = GetStringColor(custompbgcolor) cr, cg, cb, ca = tonumber(cr), tonumber(cg), tonumber(cb), tonumber(ca) f.power.bar:SetStatusBarBackgroundTexture(cr,cg,cb,ca) end local fontname, fontsize, fontstyle if f.config.customfont == "1" then fontname = pfUI.media[f.config.customfont_name] fontsize = tonumber(f.config.customfont_size) fontstyle = f.config.customfont_style else fontname = pfUI.font_unit fontsize = tonumber(C.global.font_unit_size) fontstyle = C.global.font_unit_style end f.portrait.tex:SetAllPoints(f.portrait) f.portrait.tex:SetTexCoord(.1, .9, .1, .9) f.portrait.model:SetAllPoints(f.portrait) if f.config.portrait == "bar" then f.portrait:SetParent(f.hp.bar) f.portrait:SetAllPoints(f.hp.bar) f.portrait:SetAlpha(C.unitframes.portraitalpha) if f.portrait.backdrop then f.portrait.backdrop:Hide() end -- place portrait below fonts f.portrait.model:SetFrameLevel(3) f.portrait:Show() elseif f.config.portrait == "left" then f.portrait:SetParent(f) f.portrait:ClearAllPoints() f.portrait:SetPoint("LEFT", f, "LEFT", 0, 0) f.hp:ClearAllPoints() f.hp:SetPoint("TOPRIGHT", f, "TOPRIGHT", 0, 0) f.portrait:SetAlpha(f:GetAlpha()) -- make sure incHeal is above f.portrait:SetFrameStrata("BACKGROUND") f.portrait.model:SetFrameStrata("BACKGROUND") f.portrait.model:SetFrameLevel(1) pfUI.api.CreateBackdrop(f.portrait, default_border) f.portrait.backdrop:Show() f.portrait:Show() elseif f.config.portrait == "right" then f.portrait:SetParent(f) f.portrait:ClearAllPoints() f.portrait:SetPoint("RIGHT", f, "RIGHT", 0, 0) f.hp:ClearAllPoints() f.hp:SetPoint("TOPLEFT", f, "TOPLEFT", 0, 0) f.portrait:SetAlpha(f:GetAlpha()) -- make sure incHeal is above f.portrait:SetFrameStrata("BACKGROUND") f.portrait.model:SetFrameStrata("BACKGROUND") f.portrait.model:SetFrameLevel(1) pfUI.api.CreateBackdrop(f.portrait, default_border) f.portrait.backdrop:Show() f.portrait:Show() else f.portrait:Hide() end if f.group then if f.config.raidgrouplabel == "1" then f.group:Show() else f.group:Hide() end local xoff = tonumber(f.config.grouplabelxoff) or 0 local yoff = tonumber(f.config.grouplabelyoff) or 8 f.group:SetPoint("TOPLEFT", f, "BOTTOMLEFT", xoff, yoff) f.group:SetPoint("TOPRIGHT", f, "BOTTOMRIGHT", xoff, yoff) end if f.config.hitindicator == "1" then f.feedbackText:SetFont(pfUI.media[f.config.hitindicatorfont], f.config.hitindicatorsize, "OUTLINE") f.feedbackFontHeight = f.config.hitindicatorsize f.feedbackStartTime = GetTime() if f.config.portrait == "bar" or f.config.portrait == "off" then f.feedbackText:SetParent(f.hp.bar) f.feedbackText:ClearAllPoints() f.feedbackText:SetPoint("CENTER", f.hp.bar, "CENTER") else f.feedbackText:SetParent(f.portrait) f.feedbackText:ClearAllPoints() f.feedbackText:SetPoint("CENTER", f.portrait, "CENTER") end f:RegisterEvent("UNIT_COMBAT") else f.feedbackText:Hide() f:UnregisterEvent("UNIT_COMBAT") end f.hpLeftText:SetFontObject(GameFontWhite) f.hpLeftText:SetFont(fontname, fontsize, fontstyle) f.hpLeftText:SetJustifyH("LEFT") f.hpLeftText:ClearAllPoints() f.hpLeftText:SetPoint("TOPLEFT",f.hp.bar, "TOPLEFT", 2*(default_border + f.config.txthpleftoffx), 1 + tonumber(f.config.txthpleftoffy)) f.hpLeftText:SetPoint("BOTTOMRIGHT",f.hp.bar, "BOTTOMRIGHT", -2*(default_border + f.config.txthpleftoffx), f.config.txthpleftoffy) f.hpRightText:SetFontObject(GameFontWhite) f.hpRightText:SetFont(fontname, fontsize, fontstyle) f.hpRightText:SetJustifyH("RIGHT") f.hpRightText:ClearAllPoints() f.hpRightText:SetPoint("TOPLEFT",f.hp.bar, "TOPLEFT", 2*(default_border + f.config.txthprightoffx), 1 + tonumber(f.config.txthprightoffy)) f.hpRightText:SetPoint("BOTTOMRIGHT",f.hp.bar, "BOTTOMRIGHT", -2*(default_border + f.config.txthprightoffx), f.config.txthprightoffy) f.hpCenterText:SetFontObject(GameFontWhite) f.hpCenterText:SetFont(fontname, fontsize, fontstyle) f.hpCenterText:SetJustifyH("CENTER") f.hpCenterText:ClearAllPoints() f.hpCenterText:SetPoint("TOPLEFT",f.hp.bar, "TOPLEFT", f.config.txthpcenteroffx, 1 + tonumber(f.config.txthpcenteroffy)) f.hpCenterText:SetPoint("BOTTOMRIGHT",f.hp.bar, "BOTTOMRIGHT", f.config.txthpcenteroffx, f.config.txthpcenteroffy) f.powerLeftText:SetFontObject(GameFontWhite) f.powerLeftText:SetFont(fontname, fontsize, fontstyle) f.powerLeftText:SetJustifyH("LEFT") f.powerLeftText:ClearAllPoints() f.powerLeftText:SetPoint("TOPLEFT",f.power.bar, "TOPLEFT", 2*(default_border + f.config.txtpowerleftoffx), 1 + tonumber(f.config.txtpowerleftoffy)) f.powerLeftText:SetPoint("BOTTOMRIGHT",f.power.bar, "BOTTOMRIGHT", -2*(default_border + f.config.txtpowerleftoffx), f.config.txtpowerleftoffy) f.powerRightText:SetFontObject(GameFontWhite) f.powerRightText:SetFont(fontname, fontsize, fontstyle) f.powerRightText:SetJustifyH("RIGHT") f.powerRightText:ClearAllPoints() f.powerRightText:SetPoint("TOPLEFT",f.power.bar, "TOPLEFT", 2*(default_border + f.config.txtpowerrightoffx), 1 + tonumber(f.config.txtpowerrightoffy)) f.powerRightText:SetPoint("BOTTOMRIGHT",f.power.bar, "BOTTOMRIGHT", -2*(default_border + f.config.txtpowerrightoffx), f.config.txtpowerrightoffy) f.powerCenterText:SetFontObject(GameFontWhite) f.powerCenterText:SetFont(fontname, fontsize, fontstyle) f.powerCenterText:SetJustifyH("CENTER") f.powerCenterText:ClearAllPoints() f.powerCenterText:SetPoint("TOPLEFT",f.power.bar, "TOPLEFT", f.config.txtpowercenteroffx, 1 + tonumber(f.config.txtpowercenteroffy)) f.powerCenterText:SetPoint("BOTTOMRIGHT",f.power.bar, "BOTTOMRIGHT", f.config.txtpowercenteroffx, f.config.txtpowercenteroffy) f.incHeal:SetHeight(f.config.height) f.incHeal:SetWidth(f.config.width) f.incHeal.texture:SetTexture(pfUI.media["img:bar"]) local cr, cg, cb, ca = GetStringColor(f.config.healcolor) cr, cg, cb, ca = tonumber(cr), tonumber(cg), tonumber(cb), tonumber(ca) f.incHeal.texture:SetVertexColor(cr, cg, cb, ca) f.incHeal:Hide() if f.config.verticalbar == "0" then f.incHeal:ClearAllPoints() f.incHeal:SetPoint("TOPLEFT", f.hp.bar, "TOPLEFT", 0, 0) else f.incHeal:ClearAllPoints() f.incHeal:SetPoint("BOTTOM", f.hp.bar, "BOTTOM", 0, 0) end f.ressIcon:SetFrameLevel(16) f.ressIcon:SetWidth(32) f.ressIcon:SetHeight(32) f.ressIcon:SetPoint("CENTER", f, "CENTER", 0, 4) f.ressIcon.texture:SetTexture(pfUI.media["img:ress"]) f.ressIcon.texture:SetAllPoints(f.ressIcon) f.ressIcon:Hide() f.leaderIcon:SetWidth(10) f.leaderIcon:SetHeight(10) f.leaderIcon:SetPoint("CENTER", f, "TOPLEFT", 0, 0) f.leaderIcon.texture:SetTexture("Interface\\GROUPFRAME\\UI-Group-LeaderIcon") f.leaderIcon.texture:SetAllPoints(f.leaderIcon) f.leaderIcon:Hide() f.lootIcon:SetWidth(10) f.lootIcon:SetHeight(10) f.lootIcon:SetPoint("CENTER", f, "LEFT", 0, 0) f.lootIcon.texture:SetTexture("Interface\\GROUPFRAME\\UI-Group-MasterLooter") f.lootIcon.texture:SetAllPoints(f.lootIcon) f.lootIcon:Hide() f.pvpIcon:SetWidth(f.config.pvpiconsize) f.pvpIcon:SetHeight(f.config.pvpiconsize) f.pvpIcon:SetPoint(f.config.pvpiconalign, f, f.config.pvpiconalign, f.config.pvpiconoffx, f.config.pvpiconoffy) f.pvpIcon.texture:SetTexture(pfUI.media["img:pvp"]) f.pvpIcon.texture:SetAllPoints(f.pvpIcon) f.pvpIcon.texture:SetVertexColor(1,1,1,.5) f.pvpIcon:Hide() f.raidIcon:SetWidth(f.config.raidiconsize) f.raidIcon:SetHeight(f.config.raidiconsize) f.raidIcon:SetPoint("CENTER", f, f.config.raidiconalign, f.config.raidiconoffx, f.config.raidiconoffy) f.raidIcon.texture:SetTexture(pfUI.media["img:raidicons"]) f.raidIcon.texture:SetAllPoints(f.raidIcon) f.raidIcon:Hide() f.restIcon:SetWidth(16) f.restIcon:SetHeight(16) f.restIcon:SetPoint("TOP", f, "TOPLEFT", 0, -1) f.restIcon.texture:SetTexture("Interface\\CharacterFrame\\UI-StateIcon", true) f.restIcon.texture:SetTexCoord(0, .5, 0, .421875) f.restIcon.texture:SetAllPoints(f.restIcon) f.restIcon:Hide() f.happinessIcon:SetWidth(tonumber(C.unitframes.pet.happinesssize)) f.happinessIcon:SetHeight(tonumber(C.unitframes.pet.happinesssize)) f.happinessIcon:SetPoint("CENTER", f, "TOPLEFT", default_border, -default_border) f.happinessIcon.texture:SetTexture(pfUI.media["img:neutral"]) f.happinessIcon.texture:SetAllPoints(f.happinessIcon) f.happinessIcon.texture:SetVertexColor(1, 1, 0, 1) f.happinessIcon:Hide() if f.config.buffs == "off" then for i=1, 32 do if f.buffs and f.buffs[i] then f.buffs[i]:Hide() f.buffs[i] = nil end end f.buffs = nil else f.buffs = f.buffs or {} for i=1, 32 do if i > tonumber(f.config.bufflimit) then break end local perrow = f.config.buffperrow local row = floor((i-1) / perrow) f.buffs[i] = f.buffs[i] or CreateFrame("Button", "pfUI" .. f.fname .. "Buff" .. i, f) f.buffs[i].texture = f.buffs[i].texture or f.buffs[i]:CreateTexture() f.buffs[i].texture:SetTexCoord(.08, .92, .08, .92) f.buffs[i].texture:SetAllPoints() f.buffs[i].stacks = f.buffs[i].stacks or f.buffs[i]:CreateFontString(nil, "OVERLAY", f.buffs[i]) f.buffs[i].stacks:SetFont(pfUI.font_unit, C.global.font_unit_size, "OUTLINE") f.buffs[i].stacks:SetPoint("BOTTOMRIGHT", f.buffs[i], 2, -2) f.buffs[i].stacks:SetJustifyH("LEFT") f.buffs[i].stacks:SetShadowColor(0, 0, 0) f.buffs[i].stacks:SetShadowOffset(0.8, -0.8) f.buffs[i].stacks:SetTextColor(1,1,.5) f.buffs[i].cd = f.buffs[i].cd or CreateFrame(COOLDOWN_FRAME_TYPE, f.buffs[i]:GetName() .. "Cooldown", f.buffs[i], "CooldownFrameTemplate") f.buffs[i].cd.pfCooldownType = "ALL" f.buffs[i].cd.pfCooldownStyleText = cooldown_text f.buffs[i].cd.pfCooldownStyleAnimation = cooldown_anim f.buffs[i].id = i f.buffs[i]:Hide() f.buffs[i]:SetFrameLevel(12) CreateBackdrop(f.buffs[i], default_border) f.buffs[i]:RegisterForClicks("RightButtonUp") f.buffs[i]:ClearAllPoints() local invert_h, invert_v, af if f.config.buffs == "TOPLEFT" then invert_h = 1 invert_v = 1 af = "BOTTOMLEFT" elseif f.config.buffs == "BOTTOMLEFT" then invert_h = -1 invert_v = 1 af = "TOPLEFT" elseif f.config.buffs == "TOPRIGHT" then invert_h = 1 invert_v = -1 af = "BOTTOMRIGHT" elseif f.config.buffs == "BOTTOMRIGHT" then invert_h = -1 invert_v = -1 af = "TOPRIGHT" end local anchor = f.config.portraitheight ~= "-1" and f.hp or f if anchor == f.hp and (f.config.buffs == "BOTTOMLEFT" or f.config.buffs == "BOTTOMRIGHT") then anchor = f.power end local multiply = C.appearance.border.force_blizz == "1" and 1 or 2 f.buffs[i]:SetPoint(af, anchor, f.config.buffs, invert_v * (i-1-row*perrow)*(multiply*default_border + f.config.buffsize + 1), invert_h * (row*(multiply*default_border + f.config.buffsize + 1) + (multiply*default_border + 1))) f.buffs[i]:SetWidth(f.config.buffsize) f.buffs[i]:SetHeight(f.config.buffsize) if f:GetName() == "pfPlayer" then f.buffs[i]:SetScript("OnUpdate", BuffOnUpdate) elseif f:GetName() == "pfTarget" and pfUI.expansion == "tbc" then f.buffs[i]:SetScript("OnUpdate", TargetBuffOnUpdate) end f.buffs[i]:SetScript("OnEnter", BuffOnEnter) f.buffs[i]:SetScript("OnLeave", BuffOnLeave) f.buffs[i]:SetScript("OnClick", BuffOnClick) end end if f.config.debuffs == "off" then for i=1, 32 do if f.debuffs and f.debuffs[i] then f.debuffs[i]:Hide() f.debuffs[i] = nil end end f.debuffs = nil else f.debuffs = f.debuffs or {} for i=1, 32 do if i > tonumber(f.config.debufflimit) then break end f.debuffs[i] = f.debuffs[i] or CreateFrame("Button", "pfUI" .. f.fname .. "Debuff" .. i, f) f.debuffs[i].texture = f.debuffs[i].texture or f.debuffs[i]:CreateTexture() f.debuffs[i].texture:SetTexCoord(.08, .92, .08, .92) f.debuffs[i].texture:SetAllPoints() f.debuffs[i].stacks = f.debuffs[i].stacks or f.debuffs[i]:CreateFontString(nil, "OVERLAY", f.debuffs[i]) f.debuffs[i].stacks:SetFont(pfUI.font_unit, C.global.font_unit_size, "OUTLINE") f.debuffs[i].stacks:SetPoint("BOTTOMRIGHT", f.debuffs[i], 2, -2) f.debuffs[i].stacks:SetJustifyH("LEFT") f.debuffs[i].stacks:SetShadowColor(0, 0, 0) f.debuffs[i].stacks:SetShadowOffset(0.8, -0.8) f.debuffs[i].stacks:SetTextColor(1,1,.5) f.debuffs[i].cd = f.debuffs[i].cd or CreateFrame(COOLDOWN_FRAME_TYPE, f.debuffs[i]:GetName() .. "Cooldown", f.debuffs[i], "CooldownFrameTemplate") f.debuffs[i].cd.pfCooldownType = "ALL" f.debuffs[i].cd.pfCooldownStyleText = cooldown_text f.debuffs[i].cd.pfCooldownStyleAnimation = cooldown_anim f.debuffs[i].id = i f.debuffs[i]:Hide() f.debuffs[i]:SetFrameLevel(12) CreateBackdrop(f.debuffs[i], default_border) f.debuffs[i]:RegisterForClicks("RightButtonUp") f.debuffs[i]:ClearAllPoints() f.debuffs[i]:SetWidth(f.config.debuffsize) f.debuffs[i]:SetHeight(f.config.debuffsize) f.debuffs[i]:SetNormalTexture(nil) if f:GetName() == "pfPlayer" then f.debuffs[i]:SetScript("OnUpdate", DebuffOnUpdate) end f.debuffs[i]:SetScript("OnEnter", DebuffOnEnter) f.debuffs[i]:SetScript("OnLeave", DebuffOnLeave) f.debuffs[i]:SetScript("OnClick", DebuffOnClick) end end if f.config.visible == "1" then pfUI.uf:RefreshUnit(f, "all") f:EnableScripts() f:EnableEvents() f:UpdateFrameSize() else f:UnregisterAllEvents() f:Hide() end end function pfUI.uf.OnShow() pfUI.uf:RefreshUnit(this, "portrait") pfUI.uf:RefreshUnit(this, "base") end function pfUI.uf.OnEvent() -- update indicators if event == "PARTY_LEADER_CHANGED" or event == "PARTY_LOOT_METHOD_CHANGED" or event == "PARTY_MEMBERS_CHANGED" or event == "RAID_TARGET_UPDATE" or event == "RAID_ROSTER_UPDATE" or event == "PLAYER_UPDATE_RESTING" then this.update_indicators = true end -- abort on broken unitframes (e.g focus) if not this.label then return end -- update regular frames if event == "PLAYER_ENTERING_WORLD" then this.update_full = true elseif this.label == "target" and event == "PLAYER_TARGET_CHANGED" and not pfScanActive == true then this.update_full = true elseif ( this.label == "raid" or this.label == "party" or this.label == "player" ) and event == "PARTY_MEMBERS_CHANGED" then this.update_full = true elseif ( this.label == "raid" or this.label == "party" ) and event == "PARTY_MEMBER_ENABLE" then this.update_full = true elseif ( this.label == "raid" or this.label == "party" ) and event == "PARTY_MEMBER_DISABLE" then this.update_full = true elseif ( this.label == "raid" or this.label == "party" ) and event == "RAID_ROSTER_UPDATE" then this.update_full = true elseif this.label == "pet" and event == "UNIT_PET" then this.update_full = true elseif this.label == "player" and (event == "PLAYER_AURAS_CHANGED" or event == "UNIT_INVENTORY_CHANGED") then this.update_aura = true elseif this.label == "pet" and event == "UNIT_HAPPINESS" then this.update_full = true -- UNIT_XXX Events elseif arg1 and arg1 == this.label .. this.id then if event == "UNIT_PORTRAIT_UPDATE" or event == "UNIT_MODEL_CHANGED" then this.update_portrait = true elseif event == "UNIT_AURA" then this.update_aura = true elseif event == "UNIT_FACTION" then this.update_pvp = true elseif event == "UNIT_COMBAT" then CombatFeedback_OnCombatEvent(arg2, arg3, arg4, arg5) else this.update_full = true end end end function pfUI.uf.OnUpdate() -- update combat feedback if this.feedbackText then CombatFeedback_OnUpdate(arg1) end -- process indicator update events if this.update_indicators then pfUI.uf:RefreshIndicators(this) this.update_indicators = nil end -- process all queued unit events if this.update_full then -- process full updates pfUI.uf:RefreshUnit(this, "all") -- clear update caches this.update_full = nil this.update_base = nil this.update_aura = nil this.update_portrait = nil this.update_pvp = nil else -- process individual events if this.update_aura then pfUI.uf:RefreshUnit(this, "aura") this.update_aura = nil this.update_base = true end if this.update_portrait then pfUI.uf:RefreshUnit(this, "portrait") this.update_portrait = nil this.update_base = true end if this.update_pvp then pfUI.uf:RefreshUnit(this, "pvp") this.update_pvp = nil this.update_base = true end if this.update_base then pfUI.uf:RefreshUnit(this, "base") this.update_base = nil end end -- handle pseudo focus frames if this.unitname and this == pfFocus then local unitname = ( this.label and UnitName(this.label) ) or "" if pfFocusTarget then -- update focus target pfFocusTarget.label = this.label and this.label .. "target" or nil local focustargetname = pfFocusTarget.label and UnitName(pfFocusTarget.label) or nil if pfFocusTarget.lastUnit ~= focustargetname then pfFocusTarget.lastUnit = focustargetname pfFocusTarget.instantRefresh = true pfUI.uf:RefreshUnit(pfFocusTarget, "all") end end -- break here on unset focus frames if not this.unitname or this.unitname == "focus" then return end -- focus unit detection if this.unitname ~= strlower(unitname) then -- invalid focus frame for unit, bool in pairs(pfValidUnits) do local scan = UnitName(unit) or "" if this.unitname == strlower(scan) then this.label = unit if this.portrait then this.portrait.model.lastUnit = nil end this.instantRefresh = true pfUI.uf:RefreshUnit(this, "all") return end this.label = nil this.instantRefresh = true this.hp.bar:SetStatusBarColor(.2,.2,.2) end end end -- handle pseudo focus target visibility if this.unitname and this == pfFocusTarget then if pfFocus and not pfFocus.label or pfFocus.label == "" then this.label = nil return end end if not this.label then return end -- update portrait on first visible frame if this.portrait and this.portrait.model and this.portrait.model.update then this.portrait.model.lastUnit = UnitName(this.portrait.model.update) this.portrait.model:SetUnit(this.portrait.model.update) this.portrait.model:SetCamera(0) this.portrait.model.update = nil end -- get incoming heals and resurections if libpredict then local unit = this.label .. this.id local heal = libpredict:UnitGetIncomingHeals(unit) local ress = libpredict:UnitHasIncomingResurrection(unit) local health, maxHealth = UnitHealth(unit), UnitHealthMax(unit) if heal - health - maxHealth ~= this.predictstate then local overhealperc = tonumber(this.config.overhealperc) this.predictstate = heal - health - maxHealth if heal > 0 and (health < maxHealth or overhealperc > 0 ) then local width = this.config.width local height = this.config.height if this.config.verticalbar == "0" then local healthWidth = width * (health / maxHealth) local incWidth = width * heal / maxHealth if healthWidth + incWidth > width * (1+(overhealperc/100)) then incWidth = width * (1+overhealperc/100) - healthWidth end if this.config.invert_healthbar == "1" then this.incHeal:SetWidth(incWidth) else this.incHeal:SetWidth(incWidth + healthWidth) end else local healthHeight = height * (health / maxHealth) local incHeight = height * heal / maxHealth if healthHeight + incHeight > height * (1+(overhealperc/100)) then incHeight = height * (1+overhealperc/100) - healthHeight end if this.config.invert_healthbar == "1" then this.incHeal:SetHeight(incHeight) else this.incHeal:SetHeight(incHeight + healthHeight) end end this.incHeal:Show() else this.incHeal:Hide() end end -- update ressurections if ress and UnitIsDeadOrGhost(unit) then this.ressIcon:Show() else this.ressIcon:Hide() end end -- trigger eventless actions (online/offline/range) if not this.lastTick then this.lastTick = GetTime() + (this.tick or .2) end if this.lastTick and this.lastTick < GetTime() then local unitstr = this.label .. this.id this.lastTick = GetTime() + (this.tick or .2) -- target target has a huge delay, make sure to not tick during range checks -- by waiting for a stable name over three ticks otherwise aborting the update. if this.label == "targettarget" or this.label == "targettargettarget" then local name = UnitName(this.label) if name ~= this.namebuf1 then this.namebuf1 = name return elseif name ~= this.namebuf2 then this.namebuf2 = name return end end pfUI.uf:RefreshUnitState(this) pfUI.uf:RefreshIndicators(this) if this.config.glowaggro == "1" and pfUI.api.UnitHasAggro(this.label .. this.id) > 0 then this.glow:SetBackdropBorderColor(1,.2,0) this.glow:Show() elseif this.config.glowcombat == "1" and UnitAffectingCombat(this.label .. this.id) then this.glow:SetBackdropBorderColor(1,1,.2) this.glow:Show() else this.glow:Hide() end if this.config.squareaggro == "1" and pfUI.api.UnitHasAggro(this.label .. this.id) > 0 then this.combat.tex:SetTexture(1,.2,0) this.combat:Show() elseif this.config.squarecombat == "1" and UnitAffectingCombat(this.label .. this.id) then this.combat.tex:SetTexture(1,1,.2) this.combat:Show() else this.combat:Hide() end -- update everything on eventless frames (targettarget, etc) if this.tick then pfUI.uf:RefreshUnit(this, "all") end end end function pfUI.uf.OnEnter() if not this.label then return end if this.config.showtooltip == "0" then return end GameTooltip_SetDefaultAnchor(GameTooltip, this) GameTooltip:SetUnit(this.label .. this.id) GameTooltip:Show() end function pfUI.uf.OnLeave() GameTooltip:FadeOut() end function pfUI.uf.OnClick() if not this.label and this.unitname then TargetByName(this.unitname, true) else pfUI.uf:ClickAction(arg1) end end function pfUI.uf:RightClickAction(unit) if unit == "player" then ToggleDropDownMenu(1, nil, PlayerFrameDropDown, "cursor") elseif unit == "target" then ToggleDropDownMenu(1, nil, TargetFrameDropDown, "cursor") elseif unit == "pet" then ToggleDropDownMenu(1, nil, PetFrameDropDown, "cursor") elseif unit == "party" or strfind(unit, "party%d") then ToggleDropDownMenu(1, nil, getglobal("PartyMemberFrame" .. this.id .. "DropDown"), "cursor") elseif unit == "raid" or strfind(unit, "raid%d") then local name = this.lastUnit local unitstr = this.label .. this.id FriendsDropDown.displayMode = "MENU" FriendsDropDown.initialize = function() UnitPopup_ShowMenu(_G[UIDROPDOWNMENU_OPEN_MENU], "PARTY", unitstr, name, id) end ToggleDropDownMenu(1, nil, FriendsDropDown, "cursor") end end function pfUI.uf:EnableEvents() local f = self f:RegisterEvent("PLAYER_ENTERING_WORLD") f:RegisterEvent("UNIT_DISPLAYPOWER") f:RegisterEvent("UNIT_HEALTH") f:RegisterEvent("UNIT_MAXHEALTH") f:RegisterEvent("UNIT_MANA") f:RegisterEvent("UNIT_MAXMANA") f:RegisterEvent("UNIT_RAGE") f:RegisterEvent("UNIT_MAXRAGE") f:RegisterEvent("UNIT_ENERGY") f:RegisterEvent("UNIT_MAXENERGY") f:RegisterEvent("UNIT_FOCUS") f:RegisterEvent("UNIT_PORTRAIT_UPDATE") f:RegisterEvent("UNIT_MODEL_CHANGED") f:RegisterEvent("UNIT_FACTION") f:RegisterEvent("UNIT_AURA") -- frame=buff, frame=debuff f:RegisterEvent("PLAYER_AURAS_CHANGED") -- label=player && frame=buff f:RegisterEvent("UNIT_INVENTORY_CHANGED") -- label=player && frame=buff f:RegisterEvent("PARTY_MEMBERS_CHANGED") -- label=party, frame=leaderIcon f:RegisterEvent("PARTY_LEADER_CHANGED") -- frame=leaderIcon f:RegisterEvent("RAID_ROSTER_UPDATE") -- label=raidIcon f:RegisterEvent("PLAYER_UPDATE_RESTING") -- label=restIcon f:RegisterEvent("PLAYER_TARGET_CHANGED") -- label=target f:RegisterEvent("PARTY_LOOT_METHOD_CHANGED") -- frame=lootIcon f:RegisterEvent("RAID_TARGET_UPDATE") -- frame=raidIcon f:RegisterEvent("UNIT_PET") f:RegisterEvent("UNIT_HAPPINESS") f:RegisterForClicks('LeftButtonUp', 'RightButtonUp', 'MiddleButtonUp', 'Button4Up', 'Button5Up') end function pfUI.uf:EnableScripts() local f = self -- handle secure unit button templates (> vanilla) if pfUI.client > 11200 then f.showmenu = pfUI.uf.RightClickAction f:SetAttribute("*type1", "target") f:SetAttribute("*type2", "showmenu") else f:SetScript("OnClick", pfUI.uf.OnClick) end f:SetScript("OnShow", pfUI.uf.OnShow) f:SetScript("OnEvent", pfUI.uf.OnEvent) f:SetScript("OnUpdate", pfUI.uf.OnUpdate) f:SetScript("OnEnter", pfUI.uf.OnEnter) f:SetScript("OnLeave", pfUI.uf.OnLeave) f:EnableClickCast() -- add frame to visibility refresh handler visibilityscan.frames[f] = true end function pfUI.uf:CreateUnitFrame(unit, id, config, tick) local fname = (( unit == "Party" ) and "Group" or (unit or "")) .. (id or "") local unit = strlower(unit or "") local id = strlower(id or "") -- fake party0 units as self if unit == "party" and id == "0" then unit, id = "player", "" end if unit == "partypet" and id == "0" then unit, id = "pet", "" end if unit == "pettarget" and id == "0" then unit, id = "pettarget", "" end if unit == "party0target" then unit, id = "target", "" end local f = CreateFrame("Button", "pf" .. fname, UIParent, UNITFRAME_SECURE_TEMPLATE) -- add unitframe functions f.UpdateFrameSize = pfUI.uf.UpdateFrameSize f.UpdateVisibility = pfUI.uf.UpdateVisibility f.UpdateConfig = pfUI.uf.UpdateConfig f.EnableScripts = pfUI.uf.EnableScripts f.EnableEvents = pfUI.uf.EnableEvents f.EnableClickCast = pfUI.uf.EnableClickCast f.GetColor = pfUI.uf.GetColor -- cache values to the frame f.label = unit f.fname = fname f.id = id f.config = config or pfUI_config.unitframes.fallback f.tick = tick -- disable events for unknown unitstrings if not pfValidUnits[unit .. id] then f.unitname = unit f.label, f.id = "", "" f.RegisterEvent = function() return end end CreateBackdropShadow(f) f.hp = CreateFrame("Frame",nil, f) f.hp.bar = CreateStatusBar(nil, f.hp) f.power = CreateFrame("Frame",nil, f) f.power.bar = CreateStatusBar(nil, f.power) f.glow = CreateFrame("Frame", nil, f) f.combat = CreateFrame("Frame", nil, f.hp.bar) f.combat.tex = f.combat:CreateTexture(nil, "OVERLAY") f.combat.tex:SetAllPoints() f.texts = CreateFrame("Frame", nil, f) f.texts:SetFrameLevel(16) f.texts:SetAllPoints() f.hpLeftText = f.texts:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall") f.hpRightText = f.texts:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall") f.hpCenterText = f.texts:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall") f.powerLeftText = f.texts:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall") f.powerRightText = f.texts:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall") f.powerCenterText = f.texts:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall") f.incHeal = CreateFrame("Frame", nil, f.hp) f.incHeal.texture = f.incHeal:CreateTexture(nil, "BACKGROUND") f.incHeal.texture:SetAllPoints() f.ressIcon = CreateFrame("Frame", nil, f.hp.bar) f.ressIcon.texture = f.ressIcon:CreateTexture(nil,"BACKGROUND") f.leaderIcon = CreateFrame("Frame", nil, f.hp.bar) f.leaderIcon.texture = f.leaderIcon:CreateTexture(nil,"BACKGROUND") f.lootIcon = CreateFrame("Frame",nil, f.hp.bar) f.lootIcon.texture = f.lootIcon:CreateTexture(nil,"BACKGROUND") f.pvpIcon = CreateFrame("Frame", nil, f.hp.bar) f.pvpIcon.texture = f.pvpIcon:CreateTexture(nil,"BACKGROUND") f.raidIcon = CreateFrame("Frame", nil, f.hp.bar) f.raidIcon.texture = f.raidIcon:CreateTexture(nil,"ARTWORK") f.restIcon = CreateFrame("Frame", nil, f.hp.bar) f.restIcon.texture = f.restIcon:CreateTexture(nil, "BACKGROUND") f.happinessIcon = CreateFrame("Frame", nil, f.hp.bar) f.happinessIcon.texture = f.happinessIcon:CreateTexture(nil, "BACKGROUND") f.portrait = CreateFrame("Frame", "pfPortrait" .. f.label .. f.id, f) f.portrait.tex = f.portrait:CreateTexture("pfPortraitTexture" .. f.label .. f.id, "OVERLAY") f.portrait.model = CreateFrame("PlayerModel", "pfPortraitModel" .. f.label .. f.id, f.portrait) f.portrait.model.next = CreateFrame("PlayerModel", nil, nil) f.feedbackText = f:CreateFontString("pfHitIndicator" .. f.label .. f.id, "OVERLAY", "NumberFontNormalHuge") if f.label == "raid" and mod(f.id, 5) == 1 then local group = math.ceil(f.id/5) f.group = f.group or f.hp.bar:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall") f.group:SetFont(pfUI.font_unit, 8, "OUTLINE") f.group:SetTextColor(1,1,1,.8) f.group:SetHeight(16) f.group:SetText("Group " .. group) f.group:Hide() end f:Hide() f:UpdateConfig() f:UpdateFrameSize() f:EnableScripts() f:EnableEvents() if f.config.visible == "1" then pfUI.uf:RefreshUnit(f, "all") f:EnableScripts() f:EnableEvents() f:UpdateFrameSize() else f:UnregisterAllEvents() f:Hide() end -- register frame for clique _G.ClickCastFrames = ClickCastFrames or {} ClickCastFrames[f] = true table.insert(pfUI.uf.frames, f) return f end function pfUI.uf:RefreshUnitState(unit) local alpha = unit.alpha_visible local unlock = pfUI.unlock and pfUI.unlock:IsShown() or nil if not UnitIsConnected(unit.label .. unit.id) and not unlock then -- offline alpha = unit.alpha_offline unit.hp.bar:SetMinMaxValues(0, 100, true) unit.power.bar:SetMinMaxValues(0, 100, true) unit.hp.bar:SetValue(0) unit.power.bar:SetValue(0) elseif unit.config.faderange == "1" and not pfUI.api.UnitInRange(unit.label .. unit.id, 4) and not unlock then alpha = unit.alpha_outrange end -- skip if alpha is already correct if floor(unit:GetAlpha()*10+.5) == floor(alpha*10+.5) then return end -- set unitframe alpha unit:SetAlpha(alpha) -- refresh portrait alpha if unit.config.portrait == "bar" then unit.portrait:SetAlpha(pfUI_config.unitframes.portraitalpha) end -- refresh debuff indicator alpha local disptype = unit.config.debuff_indicator local indicator = unit.hp.bar.debuffindicators if indicator then indicator:SetAlpha(0) if ( disptype == "4" or disptype == "3" ) then indicator:SetAlpha(1) elseif disptype == "2" then indicator:SetAlpha(.4) elseif disptype == "1" then indicator:SetAlpha(.2) end end end function pfUI.uf:RefreshIndicators(unit) if not unit.label or not unit.id then return end local unitstr = unit.label .. unit.id if unit.leaderIcon then -- Leader Icon if unit.config.leadericon == "1" and UnitIsPartyLeader(unitstr) and ( GetNumPartyMembers() > 0 or GetNumRaidMembers() > 0 ) then unit.leaderIcon:Show() else unit.leaderIcon:Hide() end end if unit.lootIcon then -- Loot Icon if unit.config.looticon == "0" then unit.lootIcon:Hide() else -- no third return value here.. but leaving this as a hint local method, group, raid = GetLootMethod() local name = group and UnitName(group == 0 and "player" or "party"..group) or raid and UnitName("raid"..raid) or nil if name and name == UnitName(unitstr) then unit.lootIcon:Show() else unit.lootIcon:Hide() end end end if unit.pvpIcon then -- PvP Icon if unit.config.showPVP == "1" and UnitIsPVP(unitstr) then unit.pvpIcon:Show() else unit.pvpIcon:Hide() end end if unit.restIcon and unit:GetName() == "pfPlayer" then -- Rest Icon if C.unitframes.player.showRest == "1" and UnitIsUnit(unitstr, "player") and IsResting() then unit.restIcon:Show() else unit.restIcon:Hide() end end if unit.happinessIcon and unit:GetName() == "pfPet" then -- Happiness Icon local _, pclass = UnitClass("player") if unit.config.happinessicon == "0" or pclass ~= "HUNTER" then unit.happinessIcon:Hide() else if UnitIsVisible("pet") then local happiness = GetPetHappiness() if happiness == 1 then unit.happinessIcon.texture:SetTexture(pfUI.media["img:sad"..unit.config.happinessicon]) unit.happinessIcon.texture:SetVertexColor(1, 0, 0, 1) elseif happiness == 2 then unit.happinessIcon.texture:SetTexture(pfUI.media["img:neutral"..unit.config.happinessicon]) unit.happinessIcon.texture:SetVertexColor(1, 1, 0, 1) else unit.happinessIcon.texture:SetTexture(pfUI.media["img:happy"..unit.config.happinessicon]) unit.happinessIcon.texture:SetVertexColor(0, 1, 0, 1) end unit.happinessIcon:Show() else unit.happinessIcon:Hide() end end end if unit.raidIcon then -- Raid Icon local raidIcon = UnitName(unitstr) and GetRaidTargetIndex(unitstr) if unit.config.raidicon == "1" and raidIcon then SetRaidTargetIconTexture(unit.raidIcon.texture, raidIcon) unit.raidIcon:Show() else unit.raidIcon:Hide() end end end local pfDebuffColors = { ["Magic"] = { 0.1, 0.7, 0.8, 1 }, ["Poison"] = { 0.2, 0.7, 0.3, 1 }, ["Curse"] = { 0.6, 0.2, 0.6, 1 }, ["Disease"] = { 0.9, 0.7, 0.2, 1 } } function pfUI.uf:RefreshUnit(unit, component) -- break early on misconfigured UF's if not unit.label then return end if not unit.hp then return end if not unit.power then return end if not unit.id then unit.id = "" end local component = component or "" -- don't update scanner activity if unit.label == "target" or unit.label == "targettarget" or unit.label == "targettargettarget" then if pfScanActive == true then return end end -- hide unused and invalid frames unit:UpdateVisibility() -- return on invisible unit frames if not unit:IsShown() and not unit.visible then return end -- create required fields local unitstr = unit.label..unit.id local rawborder, default_border = GetBorderSize("unitframes") -- save current values unit.namecache = UnitName(unitstr) -- buffs if unit.buffs and ( component == "all" or component == "aura" ) then local texture, stacks for i=1, unit.config.bufflimit do if not unit.buffs[i] then break end if unit.label == "player" then stacks = GetPlayerBuffApplications(GetPlayerBuff(PLAYER_BUFF_START_ID+i,"HELPFUL")) texture = GetPlayerBuffTexture(GetPlayerBuff(PLAYER_BUFF_START_ID+i,"HELPFUL")) else texture, stacks = pfUI.uf:DetectBuff(unitstr, i) end unit.buffs[i].texture:SetTexture(texture) if texture then unit.buffs[i]:Show() if stacks > 1 then unit.buffs[i].stacks:SetText(stacks) else unit.buffs[i].stacks:SetText("") end else unit.buffs[i]:Hide() end end end -- debuffs if unit.debuffs and ( component == "all" or component == "aura" ) then local texture, stacks, dtype local perrow = unit.config.debuffperrow local bperrow = unit.config.buffperrow local selfdebuff = unit.config.selfdebuff local invert_h, invert_v, af if unit.config.debuffs == "TOPLEFT" then invert_h = 1 invert_v = 1 af = "BOTTOMLEFT" elseif unit.config.debuffs == "BOTTOMLEFT" then invert_h = -1 invert_v = 1 af = "TOPLEFT" elseif unit.config.debuffs == "TOPRIGHT" then invert_h = 1 invert_v = -1 af = "BOTTOMRIGHT" elseif unit.config.debuffs == "BOTTOMRIGHT" then invert_h = -1 invert_v = -1 af = "TOPRIGHT" end local buffrow, reposition = 0, ( component == "all" and true or nil ) if unit.config.buffs == unit.config.debuffs then if unit.buffs[0*bperrow+1] and unit.buffs[0*bperrow+1]:IsShown() then buffrow = buffrow + 1 end if unit.buffs[1*bperrow+1] and unit.buffs[1*bperrow+1]:IsShown() then buffrow = buffrow + 1 end if unit.buffs[2*bperrow+1] and unit.buffs[2*bperrow+1]:IsShown() then buffrow = buffrow + 1 end if unit.buffs[3*bperrow+1] and unit.buffs[3*bperrow+1]:IsShown() then buffrow = buffrow + 1 end end if buffrow ~= unit.lastbuffrow then unit.lastbuffrow = buffrow reposition = true end for i=1, unit.config.debufflimit do if not unit.debuffs[i] then break end local row = floor((i-1) / unit.config.debuffperrow) if reposition then local anchor = unit.config.portraitheight ~= "-1" and unit.hp or unit if anchor == unit.hp and (unit.config.debuffs == "BOTTOMLEFT" or unit.config.debuffs == "BOTTOMRIGHT") then anchor = unit.power end local multiply = C.appearance.border.force_blizz == "1" and 1 or 2 unit.debuffs[i]:SetPoint(af, anchor, unit.config.debuffs, invert_v * (i-1-row*perrow)*(multiply*default_border + unit.config.debuffsize + 1), invert_h * ((row+buffrow)*(multiply*default_border + unit.config.debuffsize + 1) + (multiply*default_border + 1))) end if unit.label == "player" then texture = GetPlayerBuffTexture(GetPlayerBuff(PLAYER_BUFF_START_ID+i, "HARMFUL")) stacks = GetPlayerBuffApplications(GetPlayerBuff(PLAYER_BUFF_START_ID+i, "HARMFUL")) dtype = GetPlayerBuffDispelType(GetPlayerBuff(PLAYER_BUFF_START_ID+i, "HARMFUL")) elseif selfdebuff == "1" then _, _, texture, stacks, dtype = libdebuff:UnitOwnDebuff(unitstr, i) else texture, stacks, dtype = UnitDebuff(unitstr, i) end unit.debuffs[i].texture:SetTexture(texture) local r,g,b = DebuffTypeColor.none.r,DebuffTypeColor.none.g,DebuffTypeColor.none.b if dtype and DebuffTypeColor[dtype] then r,g,b = DebuffTypeColor[dtype].r,DebuffTypeColor[dtype].g,DebuffTypeColor[dtype].b end unit.debuffs[i].backdrop:SetBackdropBorderColor(r,g,b,1) if texture then unit.debuffs[i]:Show() if unit:GetName() == "pfPlayer" then local timeleft = GetPlayerBuffTimeLeft(GetPlayerBuff(PLAYER_BUFF_START_ID+unit.debuffs[i].id, "HARMFUL"),"HARMFUL") CooldownFrame_SetTimer(unit.debuffs[i].cd, GetTime(), timeleft, 1) elseif libdebuff and selfdebuff == "1" then local name, rank, texture, stacks, dtype, duration, timeleft, caster = libdebuff:UnitOwnDebuff(unitstr, i) if duration and timeleft then CooldownFrame_SetTimer(unit.debuffs[i].cd, GetTime() + timeleft - duration, duration, 1) end elseif libdebuff then local name, rank, texture, stacks, dtype, duration, timeleft, caster = libdebuff:UnitDebuff(unitstr, i) if duration and timeleft then CooldownFrame_SetTimer(unit.debuffs[i].cd, GetTime() + timeleft - duration, duration, 1) end end if stacks > 1 then unit.debuffs[i].stacks:SetText(stacks) else unit.debuffs[i].stacks:SetText("") end else unit.debuffs[i]:Hide() end end end -- indicators if component == "all" or component == "aura" then if not unit.dispellable and unit.config.debuff_indicator ~= "0" then unit.dispellable = pfUI.uf:SetupDebuffFilter((unit.config.debuff_ind_class == "0" and true or nil)) elseif not unit.dispellable then unit.dispellable = {} end if table.getn(unit.dispellable) > 0 then unit.hp.bar.debuffindicators = unit.hp.bar.debuffindicators or CreateFrame("Frame", nil, unit.hp.bar) -- 0 = OFF, 1 = Legacy, 2 = Glow, 3 = Square, 4 = Icons local disptype = unit.config.debuff_indicator local indicator = unit.hp.bar.debuffindicators local indipos = unit.config.debuff_ind_pos local count = 0 local size if disptype == "4" or disptype == "3" then size = unit.hp.bar:GetHeight() * tonumber(unit.config.debuff_ind_size) if size ~= indicator.size or disptype ~= indicator.disp or indipos ~= indicator.ipos then indicator:ClearAllPoints() indicator:SetPoint(indipos, 0, 0) indicator:SetHeight(size) indicator:SetWidth(size) indicator.size = size indicator.disp = disptype indicator.ipos = indipos end elseif disptype == "2" or disptype == "1" then size = "FULL" if size ~= indicator.size or disptype ~= indicator.disp or indipos ~= indicator.ipos then indicator:ClearAllPoints() indicator:SetAllPoints(unit.hp.bar) indicator.size = size indicator.disp = disptype indicator.ipos = indipos end end for _, debuff in pairs(unit.dispellable) do indicator[debuff] = indicator[debuff] or CreateFrame("Frame", nil, indicator) indicator[debuff]:SetParent(indicator) indicator[debuff].tex = indicator[debuff].tex or indicator[debuff]:CreateTexture(nil) indicator[debuff].tex:SetAllPoints(indicator[debuff]) if indicator.size ~= indicator[debuff].size or disptype ~= indicator[debuff].disp then if disptype == "4" then indicator[debuff].tex:SetTexture(pfUI.media["img:"..debuff]) indicator[debuff].tex:SetVertexColor(unpack(pfDebuffColors[debuff])) indicator[debuff].tex:Show() indicator[debuff]:ClearAllPoints() indicator[debuff]:SetHeight(size) indicator[debuff]:SetWidth(size) indicator[debuff]:SetBackdrop(nil) elseif disptype == "3" then indicator[debuff].tex:SetTexture(unpack(pfDebuffColors[debuff])) indicator[debuff].tex:SetVertexColor(1,1,1,1) indicator[debuff].tex:Show() indicator[debuff]:ClearAllPoints() indicator[debuff]:SetHeight(size) indicator[debuff]:SetWidth(size) indicator[debuff]:SetBackdrop(nil) elseif disptype == "2" then indicator[debuff].tex:Hide() indicator[debuff]:SetAllPoints(unit.hp.bar) indicator[debuff]:SetBackdrop(glow) indicator[debuff]:SetBackdropBorderColor(unpack(pfDebuffColors[debuff])) elseif disptype == "1" then indicator[debuff].tex:SetTexture(unpack(pfDebuffColors[debuff])) indicator[debuff].tex:SetVertexColor(1,1,1,1) indicator[debuff].tex:Show() indicator[debuff]:SetAllPoints(unit.hp.bar) indicator[debuff]:SetBackdrop(nil) end indicator[debuff].size = indicator.size indicator[debuff].disp = indicator.disp end indicator[debuff].visible = nil for i=1,16 do local _, _, dtype = UnitDebuff(unitstr, i) if dtype == debuff then indicator[debuff].visible = true end end if indicator[debuff].visible then indicator[debuff]:Show() indicator:Show() indicator:SetAlpha(0) if disptype == "4" or disptype == "3" then indicator:SetAlpha(1) elseif disptype == "2" then indicator:SetAlpha(.4) elseif disptype == "1" then indicator:SetAlpha(.2) end if disptype == "4" or disptype == "3" then indicator[debuff]:SetPoint("LEFT", indicator, "LEFT", count*(size+1), 0) count = count + 1 end else indicator[debuff]:Hide() end end if disptype == "4" or disptype == "3" then indicator:SetWidth(count*(size+1)) end elseif unit.hp.bar.debuffindicators then unit.hp.bar.debuffindicators:Hide() end if not unit.indicators and unit.config.buff_indicator == "1" then unit.indicators = pfUI.uf:SetupBuffIndicators(unit.config) elseif not unit.indicators then unit.indicators = {} end if not unit.indicator_custom and unit.config.buff_indicator == "1" then unit.indicator_custom = {} for k, v in pairs({strsplit("#", unit.config.custom_indicator)}) do unit.indicator_custom[k] = string.lower(v) end elseif not unit.indicator_custom then unit.indicator_custom = {} end local pos = 1 if table.getn(unit.indicators) > 0 then for i=1,32 do local texture, count = UnitBuff(unitstr, i) local timeleft, _ if pfUI.client > 11200 then _, _, texture, _, _, timeleft = _G.UnitBuff(unitstr, i) end if texture then -- match filter for _, filter in pairs(unit.indicators) do if filter == string.lower(texture) then if string.lower(texture) == "interface\\icons\\spell_nature_rejuvenation" then local start, duration, prediction = libpredict:GetHotDuration(unitstr, "Reju") pfUI.uf:AddIcon(unit, pos, texture, timeleft or prediction, count, tonumber(start), tonumber(duration)) pos = pos + 1 break elseif string.lower(texture) == "interface\\icons\\spell_holy_renew" then local start, duration, prediction = libpredict:GetHotDuration(unitstr, "Renew") pfUI.uf:AddIcon(unit, pos, texture, timeleft or prediction, count, tonumber(start), tonumber(duration)) pos = pos + 1 break elseif string.lower(texture) == "interface\\icons\\spell_nature_resistnature" then local start, duration, prediction = libpredict:GetHotDuration(unitstr, "Regr") pfUI.uf:AddIcon(unit, pos, texture, timeleft or prediction, count, tonumber(start), tonumber(duration)) pos = pos + 1 break else pfUI.uf:AddIcon(unit, pos, texture, timeleft, count) pos = pos + 1 break end end end end end end if table.getn(unit.indicator_custom) > 0 then scanner = scanner or libtipscan:GetScanner("unitframes") for i=1,32 do -- scan for custom buffs local texture, count = UnitBuff(unitstr, i) if texture then local timeleft, name, _ if pfUI.client > 11200 then name, _, texture, _, _, timeleft = _G.UnitBuff(unitstr, i) else scanner:SetUnitBuff(unitstr, i) name = scanner:Line(1) or "" end -- match filter for _, filter in pairs(unit.indicator_custom) do if filter == string.lower(name) then pfUI.uf:AddIcon(unit, pos, texture, timeleft, count) pos = pos + 1 break end end end end for i=1,32 do -- scan for custom debuffs local texture, count = UnitDebuff(unitstr, i) if texture then local timeleft, name, _ if libdebuff then name, _, texture, _, _, _, timeleft = libdebuff:UnitDebuff(unitstr, i) else scanner:SetUnitDebuff(unitstr, i) name = scanner:Line(1) or "" end -- match filter for _, filter in pairs(unit.indicator_custom) do if filter == string.lower(name) then pfUI.uf:AddIcon(unit, pos, texture, timeleft, count) pos = pos + 1 break end end end end end -- hide unused icon slots for pos=pos, 6 do pfUI.uf:HideIcon(unit, pos) end end -- portrait if unit.portrait and ( component == "all" or component == "portrait" ) then if C.unitframes.always2dportrait == "1" then unit.portrait.tex:Show() unit.portrait.model:Hide() SetPortraitTexture(unit.portrait.tex, unitstr) else if not UnitIsVisible(unitstr) or not UnitIsConnected(unitstr) then if unit.config.portrait == "bar" then unit.portrait.tex:Hide() unit.portrait.model:Hide() elseif C.unitframes.portraittexture == "1" then unit.portrait.tex:Show() unit.portrait.model:Hide() SetPortraitTexture(unit.portrait.tex, unitstr) else unit.portrait.tex:Hide() unit.portrait.model:Show() unit.portrait.model:SetModelScale(4.25) unit.portrait.model:SetPosition(0, 0, -1) unit.portrait.model:SetModel("Interface\\Buttons\\talktomequestionmark.mdx") end else if unit.config.portrait == "bar" then unit.portrait:SetAlpha(C.unitframes.portraitalpha) else unit.portrait:SetAlpha(1) end unit.portrait.tex:Hide() unit.portrait.model:Show() if component == "portrait" then -- regular portrait update after event unit.portrait.model.update = unitstr else -- detect portrait change without events unit.portrait.model.next:SetUnit(unitstr) if unit.portrait.model.lastUnit ~= UnitName(unitstr) or unit.portrait.model:GetModel() ~= unit.portrait.model.next:GetModel() then unit.portrait.model.update = unitstr end end end end end -- base frame if component == "all" or component == "base" then -- Unit HP/MP local hp, hpmax = UnitHealth(unitstr), UnitHealthMax(unitstr) local power, powermax = UnitMana(unitstr), UnitManaMax(unitstr) if unit.config.invert_healthbar == "1" then hp = hpmax - hp end unit.hp.bar:SetMinMaxValues(0, hpmax, true) unit.hp.bar:SetValue(hp) unit.power.bar:SetMinMaxValues(0, powermax, true) unit.power.bar:SetValue(power) -- set healthbar color local custom_active = nil local customfullhp = unit.config.defcolor == "0" and unit.config.customfullhp or C.unitframes.customfullhp local customcolor = unit.config.defcolor == "0" and unit.config.customcolor or C.unitframes.customcolor local customfade = unit.config.defcolor == "0" and unit.config.customfade or C.unitframes.customfade local custom = unit.config.defcolor == "0" and unit.config.custom or C.unitframes.custom local r, g, b, a = .2, .2, .2, 1 if customfullhp == "1" and UnitHealth(unitstr) == UnitHealthMax(unitstr) then r, g, b, a = GetStringColor(customcolor) custom_active = true elseif custom == "0" then if UnitIsPlayer(unitstr) then local _, class = UnitClass(unitstr) local color = RAID_CLASS_COLORS[class] if color then r, g, b = color.r, color.g, color.b end elseif unit.label == "pet" then local happiness = GetPetHappiness() if happiness == 1 then r, g, b = 1, 0, 0 elseif happiness == 2 then r, g, b = 1, 1, 0 else r, g, b = 0, 1, 0 end else local color = UnitReactionColor[UnitReaction(unitstr, "player")] if color then r, g, b = color.r, color.g, color.b end end elseif custom == "1" then r, g, b, a = GetStringColor(customcolor) custom_active = true elseif custom == "2" then if UnitHealthMax(unitstr) > 0 then r, g, b = GetColorGradient(UnitHealth(unitstr) / UnitHealthMax(unitstr)) else r, g, b = 0, 0, 0 end end if C.unitframes.pastel == "1" and not custom_active then r, g, b = (r + .5) * .5, (g + .5) * .5, (b + .5) * .5 end if customfade == "1" then -- fade custom color into default color local perc = UnitHealth(unitstr) / UnitHealthMax(unitstr) local cr, cg, cb, ca = GetStringColor(customcolor) r = (cr*perc) + (r*(1-perc)) g = (cg*perc) + (g*(1-perc)) b = (cb*perc) + (b*(1-perc)) end unit.hp.bar:SetStatusBarColor(r, g, b, a) -- set powerbar color local mana = unit.config.defcolor == "0" and unit.config.manacolor or C.unitframes.manacolor local rage = unit.config.defcolor == "0" and unit.config.ragecolor or C.unitframes.ragecolor local energy = unit.config.defcolor == "0" and unit.config.energycolor or C.unitframes.energycolor local focus = unit.config.defcolor == "0" and unit.config.focuscolor or C.unitframes.focuscolor local r, g, b, a = .5, .5, .5, 1 local utype = UnitPowerType(unitstr) if utype == 0 then r, g, b, a = GetStringColor(mana) elseif utype == 1 then r, g, b, a = GetStringColor(rage) elseif utype == 2 then r, g, b, a = GetStringColor(focus) elseif utype == 3 then r, g, b, a = GetStringColor(energy) end unit.power.bar:SetStatusBarColor(r, g, b, a) if UnitName(unitstr) then unit.hpLeftText:SetText(pfUI.uf:GetStatusValue(unit, "hpleft")) unit.hpCenterText:SetText(pfUI.uf:GetStatusValue(unit, "hpcenter")) unit.hpRightText:SetText(pfUI.uf:GetStatusValue(unit, "hpright")) unit.powerLeftText:SetText(pfUI.uf:GetStatusValue(unit, "powerleft")) unit.powerCenterText:SetText(pfUI.uf:GetStatusValue(unit, "powercenter")) unit.powerRightText:SetText(pfUI.uf:GetStatusValue(unit, "powerright")) if UnitIsTapped(unitstr) and not UnitIsTappedByPlayer(unitstr) then unit.hp.bar:SetStatusBarColor(.5,.5,.5,.5) end end pfUI.uf:RefreshUnitState(unit) end end local buttons = { [1] = "LeftButton", [2] = "RightButton", [3] = "MiddleButton", [4] = "Button4", [5] = "Button5", } local modifiers = { [""] = "", ["alt"] = "_alt", ["ctrl"] = "_ctrl", ["shift"] = "_shift", } function pfUI.uf:EnableClickCast() if self.config.clickcast ~= "1" then return end for bid, button in pairs(buttons) do for modifier, mconf in pairs(modifiers) do local bconf = bid == 1 and "" or bid if pfUI_config.unitframes["clickcast"..bconf..mconf] ~= "" then -- prepare click casting if pfUI.client > 11200 then -- set attributes for tbc+ local prefix = modifier == "" and "" or modifier .. "-" -- check for "/" in the beginning of the string, to detect macros if string.find(pfUI_config.unitframes["clickcast"..bconf..mconf], "^%/(.+)") then self:SetAttribute(prefix.."type"..bid, "macro") self:SetAttribute(prefix.."macrotext"..bid, pfUI_config.unitframes["clickcast"..bconf..mconf]) self:SetAttribute(prefix.."spell"..bid, nil) elseif string.find(pfUI_config.unitframes["clickcast"..bconf..mconf], "^target") then self:SetAttribute(prefix.."type"..bid, "target") self:SetAttribute(prefix.."macrotext"..bid, nil) self:SetAttribute(prefix.."spell"..bid, nil) elseif string.find(pfUI_config.unitframes["clickcast"..bconf..mconf], "^menu") then self:SetAttribute(prefix.."type"..bid, "showmenu") self:SetAttribute(prefix.."macrotext"..bid, nil) self:SetAttribute(prefix.."spell"..bid, nil) else self:SetAttribute(prefix.."type"..bid, "spell") self:SetAttribute(prefix.."spell"..bid, pfUI_config.unitframes["clickcast"..bconf..mconf]) self:SetAttribute(prefix.."macro"..bid, nil) end else -- fill clickaction table for vanillla self.clickactions = self.clickactions or {} self.clickactions[modifier..button] = pfUI_config.unitframes["clickcast"..bconf..mconf] end end end end end function pfUI.uf:ClickAction(button) local label = this.label or "" local id = this.id or "" local unitstr = label .. id local showmenu = button == "RightButton" and true or nil if SpellIsTargeting() and button == "RightButton" then SpellStopTargeting() return end if SpellIsTargeting() and button == "LeftButton" then SpellTargetUnit(unitstr) elseif CursorHasItem() then DropItemOnUnit(unitstr) end -- run click casting if enabled local modstring = "" modstring = IsAltKeyDown() and modstring.."alt" or modstring modstring = IsControlKeyDown() and modstring.."ctrl" or modstring modstring = IsShiftKeyDown() and modstring.."shift" or modstring modstring = modstring..button if this.clickactions and this.clickactions[modstring] then if string.find(this.clickactions[modstring], "^menu") then -- show menu showmenu = true elseif string.find(this.clickactions[modstring], "^target") then -- target unit showmenu = nil else -- run click cast action local is_macro = string.find(this.clickactions[modstring], "^%/(.+)") if superwow_active and not is_macro then CastSpellByName(this.clickactions[modstring], unitstr) else local tswitch = UnitIsUnit(unitstr, "target") TargetUnit(unitstr) if is_macro then RunMacroText(this.clickactions[modstring]) else CastSpellByName(this.clickactions[modstring]) end if not tswitch then TargetLastTarget() end end return end end -- dropdown menus if showmenu then pfUI.uf:RightClickAction(label) return end -- drop food on petframe if label == "pet" and CursorHasItem() then local _, playerClass = UnitClass("player") if playerClass == "HUNTER" then DropItemOnUnit("pet") return end end -- default click TargetUnit(unitstr) end function pfUI.uf:AddIcon(frame, pos, icon, timeleft, stacks, start, duration) local showtime = frame.config.indicator_time == "1" and true or nil local showstacks = frame.config.indicator_stacks == "1" and true or nil local position = frame.config.indicator_pos or "TOPLEFT" local iconsize = tonumber(frame.config.indicator_size) local spacing = tonumber(frame.config.indicator_spacing) if not frame.hp then return end local frame = frame.hp.bar if pos > 6 or pos > ceil(frame:GetWidth() / iconsize) then return end frame.icon = frame.icon or CreateFrame("Frame", nil, frame) if not frame.icon[pos] then frame.icon[pos] = CreateFrame("Frame", nil, frame.icon) frame.icon[pos]:SetParent(frame) frame.icon[pos].tex = frame.icon[pos]:CreateTexture("OVERLAY") frame.icon[pos].tex:SetAllPoints(frame.icon[pos]) frame.icon[pos].tex:SetTexCoord(.08, .92, .08, .92) frame.icon[pos].stacks = frame.icon[pos]:CreateFontString(nil, "OVERLAY") frame.icon[pos].stacks:SetPoint("BOTTOMRIGHT", 0, 0) frame.icon[pos].stacks:SetJustifyH("RIGHT") frame.icon[pos].stacks:SetJustifyV("BOTTOM") frame.icon[pos].cd = CreateFrame(COOLDOWN_FRAME_TYPE, nil, frame.icon[pos]) frame.icon[pos].cd.pfCooldownStyleAnimation = 0 frame.icon[pos].cd.pfCooldownType = "ALL" frame.icon[pos].cd:SetFrameLevel(48) end -- update icon configuration if frame.icon[pos].iconsize ~= iconsize or frame.icon[pos].spacing ~= spacing then frame.icon[pos]:SetWidth(iconsize) frame.icon[pos]:SetHeight(iconsize) frame.icon[pos]:SetPoint("TOPLEFT", frame.icon, "TOPLEFT", (pos-1)*(iconsize + spacing), 0) frame.icon[pos].stacks:SetFont(pfUI.font_unit, math.max(iconsize/3, 10), "OUTLINE") frame.icon[pos].iconsize = iconsize frame.icon[pos].spacing = spacing end -- update icon if frame.icon[pos].icon ~= icon then frame.icon[pos].tex:SetTexture(icon) frame.icon[pos].icon = icon end -- show remaining time if config is set if showtime and start and duration and timeleft < 100 and iconsize > 9 then CooldownFrame_SetTimer(frame.icon[pos].cd, start, duration, 1) elseif showtime and timeleft and timeleft < 100 and iconsize > 9 then CooldownFrame_SetTimer(frame.icon[pos].cd, GetTime(), timeleft, 1) else CooldownFrame_SetTimer(frame.icon[pos].cd, GetTime(), 0, 1) end -- show stacks if config is set if showstacks and stacks and stacks > 1 and iconsize > 9 then frame.icon[pos].stacks:SetText(stacks) else frame.icon[pos].stacks:SetText("") end -- update parent icon size if frame.icon.iconsize ~= iconsize then frame.icon:SetHeight(iconsize) frame.icon.iconsize = iconsize end -- update parent position if frame.icon.position ~= position then frame.icon:ClearAllPoints() frame.icon:SetPoint(position, frame, position, 0, 0) frame.icon.position = position end frame.icon[pos]:Show() frame.icon:SetWidth((pos-1)*(iconsize+spacing)+iconsize) end function pfUI.uf:HideIcon(frame, pos) if not frame or not frame.hp or not frame.hp.bar then return end local frame = frame.hp.bar if frame.icon and frame.icon[pos] then frame.icon[pos]:Hide() end end function pfUI.uf:SetupDebuffFilter(allclasses) local _, myclass = UnitClass("player") local debuffs = {} if myclass == "PALADIN" or myclass == "PRIEST" or myclass == "WARLOCK" or allclasses then table.insert(debuffs, "Magic") end if myclass == "DRUID" or myclass == "PALADIN" or myclass == "SHAMAN" or allclasses then table.insert(debuffs, "Poison") end if myclass == "PRIEST" or myclass == "PALADIN" or myclass == "SHAMAN" or allclasses then table.insert(debuffs, "Disease") end if myclass == "DRUID" or myclass == "MAGE" or allclasses then table.insert(debuffs, "Curse") end return debuffs end function pfUI.uf:SetupBuffIndicators(config) local _, myclass = UnitClass("player") local indicators = {} if config.show_buffs == "1" then -- buffs if myclass == "DRUID" then -- Mark of the Wild table.insert(indicators, "interface\\icons\\spell_nature_regeneration") -- Gift of the Wild table.insert(indicators, "interface\\icons\\spell_nature_giftofthewild") -- Thorns table.insert(indicators, "interface\\icons\\spell_nature_thorns") end if myclass == "PRIEST" then -- Prayer Of Fortitude" table.insert(indicators, "interface\\icons\\spell_holy_wordfortitude") table.insert(indicators, "interface\\icons\\spell_holy_prayeroffortitude") -- Prayer of Spirit table.insert(indicators, "interface\\icons\\spell_holy_divinespirit") table.insert(indicators, "interface\\icons\\spell_holy_prayerofspirit") -- Shadow Protection table.insert(indicators, "interface\\icons\\spell_shadow_antishadow") table.insert(indicators, "interface\\icons\\spell_holy_prayerofshadowprotection") -- Fear Ward table.insert(indicators, "interface\\icons\\spell_holy_excorcism") end if myclass == "PALADIN" then -- Blessing of Salvation table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingofsalvation") table.insert(indicators, "interface\\icons\\spell_holy_sealofsalvation") -- Blessing of Wisdom table.insert(indicators, "interface\\icons\\spell_holy_sealofwisdom") table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingofwisdom") -- Blessing of Sanctuary table.insert(indicators, "interface\\icons\\spell_nature_lightningshield") table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingofsanctuary") -- Blessing of Kings table.insert(indicators, "interface\\icons\\spell_magic_magearmor") table.insert(indicators, "interface\\icons\\spell_magic_greaterblessingofkings") -- Blessing of Might table.insert(indicators, "interface\\icons\\spell_holy_fistofjustice") table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingofkings") -- Blessing of Light table.insert(indicators, "interface\\icons\\spell_holy_prayerofhealing02") table.insert(indicators, "interface\\icons\\spell_holy_greaterblessingoflight") -- Blessing of Sacrifice table.insert(indicators, "interface\\icons\\spell_holy_sealofsacrifice") -- Blessing of Freedom table.insert(indicators, "interface\\icons\\spell_holy_sealofvalor") -- Blessing of Protection table.insert(indicators, "interface\\icons\\spell_holy_sealofprotection") end if myclass == "WARLOCK" then -- Fire Shield table.insert(indicators, "interface\\icons\\spell_fire_firearmor") -- Blood Pact table.insert(indicators, "interface\\icons\\spell_shadow_bloodboil") -- Soulstone table.insert(indicators, "interface\\icons\\spell_shadow_soulgem") -- Unending Breath table.insert(indicators, "interface\\icons\\spell_shadow_demonbreath") -- Detect Greater Invisibility or Detect Invisibility table.insert(indicators, "interface\\icons\\spell_shadow_detectinvisibility") -- Detect Lesser Invisibility table.insert(indicators, "interface\\icons\\spell_shadow_detectlesserinvisibility") -- Paranoia table.insert(indicators, "interface\\icons\\Spell_Shadow_AuraOfDarkness") end if myclass == "WARRIOR" then -- Battle Shout table.insert(indicators, "interface\\icons\\ability_warrior_battleshout") -- Commanding Shout (TBC) table.insert(indicators, "interface\\icons\\ability_warrior_rallyingcry") end if myclass == "MAGE" then -- Arcane Intellect table.insert(indicators, "interface\\icons\\spell_holy_magicalsentry") table.insert(indicators, "interface\\icons\\spell_holy_arcaneintellect") -- Dampen Magic table.insert(indicators, "interface\\icons\\spell_nature_abolishmagic") -- Amplify Magic table.insert(indicators, "interface\\icons\\spell_holy_flashheal") end if myclass == "HUNTER" then -- Aspect of the Wild table.insert(indicators, "interface\\icons\\spell_nature_protectionformnature") -- Aspect of the Pack table.insert(indicators, "interface\\icons\\ability_mount_whitetiger") -- Misdirection (TBC) table.insert(indicators, "interface\\icons\\ability_hunter_misdirection") end if myclass == "SHAMAN" then -- Earth Shield (TBC) table.insert(indicators, "interface\\icons\\spell_nature_skinofearth") end end if config.show_procs == "1" then -- procs if myclass == "SHAMAN" or config.all_procs == "1" then -- Ancestral Fortitude table.insert(indicators, "interface\\icons\\spell_nature_undyingstrength") -- Healing Way table.insert(indicators, "interface\\icons\\spell_nature_healingway") -- Totemic Power (known issue: one conflicts with Blessed Sunfruit buff) table.insert(indicators, "interface\\icons\\spell_holy_spiritualguidence") table.insert(indicators, "interface\\icons\\spell_holy_devotion") table.insert(indicators, "interface\\icons\\spell_holy_holynova") table.insert(indicators, "interface\\icons\\spell_magic_magearmor") end if myclass == "PRIEST" or config.all_procs == "1" then -- Inspiration table.insert(indicators, "interface\\icons\\inv_shield_06") end end if config.show_hots == "1" then -- hots if myclass == "PRIEST" or config.all_hots == "1" then -- Renew table.insert(indicators, "interface\\icons\\spell_holy_renew") -- Power Word: Shield table.insert(indicators, "interface\\icons\\spell_holy_powerwordshield") -- Prayer of Mending (TBC) table.insert(indicators, "interface\\icons\\spell_holy_prayerofmendingtga") end if myclass == "DRUID" or config.all_hots == "1" then -- Regrowth table.insert(indicators, "interface\\icons\\spell_nature_resistnature") -- Rejuvenation table.insert(indicators, "interface\\icons\\spell_nature_rejuvenation") -- Lifebloom table.insert(indicators, "interface\\icons\\inv_misc_herb_felblossom") end end if config.show_totems == "1" and myclass == "SHAMAN" then -- totems -- Strength of Earth Totem table.insert(indicators, "interface\\icons\\spell_nature_earthbindtotem") -- Stoneskin Totem table.insert(indicators, "interface\\icons\\spell_nature_stoneskintotem") -- Mana Spring Totem table.insert(indicators, "interface\\icons\\spell_nature_manaregentotem") -- Mana Tide Totem table.insert(indicators, "interface\\icons\\spell_frost_summonwaterelemental") -- Healing Spring Totem table.insert(indicators, "interface\\icons\\inv_spear_04") -- Tranquil Air Totem table.insert(indicators, "interface\\icons\\spell_nature_brilliance") -- Grace of Air Totem table.insert(indicators, "interface\\icons\\spell_nature_invisibilitytotem") -- Grounding Totem table.insert(indicators, "interface\\icons\\spell_nature_groundingtotem") -- Nature Resistance Totem table.insert(indicators, "interface\\icons\\spell_nature_natureresistancetotem") -- Fire Resistance Totem table.insert(indicators, "interface\\icons\\spell_fireresistancetotem_01") -- Frost Resistance Totem table.insert(indicators, "interface\\icons\\spell_frostresistancetotem_01") end return indicators end local function abbrevname(t) return string.sub(t,1,1)..". " end function pfUI.uf:GetNameString(unitstr) local name = UnitName(unitstr) local abbrev = pfUI_config.unitframes.abbrevname == "1" or nil local size = 20 -- first try to only abbreviate the first word if abbrev and name and strlen(name) > size then name = string.gsub(name, "^(%S+) ", abbrevname) end -- abbreviate all if it still doesn't fit if abbrev and name and strlen(name) > size then name = string.gsub(name, "(%S+) ", abbrevname) end return name end function pfUI.uf:GetLevelString(unitstr) local level = UnitLevel(unitstr) if level == -1 then level = "??" end local elite = UnitClassification(unitstr) if elite == "worldboss" then level = level .. "B" elseif elite == "rareelite" then level = level .. "R+" elseif elite == "elite" then level = level .. "+" elseif elite == "rare" then level = level .. "R" end return level end function pfUI.uf:GetStatusValue(unit, pos) if not pos or not unit then return end local config = unit.config["txt"..pos] local unitstr = unit.label .. unit.id local frame = unit[pos .. "Text"] -- as a fallback, draw the name if pos == "center" and not config then config = "unit" end local mp, mpmax = UnitMana(unitstr), UnitManaMax(unitstr) local hp, hpmax = UnitHealth(unitstr), UnitHealthMax(unitstr) local rhp, rhpmax = hp, hpmax if pfUI.libhealth and pfUI.libhealth.enabled then rhp, rhpmax = pfUI.libhealth:GetUnitHealth(unitstr) elseif unit.label == "target" and (MobHealth3 or MobHealthFrame) and MobHealth_GetTargetCurHP() then rhp, rhpmax = MobHealth_GetTargetCurHP(), MobHealth_GetTargetMaxHP() end if config == "unit" then local name = unit:GetColor("unit") .. pfUI.uf:GetNameString(unitstr) local level = unit:GetColor("level") .. pfUI.uf:GetLevelString(unitstr) return level .. " " .. name elseif config == "unitrev" then local name = unit:GetColor("unit") .. pfUI.uf:GetNameString(unitstr) local level = unit:GetColor("level") .. pfUI.uf:GetLevelString(unitstr) return name .. " " .. level elseif config == "name" then return unit:GetColor("unit") .. pfUI.uf:GetNameString(unitstr) elseif config == "nameshort" then return unit:GetColor("unit") .. strsub(UnitName(unitstr), 0, 3) elseif config == "level" then return unit:GetColor("level") .. pfUI.uf:GetLevelString(unitstr) elseif config == "class" then if UnitIsPlayer(unitstr) then return unit:GetColor("class") .. (UnitClass(unitstr) or UNKNOWN) else return "" end -- health elseif config == "health" then return unit:GetColor("health") .. pfUI.api.Abbreviate(rhp) elseif config == "healthmax" then return unit:GetColor("health") .. pfUI.api.Abbreviate(rhpmax) elseif config == "healthperc" then return unit:GetColor("health") .. ceil(hp / hpmax * 100) elseif config == "healthmiss" then local health = ceil(rhp - rhpmax) if UnitIsDead(unitstr) then return unit:GetColor("health") .. DEAD elseif health == 0 then return unit:GetColor("health") .. "0" else return unit:GetColor("health") .. pfUI.api.Abbreviate(health) end elseif config == "healthdyn" then if hp ~= hpmax then return unit:GetColor("health") .. pfUI.api.Abbreviate(rhp) .. " - " .. ceil(hp / hpmax * 100) .. "%" else return unit:GetColor("health") .. pfUI.api.Abbreviate(rhp) end elseif config == "namehealth" then local health = ceil(rhp - rhpmax) if UnitIsDead(unitstr) then return unit:GetColor("health") .. DEAD elseif health == 0 then return unit:GetColor("unit") .. pfUI.uf:GetNameString(unitstr) else return unit:GetColor("health") .. pfUI.api.Abbreviate(health) end elseif config == "namehealthbreak" then local health = ceil(rhp - rhpmax) if UnitIsDead(unitstr) then return unit:GetColor("unit") .. pfUI.uf:GetNameString(unitstr) .. "\n" .. unit:GetColor("health") .. DEAD elseif health == 0 then return unit:GetColor("unit") .. pfUI.uf:GetNameString(unitstr) else return unit:GetColor("unit") .. pfUI.uf:GetNameString(unitstr) .. "\n" .. unit:GetColor("health") .. pfUI.api.Abbreviate(-health) end elseif config == "shortnamehealth" then local health = ceil(rhp - rhpmax) if UnitIsDead(unitstr) then return unit:GetColor("health") .. DEAD elseif health == 0 then return unit:GetColor("unit") .. strsub(UnitName(unitstr), 0, 3) else return unit:GetColor("health") .. pfUI.api.Abbreviate(health) end elseif config == "healthminmax" then return unit:GetColor("health") .. pfUI.api.Abbreviate(rhp) .. "/" .. pfUI.api.Abbreviate(rhpmax) -- mana/power/focus elseif config == "power" then return unit:GetColor("power") .. pfUI.api.Abbreviate(mp) elseif config == "powermax" then return unit:GetColor("power") .. pfUI.api.Abbreviate(mpmax) elseif config == "powerperc" then local perc = UnitManaMax(unitstr) > 0 and ceil(mp / mpmax * 100) or 0 return unit:GetColor("power") .. perc elseif config == "powermiss" then local power = ceil(mp - mpmax) if power == 0 then return unit:GetColor("power") .. "0" else return unit:GetColor("power") .. pfUI.api.Abbreviate(power) end elseif config == "powerdyn" then -- show percentage when only mana is less than 100% if mp ~= mpmax and UnitPowerType(unitstr) == 0 then return unit:GetColor("power") .. pfUI.api.Abbreviate(mp) .. " - " .. ceil(mp / mpmax * 100) .. "%" else return unit:GetColor("power") .. pfUI.api.Abbreviate(mp) end elseif config == "powerminmax" then return unit:GetColor("power") .. pfUI.api.Abbreviate(mp) .. "/" .. pfUI.api.Abbreviate(mpmax) else return "" end end function pfUI.uf.GetColor(self, preset) local config = self.config local unitstr = self.label .. self.id local r, g, b = 1, 1, 1 if preset == "unit" and config["classcolor"] == "1" then if UnitIsPlayer(unitstr) then local _, class = UnitClass(unitstr) if RAID_CLASS_COLORS[class] then r, g, b = RAID_CLASS_COLORS[class].r, RAID_CLASS_COLORS[class].g, RAID_CLASS_COLORS[class].b end elseif self.label == "pet" then local happiness = GetPetHappiness() if happiness == 1 then r, g, b = 1, 0, 0 elseif happiness == 2 then r, g, b = 1, 1, 0 else r, g, b = 0, 1, 0 end else local color = UnitReactionColor[UnitReaction(unitstr, "player")] if color then r, g, b = color.r, color.g, color.b end end elseif preset == "class" and config["classcolor"] == "1" then local _, class = UnitClass(unitstr) if RAID_CLASS_COLORS[class] then r, g, b = RAID_CLASS_COLORS[class].r, RAID_CLASS_COLORS[class].g, RAID_CLASS_COLORS[class].b end elseif preset == "reaction" and config["classcolor"] == "1" then r = UnitReactionColor[UnitReaction(unitstr, "player")].r g = UnitReactionColor[UnitReaction(unitstr, "player")].g b = UnitReactionColor[UnitReaction(unitstr, "player")].b elseif preset == "health" and config["healthcolor"] == "1" then if UnitHealthMax(unitstr) > 0 then r, g, b = GetColorGradient(UnitHealth(unitstr) / UnitHealthMax(unitstr)) else r, g, b = 0, 0, 0 end elseif preset == "power" and config["powercolor"] == "1" then r = ManaBarColor[UnitPowerType(unitstr)].r g = ManaBarColor[UnitPowerType(unitstr)].g b = ManaBarColor[UnitPowerType(unitstr)].b elseif preset == "level" and config["levelcolor"] == "1" then r = GetDifficultyColor(UnitLevel(unitstr)).r g = GetDifficultyColor(UnitLevel(unitstr)).g b = GetDifficultyColor(UnitLevel(unitstr)).b end if C.unitframes.pastel == "1" then r, g, b = (r + .75) * .5, (g + .75) * .5, (b + .75) * .5 end return rgbhex(r,g,b) end