Files
moon-well-ui/data/addon/ForeverUI/TargetFrame.lua
T
Adrestie 59d9b1565e ForeverUI : cadres d'unité de camelot 1.60.1.70450
Camelot 70450 donne une variante c60 à toutes les barres et à tous les
voiles des cadres d'unité, et réimplante leur planche (uiunitframec60,
256 x 512 -> 1024 x 512). Les planches uiunitframec60, uipartyframec60 et
uiroguecombopointsc60 de la 70450 (BGRA non compressé) remplacent ou
rejoignent celles de l'atelier ; UIAtlas_04 prend leurs régions
(feuilles 12 à 14), et les noms du groupe quittent UIAtlas_06.

- Joueur : vie (87, -43), ressource (87, -61), nom centré de 116 en
  (149, -37), voile de menace centré ; ordre combat, liste de haine, repos.
- Cible : barres identiques pour les créatures négligeables, vie (21, -41),
  ressource (17, -60), bandeau (-82, -26), nom centré, voile (-1, 1) ;
  cadre 194 x 68 (négligeable 163 x 57) par l'atlas.
- Insigne JcJ du joueur et de la cible : plus de cercle, l'icône seule à
  l'échelle 0,8, environ 7 px plus bas.
- Points de combo : l'arc de camelot (cinq points autour du portrait de la
  cible, rayon 39,6, de -10° par pas de 21°, depuis 2° avec l'insigne JcJ,
  remplis par le haut) remplace le ComboFrame du client.
- Cible de la cible et familier : vie (45, -17,5), ressource (43, -29,5),
  nom centré.
- Groupe : vie (47, -20), ressource (45, -31), nom (48, -7) de 55, rôle
  (-5, -7), voile (1, -1), statut (-1, -1) ; vie vide pour un membre
  déconnecté et mort.

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

727 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)
local healthFill = frame:CreateTexture(nil, "BORDER")
healthFill:SetPoint("TOPLEFT", 21, -41)
local powerFill = frame:CreateTexture(nil, "BORDER")
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, "BORDER")
totHealth:SetPoint("TOPLEFT", 45, -17.5)
local totPower = tot:CreateTexture(nil, "BORDER")
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)