Files
pfUI/api/unitframes.lua
T
shagu 88af6b7e52 unitframes: buffer events and run them on update
Often events are sent more often than an actual frame is drawn.
This commit buffers those events to the next draw call, to avoid
useless updates for things that would never be drawn at all.
2021-12-13 01:33:35 +01:00

2470 lines
84 KiB
Lua

-- 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 = {}
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
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.frame: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)
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("BACKGROUND")
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 = pfUI.api.strsplit(",", 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, 0, -2*default_border - f.config.pspace * 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.bartexture])
f.power.bar:SetAllPoints(f.power)
f.power.bar:SetFrameStrata("LOW")
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 = pfUI.api.strsplit(",", 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:SetFrameStrata("LOW")
f.portrait.tex:SetAllPoints(f.portrait)
f.portrait.tex:SetTexCoord(.1, .9, .1, .9)
f.portrait.model:SetFrameStrata("LOW")
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:SetFrameStrata("BACKGROUND")
f.portrait.model:SetFrameStrata("BACKGROUND")
f.portrait.model:SetFrameLevel(3)
f.portrait:Show()
elseif f.config.portrait == "left" then
f.portrait:SetParent(f)
f.portrait:ClearAllPoints()
if f.config.portraitwidth == "-1" and f.config.portraitheight == "-1" then
f.portrait:SetPoint("TOPLEFT", f, "TOPLEFT", 0, 0)
else
f.portrait:SetPoint("LEFT", f, "LEFT", -f.config.portraitwidth - 2*default_border - spacing, 0)
end
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()
if f.config.portraitwidth == "-1" and f.config.portraitheight == "-1" then
f.portrait:SetPoint("TOPRIGHT", f, "TOPRIGHT", 0, 0)
else
f.portrait:SetPoint("RIGHT", f, "RIGHT", f.config.portraitwidth + 2*default_border + spacing, 0)
end
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.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:SetParent(f.hp.bar)
f.hpLeftText:ClearAllPoints()
f.hpLeftText:SetPoint("TOPLEFT",f.hp.bar, "TOPLEFT", 2*default_border, 1)
f.hpLeftText:SetPoint("BOTTOMRIGHT",f.hp.bar, "BOTTOMRIGHT", -2*default_border, 0)
f.hpRightText:SetFontObject(GameFontWhite)
f.hpRightText:SetFont(fontname, fontsize, fontstyle)
f.hpRightText:SetJustifyH("RIGHT")
f.hpRightText:SetParent(f.hp.bar)
f.hpRightText:ClearAllPoints()
f.hpRightText:SetPoint("TOPLEFT",f.hp.bar, "TOPLEFT", 2*default_border, 1)
f.hpRightText:SetPoint("BOTTOMRIGHT",f.hp.bar, "BOTTOMRIGHT", -2*default_border, 0)
f.hpCenterText:SetFontObject(GameFontWhite)
f.hpCenterText:SetFont(fontname, fontsize, fontstyle)
f.hpCenterText:SetJustifyH("CENTER")
f.hpCenterText:SetParent(f.hp.bar)
f.hpCenterText:ClearAllPoints()
f.hpCenterText:SetPoint("TOPLEFT",f.hp.bar, "TOPLEFT", 2*default_border, 1)
f.hpCenterText:SetPoint("BOTTOMRIGHT",f.hp.bar, "BOTTOMRIGHT", -2*default_border, 0)
f.powerLeftText:SetFontObject(GameFontWhite)
f.powerLeftText:SetFont(fontname, fontsize, fontstyle)
f.powerLeftText:SetJustifyH("LEFT")
f.powerLeftText:SetParent(f.power.bar)
f.powerLeftText:ClearAllPoints()
f.powerLeftText:SetPoint("TOPLEFT",f.power.bar, "TOPLEFT", 2*default_border, 1)
f.powerLeftText:SetPoint("BOTTOMRIGHT",f.power.bar, "BOTTOMRIGHT", -2*default_border, 0)
f.powerRightText:SetFontObject(GameFontWhite)
f.powerRightText:SetFont(fontname, fontsize, fontstyle)
f.powerRightText:SetJustifyH("RIGHT")
f.powerRightText:SetParent(f.power.bar)
f.powerRightText:ClearAllPoints()
f.powerRightText:SetPoint("TOPLEFT",f.power.bar, "TOPLEFT", 2*default_border, 1)
f.powerRightText:SetPoint("BOTTOMRIGHT",f.power.bar, "BOTTOMRIGHT", -2*default_border, 0)
f.powerCenterText:SetFontObject(GameFontWhite)
f.powerCenterText:SetFont(fontname, fontsize, fontstyle)
f.powerCenterText:SetJustifyH("CENTER")
f.powerCenterText:SetParent(f.power.bar)
f.powerCenterText:ClearAllPoints()
f.powerCenterText:SetPoint("TOPLEFT",f.power.bar, "TOPLEFT", 2*default_border, 1)
f.powerCenterText:SetPoint("BOTTOMRIGHT",f.power.bar, "BOTTOMRIGHT", -2*default_border, 0)
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 = pfUI.api.strsplit(",", 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(16)
f.pvpIcon:SetHeight(16)
f.pvpIcon:SetPoint("CENTER", 0, 0)
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("TOP", f, "TOP", 0, 6)
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()
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
f.buffs[i]:SetPoint(af, f, f.config.buffs,
invert_v * (i-1-row*perrow)*(2*default_border + f.config.buffsize + 1),
invert_h * (row*(2*default_border + f.config.buffsize + 1) + (2*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()
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)
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_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
end
if this.update_portrait then
pfUI.uf:RefreshUnit(this, "portrait")
this.update_portrait = nil
end
if this.update_pvp then
pfUI.uf:RefreshUnit(this, "pvp")
this.update_pvp = 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)
if heal ~= this.lastheal then
local overhealperc = tonumber(this.config.overhealperc)
local health, maxHealth = UnitHealth(unit), UnitHealthMax(unit)
this.lastheal = heal
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.hpLeftText = f:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall")
f.hpRightText = f:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall")
f.hpCenterText = f:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall")
f.powerLeftText = f:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall")
f.powerRightText = f:CreateFontString("Status", "OVERLAY", "GameFontNormalSmall")
f.powerCenterText = f: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")
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
if unit.config.happinessicon == "0" 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")
-- 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 = UnitBuff(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 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
unit.debuffs[i]:SetPoint(af, unit, unit.config.debuffs,
invert_v * (i-1-row*perrow)*(2*default_border + unit.config.debuffsize + 1),
invert_h * ((row+buffrow)*(2*default_border + unit.config.debuffsize + 1) + (2*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"))
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 then
local name, rank, texture, stacks, dtype, duration, timeleft = 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 == "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
pfUI.uf:AddIcon(unit, pos, texture, timeleft, count)
pos = pos + 1
break
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 unit.tick then
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
else
unit.portrait.model.update = unitstr
end
end
end
end
-- 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 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 = pfUI.api.strsplit(",", 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 = pfUI.api.strsplit(",", 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
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 = pfUI.api.strsplit(",", mana)
elseif utype == 1 then
r, g, b, a = pfUI.api.strsplit(",", rage)
elseif utype == 2 then
r, g, b, a = pfUI.api.strsplit(",", focus)
elseif utype == 3 then
r, g, b, a = pfUI.api.strsplit(",", 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
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 tswitch = UnitIsUnit(unitstr, "target")
TargetUnit(unitstr)
if string.find(this.clickactions[modstring], "^%/(.+)") then
RunMacroText(this.clickactions[modstring])
else
CastSpellByName(this.clickactions[modstring])
end
if not tswitch then TargetLastTarget() 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)
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", 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:SetAlpha(0)
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 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")
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
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") .. UnitName(unitstr)
local level = unit:GetColor("level") .. pfUI.uf:GetLevelString(unitstr)
return level .. " " .. name
elseif config == "unitrev" then
local name = unit:GetColor("unit") .. UnitName(unitstr)
local level = unit:GetColor("level") .. pfUI.uf:GetLevelString(unitstr)
return name .. " " .. level
elseif config == "name" then
return unit:GetColor("unit") .. UnitName(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") .. UnitName(unitstr)
else
return 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
-- pastel
if C.unitframes.pastel == "1" then
r = ( r + .75 ) * .5
g = ( g + .75 ) * .5
b = ( b + .75 ) * .5
end
return rgbhex(r,g,b)
end