Files
moon-well-ui/data/addon/ForeverUI/TargetFrame.lua
T
Adrestie c994e4c16d ForeverUI : barres des cadres d'unité au-dessus de l'art
Les barres de vie et de ressource du joueur, de la cible, de la cible de
la cible et du familier étaient dans le calque BORDER, sous l'art du
cadre (ARTWORK). Les emplacements des barres de cet art sont un voile
sombre : noir à 40 % en 69893, brun à 74 % en 70450, qui recouvrait donc
les barres et les rendait très sombres. Camelot dessine ses barres
(StatusBar, cadres enfants) au-dessus de l'art : elles passent en
OVERLAY. Les cadres de groupe les avaient déjà dans un cadre enfant.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 23:06:49 +02:00

729 lines
24 KiB
Lua

-- Target frame and target-of-target frame, rebuilt from camelot.
-- Sizes and anchors: mainline/TargetFrame.xml, as camelot/TargetFrame.lua overrides them since
-- 1.60.1.70450 (TargetFrameMixin:OnLoad, UpdateHealthBarVisuals, TargetOfTargetMixin:OnLoad);
-- art by classification: CheckClassification in mainline/TargetFrame.lua; combo points:
-- camelot/ComboFrame.lua.
local FRAME_WIDTH, FRAME_HEIGHT = 232, 100
local L = ForeverUI.L
local ART = {
normal = "ui-hud-unitframe-target-portraiton",
rare = "ui-hud-unitframe-target-rare-portraiton",
minus = "ui-hud-unitframe-target-minusmob-portraiton",
}
local THREAT_GLOW = "ui-hud-unitframe-target-portraiton-incombat"
local HEALTH_FILL = "ui-hud-unitframe-target-portraiton-bar-health"
-- camelot TargetFrameMixin:UpdateHealthBarVisuals ignores the classification: a minus mob takes
-- the normal bar. Health container 126 x 15 at TOPLEFT (21, -44), the 126 x 20 bar 3 px above
-- it, so (21, -41); power 134 x 10 at (17, -60), hidden for minus mobs (mainline).
local BAR_LAYOUT = {
normal = { health = { 21, -41, 126, 20 }, power = { 17, -60, 134, 10 }, showPower = true },
minus = { health = { 21, -41, 126, 20 }, power = nil, showPower = false },
}
local POWER_FILL = {
normal = {
MANA = "ui-hud-unitframe-target-portraiton-bar-mana",
RAGE = "ui-hud-unitframe-target-portraiton-bar-rage",
FOCUS = "ui-hud-unitframe-target-portraiton-bar-focus",
ENERGY = "ui-hud-unitframe-target-portraiton-bar-energy",
RUNIC_POWER = "ui-hud-unitframe-target-portraiton-bar-runicpower",
},
}
-- Portrait ring by classification (camelot GetBossPortraitFrameData): gold for elite,
-- silver winged for rare and rare elite, gold winged for world boss, anchored TOPRIGHT with
-- these offsets; no star (ShouldShowStar is always false). Sheet: interface/hud/uiunitframeboss.blp.
-- Boss-Rare-Silver-Winged exists only as c60, so camelot uses the c60 set. The base
-- variants are smaller (80x79 vs 100x100) and, anchored by their top-right, land off.
local CLASS_RING = {
worldboss = { "ui-hud-unitframe-target-portraiton-boss-gold-winged-c60", 11, -4 },
rareelite = { "ui-hud-unitframe-target-portraiton-boss-rare-silver-winged-c60", 8, -7 },
rare = { "ui-hud-unitframe-target-portraiton-boss-rare-silver-winged-c60", 8, -7 },
elite = { "ui-hud-unitframe-target-portraiton-boss-gold-c60", 0, 1 },
}
local frame = CreateFrame("Button", "ForeverUITargetFrame", UIParent, "SecureUnitButtonTemplate")
frame:SetWidth(FRAME_WIDTH)
frame:SetHeight(FRAME_HEIGHT)
frame:SetFrameStrata("LOW")
frame:SetHitRectInsets(20, 20, 16, 17)
frame.unit = "target"
frame:SetAttribute("unit", "target")
frame:SetAttribute("*type1", "target")
frame:SetAttribute("*type2", "menu")
frame:RegisterForClicks("AnyUp")
frame.menu = function(self)
ForeverUI.UnitMenu.open(ToggleDropDownMenu, 1, nil, TargetFrameDropDown, self, 120, 10)
end
local portrait = frame:CreateTexture(nil, "BACKGROUND")
portrait:SetWidth(58)
portrait:SetHeight(58)
portrait:SetPoint("TOPRIGHT", -26, -19)
-- Bars above the art (OVERLAY): camelot draws its StatusBars over the frame texture, whose
-- bar slots are a dark veil
local healthFill = frame:CreateTexture(nil, "OVERLAY")
healthFill:SetPoint("TOPLEFT", 21, -41)
local powerFill = frame:CreateTexture(nil, "OVERLAY")
powerFill:SetPoint("TOPLEFT", 17, -60)
local art = frame:CreateTexture(nil, "ARTWORK")
art:SetPoint("CENTER", 0, 0)
ForeverUI.SetAtlas(art, ART.normal)
-- Reputation band: it carries the reaction color, not the health bar.
local reputation = frame:CreateTexture(nil, "BACKGROUND")
reputation:SetPoint("TOPRIGHT", -82, -26)
ForeverUI.SetAtlas(reputation, "ui-hud-unitframe-target-portraiton-type")
-- As on the player frame: whatever must stay above the art lives in a child frame, never
-- in the same layer.
local overlayHolder = CreateFrame("Frame", nil, frame)
overlayHolder:SetAllPoints(frame)
overlayHolder:SetFrameLevel(frame:GetFrameLevel() + 1)
local threatGlow = overlayHolder:CreateTexture(nil, "BACKGROUND")
threatGlow:SetPoint("CENTER", frame, "CENTER", -1, 1)
ForeverUI.SetAtlas(threatGlow, THREAT_GLOW)
threatGlow:Hide()
-- Ring: GetBossPortraitFrameData anchors it TOPRIGHT on the container. The source puts it
-- in ARTWORK sublevel 2, above the frame art and threat glow but below the bars, name and
-- level circle. Here: BORDER of the holder, above the glow (BACKGROUND) and below the level
-- circle (ARTWORK) and the text (OVERLAY).
local classRing = overlayHolder:CreateTexture(nil, "BORDER")
classRing:Hide()
local levelCircle = overlayHolder:CreateTexture(nil, "ARTWORK")
levelCircle:SetPoint("BOTTOMRIGHT", -13, 7)
ForeverUI.SetAtlas(levelCircle, "ui-hud-unitframe-smallcircle")
local levelText = overlayHolder:CreateFontString(nil, "OVERLAY", "GameFontNormal")
levelText:SetPoint("CENTER", levelCircle, "CENTER", 0, -0.5)
local nameText = overlayHolder:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
nameText:SetWidth(117)
nameText:SetHeight(12)
-- camelot: centered, TOPLEFT on the band's TOPRIGHT at (-126, -4)
nameText:SetJustifyH("CENTER")
nameText:SetPoint("TOPLEFT", 24, -30)
-- PvP badge (camelot 70450 PvpBackgroundIconTemplate): no circle any more, the icon alone at
-- scale 0.8, its TOP at (-24, -65) of the TOPRIGHT in its own scaled units. camelot keeps it in
-- TargetFrameContentContextual, drawn over the level circle: here a child frame one level up.
local PVP_SCALE = 0.8
local pvpHolder = CreateFrame("Frame", nil, overlayHolder)
pvpHolder:SetAllPoints(frame)
pvpHolder:SetFrameLevel(overlayHolder:GetFrameLevel() + 1)
local pvpIcon = pvpHolder:CreateTexture(nil, "BORDER")
pvpIcon:SetPoint("TOP", frame, "TOPRIGHT", -24 * PVP_SCALE, -65 * PVP_SCALE)
pvpIcon:Hide()
local PVP_ICONS = {
FFA = "ui-hud-unitframe-player-pvp-ffaicon",
Horde = "ui-hud-unitframe-smallcircle-horde",
Alliance = "ui-hud-unitframe-smallcircle-alliance",
}
local healthText = overlayHolder:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
healthText:SetPoint("CENTER", frame, "TOPLEFT", 21 + 63, -41 - 10)
healthText:Hide()
local powerText = overlayHolder:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
powerText:SetPoint("CENTER", frame, "TOPLEFT", 17 + 67, -60 - 5)
powerText:Hide()
frame.portrait = portrait
frame.healthFill = healthFill
frame.powerFill = powerFill
frame.art = art
frame.threatGlow = threatGlow
frame.classRing = classRing
frame.reputation = reputation
frame.nameText = nameText
frame.levelText = levelText
frame.healthText = healthText
frame.powerText = powerText
frame.overlayHolder = overlayHolder
frame.pvpHolder = pvpHolder
frame.pvpIcon = pvpIcon
-- Target classification. It picks two separate things: the frame art family (normal, rare,
-- minus) and the portrait ring (elite, rare, boss).
local function classification()
return (UnitClassification and UnitClassification("target")) or "normal"
end
local function artKindFor(class)
if class == "minus" then
return "minus"
end
if class == "rare" or class == "rareelite" then
return "rare"
end
return "normal"
end
-- camelot CheckFaction, PvP part: free-for-all badge, else the faction's for a flagged target
-- (3.3.5 has no honor levels, so never the prestige variant)
local function updatePvP()
local key
local faction = UnitFactionGroup("target")
if UnitIsPVPFreeForAll("target") then
key = "FFA"
elseif faction and faction ~= "Neutral" and UnitIsPVP("target") then
key = faction
end
if key and PVP_ICONS[key] and ForeverUI.SetAtlas(pvpIcon, PVP_ICONS[key]) then
pvpIcon:SetWidth(pvpIcon:GetWidth() * PVP_SCALE)
pvpIcon:SetHeight(pvpIcon:GetHeight() * PVP_SCALE)
pvpIcon:Show()
else
pvpIcon:Hide()
end
frame.pvpShown = pvpIcon:IsShown()
-- the badge moves the start of the combo point arc
if ForeverUI.ComboPoints then
ForeverUI.ComboPoints.place()
end
end
-- camelot CheckFaction: the band takes the selection color; band and portrait turn grey
-- when the target is tapped by someone else. 3.3.5 has no UnitIsTapDenied, so this uses
-- UnitIsTapped and UnitIsTappedByPlayer.
local function updateFaction()
local tapDenied = UnitIsTapped and UnitIsTapped("target")
and not UnitIsTappedByPlayer("target") and not UnitPlayerControlled("target")
if tapDenied then
reputation:SetVertexColor(0.5, 0.5, 0.5)
portrait:SetVertexColor(0.5, 0.5, 0.5)
else
if UnitSelectionColor then
reputation:SetVertexColor(UnitSelectionColor("target"))
end
portrait:SetVertexColor(1.0, 1.0, 1.0)
end
updatePvP()
end
-- Copy of TargetFrameMixin:CheckClassification: art, glow, and the bars' size and
-- position, which it sets again every time.
local function updateArt()
local class = classification()
local artKind = artKindFor(class)
local barKind = (artKind == "minus") and "minus" or "normal"
local layout = BAR_LAYOUT[barKind]
ForeverUI.SetAtlas(art, ART[artKind] or ART.normal)
ForeverUI.SetAtlas(threatGlow, THREAT_GLOW)
healthFill:ClearAllPoints()
healthFill:SetPoint("TOPLEFT", layout.health[1], layout.health[2])
if layout.showPower then
powerFill:ClearAllPoints()
powerFill:SetPoint("TOPLEFT", layout.power[1], layout.power[2])
powerFill:Show()
powerText:Show()
else
powerFill:Hide()
powerText:Hide()
end
frame.barKind = barKind
frame.classification = class
frame.layout = layout
local ring = CLASS_RING[class]
if ring and ForeverUI.SetAtlas(classRing, ring[1]) then
classRing:ClearAllPoints()
classRing:SetPoint("TOPRIGHT", frame, "TOPRIGHT", ring[2], ring[3])
classRing:Show()
else
classRing:Hide()
end
end
local function updateHealth()
local maximum = UnitHealthMax("target")
local fraction = 0
if maximum and maximum > 0 then
fraction = UnitHealth("target") / maximum
end
local layout = frame.layout or BAR_LAYOUT.normal
ForeverUI.SetAtlasFill(healthFill, HEALTH_FILL, fraction, layout.health[3])
end
local function updatePower()
local layout = frame.layout or BAR_LAYOUT.normal
if not layout.showPower then
powerFill:Hide()
return
end
local powerType, powerToken = UnitPowerType("target")
local set = POWER_FILL.normal
local atlas = set[powerToken or ""] or set.MANA
local maximum = UnitPowerMax("target", powerType)
local fraction = 0
if maximum and maximum > 0 then
fraction = UnitPower("target", powerType) / maximum
end
ForeverUI.SetAtlasFill(powerFill, atlas, fraction, layout.power[3])
end
-- Health and power texts: shown with the targetStatusText CVar or on hover.
local function updateTexts()
local always = GetCVar and GetCVar("targetStatusText") == "1"
if not (always or frame.hovered) then
healthText:Hide()
powerText:Hide()
return
end
local function format(value, maximum)
if not maximum or maximum <= 0 then
return ""
end
if GetCVarBool and GetCVarBool("statusTextPercentage") then
return tostring(math.ceil(value / maximum * 100)) .. "%"
end
return value .. " / " .. maximum
end
local powerType = UnitPowerType("target")
healthText:SetText(format(UnitHealth("target"), UnitHealthMax("target")))
powerText:SetText(format(UnitPower("target", powerType), UnitPowerMax("target", powerType)))
healthText:Show()
powerText:Show()
end
local function updateName()
nameText:SetText(UnitName("target"))
end
-- camelot CheckLevel: an attackable target's level takes its difficulty color, any other the
-- normal gold. 3.3.5 rates it with GetQuestDifficultyColor (its TargetFrame_CheckLevel); a level
-- too high to tell is the hardest one. Among creatures, only neutral and hostile ones are rated:
-- UnitCanAttack judges a creature from the player's side only, so a faction set at war while
-- Friendly passes as attackable, which the server refuses.
local LEVEL_GOLD = { r = 1.0, g = 0.82, b = 0.0 }
local REACTION_NEUTRAL = 4
local function isLevelRated()
if not UnitCanAttack("player", "target") then
return false
end
if UnitPlayerControlled("target") then
return true
end
local reaction = UnitReaction("target", "player")
return not reaction or reaction <= REACTION_NEUTRAL
end
local function updateLevel()
local level = UnitLevel("target")
local attackable = isLevelRated()
local color = LEVEL_GOLD
if level and level > 0 then
levelText:SetText(level)
if attackable then
color = GetQuestDifficultyColor(level)
end
else
levelText:SetText("??")
if attackable then
color = QuestDifficultyColors.impossible
end
end
levelText:SetVertexColor(color.r, color.g, color.b)
end
local function updatePortrait()
SetPortraitTexture(portrait, "target")
end
-- Threat: whether the target holds aggro on someone. Falls back on UnitAffectingCombat,
-- like the player frame, since threat data is not guaranteed.
local function updateThreat()
if not UnitExists("target") then
threatGlow:Hide()
return
end
local status = UnitThreatSituation and UnitThreatSituation("target")
local engaged = UnitAffectingCombat("target")
if status and status >= 2 then
threatGlow:SetVertexColor(1.0, 0.0, 0.0)
threatGlow:SetAlpha(1.0)
threatGlow:Show()
elseif engaged or (status and status > 0) then
threatGlow:SetVertexColor(1.0, 0.0, 0.0)
threatGlow:SetAlpha(0.45)
threatGlow:Show()
else
threatGlow:Hide()
end
end
local function updateAll()
if not UnitExists("target") then
return
end
updateArt()
updateFaction()
updateHealth()
updatePower()
updateName()
updateLevel()
updatePortrait()
updateThreat()
updateTexts()
end
-- ------------------------------------------------------------- Combo points
-- camelot/ComboFrame.lua (1.60.1.70450): five RogueComboPoint (20 x 20) at scale 0.6, on an
-- arc around the target's portrait: centre = portrait centre + (2, 2), radius 66 in the points'
-- scale (39.6 px), angles -10 + 21 * (i - 1) degrees, from 2 degrees when the PvP badge shows.
-- Filled from the top (one point lights the 74-degree one); shown from one point, fading in
-- over 0.3 s. Atlases uf-roguecp-*-c60 (red only: camelot has no charged points). 3.3.5 has no
-- FlipBook: the slash animation is not reproduced, the icon fades in instead.
local COMBO_COUNT, COMBO_SCALE, COMBO_RADIUS, COMBO_STEP = 5, 0.6, 66, 21
local COMBO_START, COMBO_START_PVP = -10, 2
local COMBO_FADE, COMBO_ICON_FADE = 0.3, 0.27
local combo = CreateFrame("Frame", "ForeverUIComboPoints", frame)
combo:SetWidth(1)
combo:SetHeight(1)
combo:SetPoint("CENTER", portrait, "CENTER", 2, 2)
combo:SetFrameLevel(pvpHolder:GetFrameLevel() + 1)
combo:Hide()
-- one point: shadow, inactive and active backgrounds, red icon, each at its atlas size x 0.6
local function comboLayer(point, layer, atlas, x, y)
local t = point:CreateTexture(nil, layer)
ForeverUI.SetAtlas(t, atlas)
t:SetWidth(t:GetWidth() * COMBO_SCALE)
t:SetHeight(t:GetHeight() * COMBO_SCALE)
t:SetPoint("CENTER", point, "CENTER", (x or 0) * COMBO_SCALE, (y or 0) * COMBO_SCALE)
return t
end
combo.dots = {}
for i = 1, COMBO_COUNT do
local point = CreateFrame("Frame", nil, combo)
point:SetWidth(20 * COMBO_SCALE)
point:SetHeight(20 * COMBO_SCALE)
point.shadow = comboLayer(point, "BACKGROUND", "uf-roguecp-bg-shadow", 0, -4)
point.inactive = comboLayer(point, "BORDER", "uf-roguecp-bg-dis")
point.active = comboLayer(point, "BORDER", "uf-roguecp-bg")
point.icon = comboLayer(point, "ARTWORK", "uf-roguecp-icon-red")
point.active:Hide()
point.icon:Hide()
combo.dots[i] = point
end
-- arc positions; the PvP badge moves the start of the arc
local function placeComboPoints()
local start = frame.pvpShown and COMBO_START_PVP or COMBO_START
for i, point in ipairs(combo.dots) do
local angle = math.rad(start + COMBO_STEP * (i - 1))
point:ClearAllPoints()
point:SetPoint("CENTER", combo, "CENTER",
COMBO_RADIUS * COMBO_SCALE * math.cos(angle), COMBO_RADIUS * COMBO_SCALE * math.sin(angle))
end
end
local function setPointActive(point, active, animate)
if point.isActive == active then return end
point.isActive = active
if active then
point.inactive:Hide()
point.active:Show()
point.icon:Show()
else
point.inactive:Show()
point.active:Hide()
point.icon:Hide()
end
point.fade = (active and animate) and 0 or nil
point.icon:SetAlpha((active and animate) and 0 or 1)
end
-- animate: false on a target change (camelot SetActiveNoAnimation)
local function updateComboPoints(animate)
local count = (UnitExists("target") and GetComboPoints("player", "target")) or 0
placeComboPoints()
local first = COMBO_COUNT - count + 1
for i, point in ipairs(combo.dots) do
setPointActive(point, i >= first, animate)
end
if count > 0 then
if not combo:IsShown() then
combo.fade = 0
combo:SetAlpha(0)
combo:Show()
end
else
combo:Hide()
end
combo.count = count
end
combo:SetScript("OnUpdate", function(self, elapsed)
if self.fade then
self.fade = self.fade + elapsed
self:SetAlpha(math.min(1, self.fade / COMBO_FADE))
if self.fade >= COMBO_FADE then self.fade = nil end
end
for _, point in ipairs(self.dots) do
if point.fade then
point.fade = point.fade + elapsed
point.icon:SetAlpha(math.min(1, point.fade / COMBO_ICON_FADE))
if point.fade >= COMBO_ICON_FADE then point.fade = nil end
end
end
end)
-- The arc lives on the target frame but listens on its own: the target frame hides with no
-- target, and combo points belong to the player
local comboEvents = CreateFrame("Frame")
comboEvents:SetScript("OnEvent", function(_self, event, unit)
if event == "PLAYER_TARGET_CHANGED" then
updateComboPoints(false)
elseif event == "UNIT_COMBO_POINTS" then
if unit == nil or unit == "player" or unit == "vehicle" then
updateComboPoints(true)
end
elseif event == "UNIT_FACTION" and unit == "target" then
updateComboPoints(false)
end
end)
comboEvents:RegisterEvent("PLAYER_TARGET_CHANGED")
comboEvents:RegisterEvent("UNIT_COMBO_POINTS")
comboEvents:RegisterEvent("UNIT_FACTION")
frame.combo = combo
ForeverUI.ComboPoints = { frame = combo, update = updateComboPoints, place = placeComboPoints }
local function hideDefaultTargetFrame()
if InCombatLockdown() or not TargetFrame then
return
end
ForeverUI.Suppress(TargetFrame)
-- The client's ComboFrame gives way to camelot's arc (ForeverUI.ComboPoints below)
if ComboFrame then
ForeverUI.Suppress(ComboFrame)
end
end
frame:SetScript("OnEnter", function(self)
self.hovered = true
updateTexts()
end)
frame:SetScript("OnLeave", function(self)
self.hovered = false
updateTexts()
end)
frame:SetScript("OnEvent", function(self, event, unit)
if event == "PLAYER_ENTERING_WORLD" then
hideDefaultTargetFrame()
updateAll()
return
end
-- Skipped when PLAYER_ENTERING_WORLD came in combat (reload, reconnect): done now
if event == "PLAYER_REGEN_ENABLED" then
hideDefaultTargetFrame()
return
end
if event == "PLAYER_TARGET_CHANGED" then
updateAll()
return
end
if event == "CVAR_UPDATE" then
updateTexts()
return
end
-- The player's own faction (PvP flag) also decides which targets are attackable
if event == "UNIT_FACTION" and unit == "player" then
if UnitExists("target") then
updateLevel()
end
return
end
if unit ~= "target" then
return
end
if event == "UNIT_HEALTH" or event == "UNIT_MAXHEALTH" then
updateHealth()
updateTexts()
elseif event == "UNIT_NAME_UPDATE" then
updateName()
elseif event == "UNIT_PORTRAIT_UPDATE" then
updatePortrait()
elseif event == "UNIT_LEVEL" then
updateLevel()
elseif event == "UNIT_CLASSIFICATION_CHANGED" then
updateArt()
updateHealth()
updatePower()
elseif event == "UNIT_FACTION" then
updateFaction()
updateLevel()
elseif event == "UNIT_THREAT_SITUATION_UPDATE" then
updateThreat()
else
updatePower()
updateTexts()
end
end)
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("PLAYER_REGEN_ENABLED")
frame:RegisterEvent("PLAYER_TARGET_CHANGED")
frame:RegisterEvent("CVAR_UPDATE")
frame:RegisterEvent("UNIT_HEALTH")
frame:RegisterEvent("UNIT_MAXHEALTH")
frame:RegisterEvent("UNIT_NAME_UPDATE")
frame:RegisterEvent("UNIT_PORTRAIT_UPDATE")
frame:RegisterEvent("UNIT_LEVEL")
frame:RegisterEvent("UNIT_CLASSIFICATION_CHANGED")
frame:RegisterEvent("UNIT_FACTION")
frame:RegisterEvent("UNIT_DISPLAYPOWER")
frame:RegisterEvent("UNIT_THREAT_SITUATION_UPDATE")
frame:RegisterEvent("UNIT_MANA")
frame:RegisterEvent("UNIT_RAGE")
frame:RegisterEvent("UNIT_FOCUS")
frame:RegisterEvent("UNIT_ENERGY")
frame:RegisterEvent("UNIT_RUNIC_POWER")
frame:RegisterEvent("UNIT_MAXMANA")
frame:RegisterEvent("UNIT_MAXRAGE")
frame:RegisterEvent("UNIT_MAXFOCUS")
frame:RegisterEvent("UNIT_MAXENERGY")
frame:RegisterEvent("UNIT_MAXRUNIC_POWER")
-- The frame exists only with a target: the client shows and hides it, like any secure
-- unit frame.
if RegisterUnitWatch then
RegisterUnitWatch(frame)
end
-- ------------------------------------------------- Target of target
-- mainline/TargetFrame.xml TargetofTargetFrameTemplate: 120 x 49, TOPRIGHT on the target
-- frame's BOTTOMRIGHT at (12, 10), portrait 37 x 37 at (5, -5). camelot 70450
-- TargetOfTargetMixin:OnLoad: name centered right of the portrait (2, -1); health bar 70 x 10
-- BOTTOMRIGHT on the RIGHT point at (-5, -3), i.e. TOPLEFT (45, -17.5); power bar 74 x 7 at
-- (-2, -2) of the health bar's BOTTOMLEFT, i.e. (43, -29.5).
local tot = CreateFrame("Button", "ForeverUITargetOfTarget", frame, "SecureUnitButtonTemplate")
tot:SetWidth(120)
tot:SetHeight(49)
tot:SetPoint("TOPRIGHT", frame, "BOTTOMRIGHT", 12, 10)
tot:SetAttribute("unit", "targettarget")
tot:SetAttribute("*type1", "target")
tot:RegisterForClicks("AnyUp")
tot.unit = "targettarget"
local totPortrait = tot:CreateTexture(nil, "BACKGROUND")
totPortrait:SetWidth(37)
totPortrait:SetHeight(37)
totPortrait:SetPoint("TOPLEFT", 5, -5)
local totHealth = tot:CreateTexture(nil, "OVERLAY")
totHealth:SetPoint("TOPLEFT", 45, -17.5)
local totPower = tot:CreateTexture(nil, "OVERLAY")
totPower:SetPoint("TOPLEFT", 43, -29.5)
local totArt = tot:CreateTexture(nil, "ARTWORK")
totArt:SetPoint("CENTER")
ForeverUI.SetAtlas(totArt, "ui-hud-unitframe-targetoftarget-portraiton")
local totName = tot:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
totName:SetWidth(68)
totName:SetHeight(10)
totName:SetJustifyH("CENTER")
totName:SetPoint("TOPLEFT", totPortrait, "TOPRIGHT", 2, -1)
local TOT_POWER = {
MANA = "ui-hud-unitframe-targetoftarget-portraiton-bar-mana",
RAGE = "ui-hud-unitframe-targetoftarget-portraiton-bar-rage",
FOCUS = "ui-hud-unitframe-targetoftarget-portraiton-bar-focus",
ENERGY = "ui-hud-unitframe-targetoftarget-portraiton-bar-energy",
RUNIC_POWER = "ui-hud-unitframe-targetoftarget-portraiton-bar-runicpower",
}
local function updateTargetOfTarget()
if not UnitExists("targettarget") then
return
end
local maximum = UnitHealthMax("targettarget")
local fraction = 0
if maximum and maximum > 0 then
fraction = UnitHealth("targettarget") / maximum
end
ForeverUI.SetAtlasFill(totHealth, "ui-hud-unitframe-targetoftarget-portraiton-bar-health", fraction)
local powerType, powerToken = UnitPowerType("targettarget")
local powerMax = UnitPowerMax("targettarget", powerType)
local powerFraction = 0
if powerMax and powerMax > 0 then
powerFraction = UnitPower("targettarget", powerType) / powerMax
end
ForeverUI.SetAtlasFill(totPower, TOT_POWER[powerToken or ""] or TOT_POWER.MANA, powerFraction)
totName:SetText(UnitName("targettarget"))
SetPortraitTexture(totPortrait, "targettarget")
end
tot:SetScript("OnEvent", function(_self, event, unit)
if event == "PLAYER_TARGET_CHANGED" or unit == "target" then
updateTargetOfTarget()
end
end)
tot:RegisterEvent("PLAYER_TARGET_CHANGED")
tot:RegisterEvent("UNIT_TARGET")
-- "targettarget" gets no unit event in 3.3.5 (the client's own frame polls it every frame,
-- TargetofTarget_Update as OnUpdate): polled while shown, five times a second
tot:SetScript("OnUpdate", function(self, elapsed)
self.elapsed = (self.elapsed or 0) + elapsed
if self.elapsed >= 0.2 then
self.elapsed = 0
updateTargetOfTarget()
end
end)
if RegisterUnitWatch then
RegisterUnitWatch(tot)
end
tot.portrait = totPortrait
tot.healthFill = totHealth
tot.powerFill = totPower
tot.art = totArt
tot.nameText = totName
ForeverUI.TargetFrame = frame
ForeverUI.Layout.Register(frame, "targetframe", L.TARGETFRAME_EDIT_LABEL, "TOPLEFT", "TOPLEFT", 250, -10)