e179c33a15
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
245 lines
8.3 KiB
Lua
245 lines
8.3 KiB
Lua
-- Auras under the target frame, as camelot's TargetFrameAuraContainer lays them out
|
|
-- (shared/targetframeauracontainer.lua, targetframeaurashared.lua, targetframeaurabutton.xml)
|
|
-- and TargetFrameMixin anchors them (mainline/targetframe.lua, loaded by camelot's family):
|
|
-- rows of 122 from the frame art's BOTTOMLEFT (5, 9), 3 apart; the first two rows 101 wide
|
|
-- while the target of target shows. Hostile target: debuffs first, then buffs on a new row;
|
|
-- friendly target: the reverse. Auras from the player, pet or vehicle are 21, others 17.
|
|
-- 3.3.5 gives the caster as a unit token only: an aura from a player outside the group has
|
|
-- none, so camelot's "from a player or a player's pet" test only knows the casters it can see.
|
|
|
|
local ForeverUI = ForeverUI or {}
|
|
_G.ForeverUI = ForeverUI
|
|
|
|
local MAX_BUFFS, MAX_DEBUFFS = 32, 16
|
|
local SMALL_SIZE, LARGE_SIZE = 17, 21
|
|
local SPACING = 3 -- between auras, and between rows
|
|
local LINE_SIZE = 122
|
|
local TOT_LINE_SIZE, TOT_LINES = 101, 2 -- rows beside the target of target
|
|
local START_X, START_Y = 5, 9 -- from the frame art's BOTTOMLEFT
|
|
|
|
-- Buff border when the buff can be stolen (the target is not the player); debuff border in
|
|
-- the dispel type's color, 1 px around the icon.
|
|
local STEALABLE_BORDER = "Interface\\TargetingFrame\\UI-TargetingFrame-Stealable"
|
|
local STEALABLE_SIZE = 24
|
|
local DEBUFF_BORDER = "Interface\\Buttons\\UI-Debuff-Overlays"
|
|
|
|
local PLAYER_UNITS = { "player", "vehicle", "pet" }
|
|
|
|
local frame = ForeverUI.TargetFrame
|
|
local targetOfTarget = _G["ForeverUITargetOfTarget"]
|
|
|
|
local container = CreateFrame("Frame", "ForeverUITargetFrameAuras", frame)
|
|
container:SetWidth(1)
|
|
container:SetHeight(1)
|
|
container:SetPoint("TOPLEFT", frame.art, "BOTTOMLEFT", START_X, START_Y)
|
|
|
|
local buttons = { HELPFUL = {}, HARMFUL = {} }
|
|
|
|
-- ---------------------------------------------------------------- Rules
|
|
|
|
-- The player, its vehicle or its pet cast it (camelot ShouldShowAuraWithLargeSize)
|
|
local function fromPlayer(caster)
|
|
if not caster then
|
|
return false
|
|
end
|
|
for _, token in ipairs(PLAYER_UNITS) do
|
|
if UnitIsUnit(caster, token) then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- camelot ShouldShowAuraAsDebuff: on a hostile creature, debuffs from other players are left out
|
|
local function keepDebuff(caster, friendly)
|
|
if fromPlayer(caster) or UnitIsUnit("player", "target") then
|
|
return true
|
|
end
|
|
local targetIsPlayer = UnitIsPlayer("target")
|
|
local targetIsPet = UnitPlayerControlled("target") and not targetIsPlayer
|
|
if not targetIsPlayer and not targetIsPet and not friendly and caster and UnitPlayerControlled(caster) then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
-- Auras of one kind, in client order; filter: "HELPFUL" or "HARMFUL"; maximum: how many
|
|
-- at most; friendly: the target's reaction
|
|
local function collect(filter, maximum, friendly)
|
|
local list = {}
|
|
local index = 1
|
|
while #list < maximum do
|
|
local name, _, icon, count, dispelType, duration, expiration, caster, stealable = UnitAura("target", index, filter)
|
|
if not name then
|
|
break
|
|
end
|
|
if filter == "HELPFUL" or keepDebuff(caster, friendly) then
|
|
list[#list + 1] = {
|
|
index = index, icon = icon, count = count, dispelType = dispelType,
|
|
duration = duration, expiration = expiration, stealable = stealable,
|
|
size = fromPlayer(caster) and LARGE_SIZE or SMALL_SIZE,
|
|
}
|
|
end
|
|
index = index + 1
|
|
end
|
|
return list
|
|
end
|
|
|
|
-- ---------------------------------------------------------------- Buttons
|
|
|
|
local function createButton(filter, rank)
|
|
local kind = filter == "HELPFUL" and "Buff" or "Debuff"
|
|
local button = CreateFrame("Button", container:GetName() .. kind .. rank, container)
|
|
button.filter = filter
|
|
|
|
local icon = button:CreateTexture(nil, "BACKGROUND")
|
|
icon:SetAllPoints(button)
|
|
button.icon = icon
|
|
|
|
local cooldown = CreateFrame("Cooldown", button:GetName() .. "Cooldown", button)
|
|
cooldown:SetPoint("TOPLEFT", button, "TOPLEFT", 0, -1)
|
|
cooldown:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 0, -1)
|
|
cooldown:SetReverse(true)
|
|
if cooldown.SetDrawEdge then
|
|
cooldown:SetDrawEdge(true)
|
|
end
|
|
button.cooldown = cooldown
|
|
|
|
-- The cooldown is a child frame, drawn above the button: the count and the borders go
|
|
-- in a frame above it
|
|
local overlay = CreateFrame("Frame", nil, button)
|
|
overlay:SetAllPoints(button)
|
|
overlay:SetFrameLevel(cooldown:GetFrameLevel() + 1)
|
|
|
|
local count = overlay:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmall")
|
|
count:SetJustifyH("RIGHT")
|
|
count:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 1, 0)
|
|
button.count = count
|
|
|
|
local border = overlay:CreateTexture(nil, "OVERLAY")
|
|
if filter == "HELPFUL" then
|
|
border:SetTexture(STEALABLE_BORDER)
|
|
border:SetBlendMode("ADD")
|
|
border:SetWidth(STEALABLE_SIZE)
|
|
border:SetHeight(STEALABLE_SIZE)
|
|
border:SetPoint("CENTER", button, "CENTER", 0, 0)
|
|
else
|
|
border:SetTexture(DEBUFF_BORDER)
|
|
border:SetTexCoord(0.296875, 0.5703125, 0, 0.515625)
|
|
border:SetPoint("TOPLEFT", button, "TOPLEFT", -1, 1)
|
|
border:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 1, -1)
|
|
end
|
|
button.border = border
|
|
|
|
button:SetScript("OnEnter", function(self)
|
|
GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT", 15, -25)
|
|
if self.filter == "HELPFUL" then
|
|
GameTooltip:SetUnitBuff("target", self.index)
|
|
else
|
|
GameTooltip:SetUnitDebuff("target", self.index)
|
|
end
|
|
end)
|
|
button:SetScript("OnLeave", function()
|
|
GameTooltip:Hide()
|
|
end)
|
|
|
|
buttons[filter][rank] = button
|
|
return button
|
|
end
|
|
|
|
-- aura: an entry of collect; playerIsTarget: no stealable border on the player's own buffs
|
|
local function fill(button, aura, playerIsTarget)
|
|
button.index = aura.index
|
|
button.icon:SetTexture(aura.icon)
|
|
|
|
if aura.count and aura.count > 1 then
|
|
button.count:SetText(aura.count)
|
|
button.count:Show()
|
|
else
|
|
button.count:Hide()
|
|
end
|
|
|
|
if aura.duration and aura.duration > 0 and aura.expiration then
|
|
CooldownFrame_SetTimer(button.cooldown, aura.expiration - aura.duration, aura.duration, 1)
|
|
else
|
|
button.cooldown:Hide()
|
|
end
|
|
|
|
if button.filter == "HELPFUL" then
|
|
if aura.stealable and not playerIsTarget then
|
|
button.border:Show()
|
|
else
|
|
button.border:Hide()
|
|
end
|
|
else
|
|
local color = DebuffTypeColor[aura.dispelType or "none"] or DebuffTypeColor["none"]
|
|
button.border:SetVertexColor(color.r, color.g, color.b)
|
|
end
|
|
end
|
|
|
|
-- ---------------------------------------------------------------- Layout
|
|
|
|
-- Flow layout of camelot's container: each aura right of the previous one, a new row when
|
|
-- the next one would pass the row's width, the second group always on a new row.
|
|
local function layout()
|
|
local used = { HELPFUL = 0, HARMFUL = 0 }
|
|
|
|
if UnitExists("target") then
|
|
local friendly = UnitIsFriend("player", "target")
|
|
local playerIsTarget = UnitIsUnit("player", "target")
|
|
local buffs = { filter = "HELPFUL", list = collect("HELPFUL", MAX_BUFFS, friendly) }
|
|
local debuffs = { filter = "HARMFUL", list = collect("HARMFUL", MAX_DEBUFFS, friendly) }
|
|
local groups = friendly and { buffs, debuffs } or { debuffs, buffs }
|
|
local constrained = (targetOfTarget and targetOfTarget:IsShown()) and TOT_LINES or 0
|
|
|
|
local x, y, line, lineHeight = 0, 0, 1, 0
|
|
local function newLine()
|
|
y = y + lineHeight + SPACING
|
|
x, line, lineHeight = 0, line + 1, 0
|
|
end
|
|
|
|
for _, group in ipairs(groups) do
|
|
if #group.list > 0 and x > 0 then
|
|
newLine()
|
|
end
|
|
for rank, aura in ipairs(group.list) do
|
|
local limit = line <= constrained and TOT_LINE_SIZE or LINE_SIZE
|
|
if x > 0 and x + aura.size > limit then
|
|
newLine()
|
|
end
|
|
local button = buttons[group.filter][rank] or createButton(group.filter, rank)
|
|
button:ClearAllPoints()
|
|
button:SetPoint("TOPLEFT", container, "TOPLEFT", x, -y)
|
|
button:SetWidth(aura.size)
|
|
button:SetHeight(aura.size)
|
|
fill(button, aura, playerIsTarget)
|
|
button:Show()
|
|
x = x + aura.size + SPACING
|
|
lineHeight = math.max(lineHeight, aura.size)
|
|
end
|
|
used[group.filter] = #group.list
|
|
end
|
|
end
|
|
|
|
for filter, list in pairs(buttons) do
|
|
for rank = used[filter] + 1, #list do
|
|
list[rank]:Hide()
|
|
end
|
|
end
|
|
end
|
|
|
|
container:SetScript("OnEvent", function(_, event, unit)
|
|
if event ~= "UNIT_AURA" and event ~= "UNIT_FACTION" or unit == "target" then
|
|
layout()
|
|
end
|
|
end)
|
|
for _, event in ipairs({ "PLAYER_ENTERING_WORLD", "PLAYER_TARGET_CHANGED", "UNIT_AURA", "UNIT_FACTION" }) do
|
|
container:RegisterEvent(event)
|
|
end
|
|
|
|
-- The first rows narrow while the target of target shows
|
|
if targetOfTarget then
|
|
targetOfTarget:HookScript("OnShow", layout)
|
|
targetOfTarget:HookScript("OnHide", layout)
|
|
end
|