Files
benilla-octo/crates/benilla/assets/ui/UnitFrames.xml
T
SamandClaude Fable 5 2ad0043523 benilla: initial public release
A from-scratch World of Warcraft 1.12.1 client in Rust + Bevy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:27:01 +07:00

959 lines
56 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<Ui>
<!--
benilla's player + target unit frames (decision 0068 slice-1 game shell), RE-SKINNED to the
real 1.12 UI-TargetingFrame art + geometry (dressing arc, decision 0084). The geometry below
is QUOTED from the real FrameXML (scratchpad ref-PlayerFrame.xml, ref-TargetFrame.xml; line
citations inline), turned onto our own snapshot → Unit* → event data flow. The player and
target are horizontal MIRRORS of one shared art (Interface\TargetingFrame\UI-TargetingFrame):
the player samples it flipped (portrait window on the LEFT), the target un-flipped (window on
the RIGHT). The mirror is a `<TexCoords left> right`; it reaches the vertex buffer un-normalized
via the engine's UvRect (see crates/benilla/src/ui_pass.rs).
LAYERING (matches the ref's structure). The draw order must be trough < bars < ring art < text:
the frame's metal well-edges overlap the bar edges so the bars read RECESSED, not pasted on top.
The ref gets this with a child `TargetFrameTextureFrame` (ring art) and `SetFrameLevel(bar,
textureFrame-1)` to push the bars beneath it. Our z-model tie-breaks a `(strata, level)` bucket by
frame *insertion* order (crates/benilla-ui/src/order.rs), so we get the same result structurally
and without SetFrameLevel: the parent holds trough + portrait (+ target's name plate) in
BACKGROUND; the `$parentHealthBar`/`$parentPowerBar` StatusBars come next; then
`$parentTextureFrame` (232×100, filling the parent) holds the ring art (BACKGROUND) + name/level/dead text
(OVERLAY), declared LAST so it draws over the bar edges and the portrait. The ring's art is a THIN
band with transparent corners (not an opaque surround), so the square is hidden by the portrait
itself — set via SetPortraitToTexture, masked to its inscribed circle (the client's portraits are
always round: a model render or a circular-cropped icon), so no corner reaches the ring's gap. The
text lives under the TextureFrame, so its globals carry the `…TextureFrame…` name prefix (OnLoad).
The shared black `…Background` trough sits behind the unfilled bar portion.
DATA FLOW UNCHANGED (pin: re-anchor + re-skin, don't rewrite the eventing): BenillaUnitFrame_*
handlers, the power-color table, hide-on-nonexistent-unit. Names resolve asynchronously through
the name-query cache (UNIT_NAME_UPDATE repaints). A powerless unit (maxPower 0) hides its power
bar, as the real frames do. The target frame's "DEAD" text shows when the target's health hits
zero (ref TargetFrame_CheckDead, ref-TargetFrame.lua l.220-228).
DEVIATIONS (noted, not bugs):
- Portrait: the live model bake (SetPortraitTexture(region, unit) → crates/benilla/src/portrait.rs),
the modern high-res 2D take on the ref's low-res model snapshot (director's call; wow-re
portrait RE §5-verified). Circle-cut at draw (ui_quad.wgsl, the ref's stencil shape); the
TemporaryPortrait stand-in shows while a model streams in; the bake poses a fresh
instance at Stand through the model's authored portrait camera, helms/shoulders/held
items riding their bones (portrait/).
- Bar value FontStrings (ref-PlayerFrame.xml l.79-96) are omitted — the real client hides them
unless the statusText CVar is set; we match the default look.
- TargetFrameNameBackground (the level plate) is faction-tinted (TargetFrame_CheckFaction,
ref-TargetFrame.lua l.144-191). A PLAYER-controlled target takes the player branch (red when
mutually hostile, else blue — a friendly player is BLUE, never the reaction green); an NPC
takes the reaction swatch, resolved from the same faction-template / reputation decode the
selection ring uses (ui_unit.rs → UnitState.reaction → UnitReaction). DEFERRED (no PvP/tap
wire): the player branch's two middle legs — yellow "attackable but not hostile" (l.158) and
green "assist + PvP-flagged" (l.163) — and the tapped-by-other grey (l.176); they collapse
into red/blue, correct for every same-/opposite-faction player. The ref gates the player
branch on UnitPlayerControlled (which also covers pets); we resolve its player half via
UnitIsPlayer, so a player-controlled PET takes the NPC reaction branch until that flag lands.
- The classification border swap (elite/rare art, ref-TargetFrame.lua l.205-218) is a named
gap: the creature `rank` DOES surface now (the query's record caches it — names.rs, the
tooltip's rank word and the plate's world-boss skull read it), but UnitClassification is
not yet bound and the elite/rare border art swap is unbuilt.
- The level slot runs the full CheckLevel law (ref-TargetFrame.lua l.119-142, transcribed in
Update below): corpse or "can't tell" (UnitLevel ≤ 0) → the HighLevelTexture skull;
otherwise the number, GetDifficultyColor-tinted when attackable, gold when not.
- OUT (no wire / future arcs): PVP/leader/master/rest/attack icons, group indicator, dropdowns,
hit indicators, raid target icon, playtime, target-of-target.
TARGET AURA ROWS (0255's deferred slice): 5 buffs / 16 debuffs under the frame, geometry +
law QUOTED from ref-TargetFrame.xml (button templates l.26-87, instances l.293-481) and
TargetDebuffButton_Update (ref-TargetFrame.lua l.263-387): friend ⇒ buff row first, else
debuffs first and buffs below row 2; icons 21px shrinking to 17 when the debuff count
reaches the wrap; dispel-tinted UI-Debuff-Overlays border; stack count above 1; NO timers
(the 1.12 wire carries no duration for another unit — byte-verified, decision 0257 B6). The
list UnitBuff/UnitDebuff enumerate for the target is the app's straight descriptor read,
ascending raw slot within the half — the verified other-unit law (0257) — so icons sit in
server slot order, not insertion order, exactly like the reference. The target-of-target
branches take the not-shown leg (wrap 6, buffs under Debuff7) until a ToT frame exists.
-->
<Script><![CDATA[
-- The client's PowerBarColor values, keyed by the UnitPowerType token.
BENILLA_POWER_COLORS = {
MANA = { r = 0.0, g = 0.0, b = 1.0 },
RAGE = { r = 1.0, g = 0.0, b = 0.0 },
FOCUS = { r = 1.0, g = 0.5, b = 0.25 },
ENERGY = { r = 1.0, g = 1.0, b = 0.0 },
HAPPINESS = { r = 0.0, g = 1.0, b = 1.0 },
}
-- The client's UnitReactionColor table (ref-TargetFrame.lua l.6-14), indexed by
-- UnitReaction 1..8 (hated..exalted): 1-2 red, 3 orange, 4 neutral yellow, 5-7 green. The
-- name-plate reaction tint (TargetFrame_CheckFaction) reads it — this is what makes a neutral
-- beast's plate yellow, a hostile's red, a friendly's green.
BENILLA_REACTION_COLORS = {
{ r = 1.0, g = 0.0, b = 0.0 },
{ r = 1.0, g = 0.0, b = 0.0 },
{ r = 1.0, g = 0.5, b = 0.0 },
{ r = 1.0, g = 1.0, b = 0.0 },
{ r = 0.0, g = 1.0, b = 0.0 },
{ r = 0.0, g = 1.0, b = 0.0 },
{ r = 0.0, g = 1.0, b = 0.0 },
}
-- Wire an instance to its unit token (called from each frame's OnLoad), cache its child
-- widgets, register the event set, and do the first paint. deadText is target-only
-- (getglobal returns nil for the player, which has no DEAD region) — guarded at use.
function BenillaUnitFrame_OnLoad(frame, unit)
frame.unit = unit
local name = frame:GetName()
frame.healthBar = getglobal(name .. "HealthBar")
frame.powerBar = getglobal(name .. "PowerBar")
-- The name/level/dead text live inside $parentTextureFrame (so they draw over the ring art
-- that draws over the bars), so their global names carry the TextureFrame prefix.
local tf = name .. "TextureFrame"
frame.nameText = getglobal(tf .. "NameText")
frame.levelText = getglobal(tf .. "LevelText")
frame.deadText = getglobal(tf .. "DeadText")
-- Target-only (the "??" skull on the level slot); getglobal returns nil for the
-- player, guarded at use.
frame.highLevel = getglobal(tf .. "HighLevelTexture")
-- Target-only (the level/name plate); getglobal returns nil for the player, guarded at use.
frame.nameBackground = getglobal(name .. "NameBackground")
-- The live model portrait (the real API's global, ref-UnitFrame.lua): bind the portrait
-- region to this frame's unit token; the app renders that unit's model off-screen and feeds
-- the bake back into the region (crates/benilla/src/portrait.rs). The binding is a token, so
-- one call suffices — the app keeps the image current per token (it re-bakes on target /
-- model change), which is why the ref's UNIT_PORTRAIT_UPDATE re-call isn't needed here (the
-- event joins the surface when addon parity wants it). Until a bake lands (no unit / model
-- still loading) the circle is simply empty.
frame.portrait = getglobal(name .. "Portrait")
SetPortraitTexture(frame.portrait, unit)
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("UNIT_HEALTH")
frame:RegisterEvent("UNIT_MAXHEALTH")
frame:RegisterEvent("UNIT_LEVEL")
frame:RegisterEvent("UNIT_POWER_UPDATE")
frame:RegisterEvent("UNIT_MAXPOWER")
frame:RegisterEvent("UNIT_DISPLAYPOWER")
frame:RegisterEvent("UNIT_NAME_UPDATE")
frame:RegisterEvent("PLAYER_TARGET_CHANGED")
-- The aura rows are target-only (ref TargetFrame_OnLoad registers UNIT_AURA).
if unit == "target" then
frame:RegisterEvent("UNIT_AURA")
end
-- The portrait hit indicator (decision 0576): only the player frame registers
-- UNIT_COMBAT in 1.12 (ref PlayerFrame_OnLoad — the target frame has no feedback
-- text). Ref: CombatFeedback_Initialize(PlayerHitIndicator, 30).
if unit == "player" then
frame.feedbackText = getglobal(tf .. "HitIndicator")
frame.feedbackFontHeight = 30
frame:RegisterEvent("UNIT_COMBAT")
end
-- A plain Frame isn't mouse-enabled by construction (only Button/EditBox are); enable it so
-- the hover tooltip fires (the real client's unit frames take the mouse to target/tooltip).
frame:EnableMouse(true)
BenillaUnitFrame_Update(frame)
end
-- Hover: the engine's SetUnit — the byte-verified unit line law (decision 0276: name/
-- subtitle/level composition/PvP/Skinnable + the health bar), then the ref's reaction
-- recolor of line 1 (ref UnitFrame_OnEnter l.68-76: the FrameXML half of the split).
-- The plate goes to the DEFAULT corner, not beside the frame — ref UnitFrame_OnEnter
-- l.56 is GameTooltip_SetDefaultAnchor(GameTooltip, this): unit-frame tooltips live at
-- the screen's bottom-right like world hovers. (The ref's newbie-tip fork and the
-- TOOLTIP_UPDATE_TIME re-poll are not ours: no newbie-tip option; the engine's health
-- watcher is the live half of the re-poll, and name/level don't drift mid-hover yet.)
function BenillaUnitFrame_OnEnter(frame)
local unit = frame.unit
if not UnitExists(unit) then return end
GameTooltip_SetDefaultAnchor(GameTooltip, frame)
if GameTooltip:SetUnit(unit) then
getglobal("GameTooltipTextLeft1"):SetTextColor(GameTooltip_UnitColor(unit))
end
end
-- ref UnitFrame_OnLeave l.88-93: the unit-frame tooltip FADES on leave (items Hide;
-- units fade — the same ramp the world mouseover uses).
function BenillaUnitFrame_OnLeave(frame)
GameTooltip:FadeOut()
end
function BenillaUnitFrame_OnEvent(frame)
-- UNIT_* events name their unit in arg1; ignore other units' updates.
if string.sub(event, 1, 5) == "UNIT_" and arg1 ~= frame.unit then
return
end
-- The hit-indicator feed (ref PlayerFrame_OnEvent's UNIT_COMBAT arm): no repaint —
-- the event carries feedback, not state.
if event == "UNIT_COMBAT" then
CombatFeedback_OnCombatEvent(frame, arg2, arg3, arg4, arg5)
return
end
BenillaUnitFrame_Update(frame)
end
-- ── Combat feedback: the portrait hit indicator (decision 0576) ──────────────────────
-- Ref CombatFeedback.lua, transcribed onto frame-param style (the ref reads `this`).
-- Timings/colors/size factors verbatim; the word strings are the shipped enUS
-- GlobalStrings values, hardcoded like the rest of our enUS-only data.
COMBATFEEDBACK_FADEINTIME = 0.2
COMBATFEEDBACK_HOLDTIME = 0.7
COMBATFEEDBACK_FADEOUTTIME = 0.3
CombatFeedbackText = {
INTERRUPT = "Interrupt",
MISS = "Miss",
RESIST = "Resist",
DODGE = "Dodge",
PARRY = "Parry",
BLOCK = "Block",
EVADE = "Evade",
IMMUNE = "Immune",
DEFLECT = "Deflect",
ABSORB = "Absorb",
REFLECT = "Reflect",
}
function CombatFeedback_OnCombatEvent(frame, event, flags, amount, type)
local feedbackText = frame.feedbackText
if not feedbackText then return end
local fontHeight = frame.feedbackFontHeight
local text = ""
local r, g, b = 1.0, 1.0, 1.0
if event == "IMMUNE" then
fontHeight = fontHeight * 0.5
text = CombatFeedbackText[event]
elseif event == "WOUND" then
if amount ~= 0 then
if flags == "CRITICAL" or flags == "CRUSHING" then
fontHeight = fontHeight * 1.5
elseif flags == "GLANCING" then
fontHeight = fontHeight * 0.75
end
-- type > 0 = a spell school: the number reads yellow, physical white.
if type > 0 then
r, g, b = 1.0, 1.0, 0.0
end
text = amount
elseif flags == "ABSORB" then
fontHeight = fontHeight * 0.75
text = CombatFeedbackText["ABSORB"]
elseif flags == "BLOCK" then
fontHeight = fontHeight * 0.75
text = CombatFeedbackText["BLOCK"]
elseif flags == "RESIST" then
fontHeight = fontHeight * 0.75
text = CombatFeedbackText["RESIST"]
else
text = CombatFeedbackText["MISS"]
end
elseif event == "BLOCK" then
fontHeight = fontHeight * 0.75
text = CombatFeedbackText[event]
elseif event == "HEAL" then
text = amount
r, g, b = 0.0, 1.0, 0.0
if flags == "CRITICAL" then
fontHeight = fontHeight * 1.5
end
elseif event == "ENERGIZE" then
text = amount
r, g, b = 0.41, 0.8, 0.94
if flags == "CRITICAL" then
fontHeight = fontHeight * 1.5
end
else
text = CombatFeedbackText[event]
end
frame.feedbackStartTime = GetTime()
feedbackText:SetTextHeight(fontHeight)
feedbackText:SetText(text)
feedbackText:SetTextColor(r, g, b)
feedbackText:SetAlpha(0.0)
feedbackText:Show()
end
-- The fade envelope (ref CombatFeedback_OnUpdate): 0.2 s ramp in, 0.7 s hold, 0.3 s
-- ramp out, then Hide. GetTime-based like the ref — no per-tick accumulation.
function CombatFeedback_OnUpdate(frame)
local feedbackText = frame.feedbackText
if not (feedbackText and feedbackText:IsVisible()) then return end
local elapsedTime = GetTime() - frame.feedbackStartTime
local fadeInTime = COMBATFEEDBACK_FADEINTIME
if elapsedTime < fadeInTime then
feedbackText:SetAlpha(elapsedTime / fadeInTime)
return
end
local holdTime = COMBATFEEDBACK_HOLDTIME
if elapsedTime < fadeInTime + holdTime then
feedbackText:SetAlpha(1.0)
return
end
local fadeOutTime = COMBATFEEDBACK_FADEOUTTIME
if elapsedTime < fadeInTime + holdTime + fadeOutTime then
feedbackText:SetAlpha(1.0 - (elapsedTime - holdTime - fadeInTime) / fadeOutTime)
return
end
feedbackText:Hide()
end
-- Pull the unit's current state and repaint. A non-existent unit hides the whole frame.
function BenillaUnitFrame_Update(frame)
local unit = frame.unit
if not UnitExists(unit) then
frame:Hide()
return
end
frame:Show()
frame.healthBar:SetMinMaxValues(0, UnitHealthMax(unit))
frame.healthBar:SetValue(UnitHealth(unit))
local maxPower = UnitPowerMax(unit)
if maxPower > 0 then
frame.powerBar:Show()
local _, token = UnitPowerType(unit)
local c = BENILLA_POWER_COLORS[token] or BENILLA_POWER_COLORS.MANA
frame.powerBar:SetStatusBarColor(c.r, c.g, c.b)
frame.powerBar:SetMinMaxValues(0, maxPower)
frame.powerBar:SetValue(UnitPower(unit))
else
frame.powerBar:Hide()
end
frame.nameText:SetText(UnitName(unit) or "Unknown")
-- The level slot (TargetFrame_CheckLevel, ref-TargetFrame.lua l.119-142): a corpse
-- target or one whose level "can't be told" (UnitLevel ≤ 0 — the −1 return for a
-- world boss / a hostile 10+ levels up) shows the skull instead of the number; a
-- told level is difficulty-colored when the target is attackable (GetDifficultyColor,
-- the same con law the V-plate paints its level with) and plain gold when not.
-- The player frame has no skull region and shows its own level, always gold.
local level = UnitLevel(unit)
if frame.highLevel then
if UnitIsCorpse(unit) or level <= 0 then
frame.levelText:Hide()
frame.highLevel:Show()
else
frame.levelText:SetText(level)
if UnitCanAttack("player", unit) then
local color = GetDifficultyColor(level)
frame.levelText:SetVertexColor(color.r, color.g, color.b)
else
frame.levelText:SetVertexColor(1.0, 0.82, 0.0)
end
frame.levelText:Show()
frame.highLevel:Hide()
end
else
frame.levelText:SetText(level)
end
-- Tint the name/level plate (target only; TargetFrame_CheckFaction, ref-TargetFrame.lua
-- l.144-191). A PLAYER-CONTROLLED target takes the player branch (l.145-174): its plate is
-- RED when we're mutually hostile (UnitReactionColor[2]) and BLUE otherwise — a friendly,
-- non-attackable player is blue, NOT the reaction green. Only an NPC takes the reaction
-- swatch (l.179-190): the resolved UnitReactionColor (neutral → yellow), or blue when the
-- reaction can't be told (nil). The ref's two middle player legs — yellow "attackable but
-- not hostile" (l.158) and green "assist + PvP-flagged" (l.163) — plus the tapped-by-other
-- grey (l.176) need the PvP/tap wire we don't carry; until it lands they collapse into
-- red/blue, which is the right answer for every same-/opposite-faction player we can
-- resolve. (The ref gates on UnitPlayerControlled, which also covers pets; UnitIsPlayer is
-- the player half we can resolve — see the header deviation.)
if frame.nameBackground then
if UnitIsPlayer(unit) then
if UnitIsEnemy(unit, "player") then
local c = BENILLA_REACTION_COLORS[2]
frame.nameBackground:SetVertexColor(c.r, c.g, c.b)
else
frame.nameBackground:SetVertexColor(0.0, 0.0, 1.0)
end
else
local reaction = UnitReaction(unit, "player")
local c = reaction and BENILLA_REACTION_COLORS[reaction]
if c then
frame.nameBackground:SetVertexColor(c.r, c.g, c.b)
else
frame.nameBackground:SetVertexColor(0.0, 0.0, 1.0)
end
end
end
-- DEAD text (target only): shown at zero health (ref TargetFrame_CheckDead). Blanked
-- via SetText when alive — a site that predates region Show/Hide (region.rs has the
-- real pair now, the level slot above uses it); equivalent either way.
if frame.deadText then
if UnitHealth(unit) <= 0 then
frame.deadText:SetText("DEAD")
else
frame.deadText:SetText("")
end
end
-- The aura rows (target only) — the ref's TargetFrame_Update →
-- TargetDebuffButton_Update: repainted on PLAYER_TARGET_CHANGED and UNIT_AURA alike.
if unit == "target" then
BenillaTargetAuras_Update()
end
end
-- ref TargetFrame.lua l.2-3.
MAX_TARGET_DEBUFFS = 16
MAX_TARGET_BUFFS = 5
-- TargetDebuffButton_Update (ref-TargetFrame.lua l.263-387), transcribed onto the Era API
-- (decision 0255: UnitBuff returns name,icon,count,debuffType,… — the 1.12 original returned
-- the texture first). Only called with a live target (BenillaUnitFrame_Update guards).
-- DebuffTypeColor is defined by BuffFrame.xml, which loads AFTER this file — safe, because
-- this function only runs on events, never at load (no target exists at load).
function BenillaTargetAuras_Update()
local button
local numBuffs = 0
for i = 1, MAX_TARGET_BUFFS do
local name, icon = UnitBuff("target", i)
button = getglobal("BenillaTargetFrameBuff" .. i)
if name then
getglobal(button:GetName() .. "Icon"):SetTexture(icon or BENILLA_FALLBACK_ICON)
button:Show()
numBuffs = numBuffs + 1
else
button:Hide()
end
end
local numDebuffs = 0
for i = 1, MAX_TARGET_DEBUFFS do
local name, icon, count, debuffType = UnitDebuff("target", i)
button = getglobal("BenillaTargetFrameDebuff" .. i)
if name then
getglobal(button:GetName() .. "Icon"):SetTexture(icon or BENILLA_FALLBACK_ICON)
local color = DebuffTypeColor[debuffType] or DebuffTypeColor["none"]
local countText = getglobal(button:GetName() .. "Count")
if count and count > 1 then
countText:SetText(count)
else
countText:SetText("")
end
getglobal(button:GetName() .. "Border"):SetVertexColor(color.r, color.g, color.b)
button:Show()
numDebuffs = numDebuffs + 1
else
button:Hide()
end
end
-- The dynamic anchors (ref l.312-331). No target-of-target frame exists yet, so its
-- branches take the not-shown leg: wrap 6, buffs open under Debuff7.
local targetofTarget = false
local debuffWrap = 6
if UnitIsFriend("player", "target") then
BenillaTargetFrameBuff1:SetPoint("TOPLEFT", "BenillaTargetFrame", "BOTTOMLEFT", 5, 32)
BenillaTargetFrameDebuff1:SetPoint("TOPLEFT", "BenillaTargetFrameBuff1", "BOTTOMLEFT", 0, -2)
else
BenillaTargetFrameDebuff1:SetPoint("TOPLEFT", "BenillaTargetFrame", "BOTTOMLEFT", 5, 32)
BenillaTargetFrameBuff1:SetPoint("TOPLEFT", "BenillaTargetFrameDebuff7", "BOTTOMLEFT", 0, -2)
end
-- Shrink when the debuffs would overlap the frame (ref l.340-347): 21→17 icons,
-- 23→19 borders.
local debuffSize, debuffFrameSize
if (targetofTarget and numBuffs == 5) or numDebuffs >= debuffWrap then
debuffSize = 17
debuffFrameSize = 19
else
debuffSize = 21
debuffFrameSize = 23
end
for i = 1, 5 do
button = getglobal("BenillaTargetFrameBuff" .. i)
button:SetWidth(debuffSize)
button:SetHeight(debuffSize)
end
-- The ref resizes only the FIRST ROW's debuffs (l.358-368) — rows 2+ stay 17px always.
for i = 1, 6 do
button = getglobal("BenillaTargetFrameDebuff" .. i)
local border = getglobal(button:GetName() .. "Border")
border:SetWidth(debuffFrameSize)
border:SetHeight(debuffFrameSize)
button:SetWidth(debuffSize)
button:SetHeight(debuffSize)
end
-- Re-seat the wrap points (ref l.370-385) — constant with wrap pinned at 6, kept
-- verbatim so the ToT wrap-5 leg drops in unchanged.
getglobal("BenillaTargetFrameDebuff" .. debuffWrap):ClearAllPoints()
getglobal("BenillaTargetFrameDebuff" .. debuffWrap):SetPoint("LEFT", getglobal("BenillaTargetFrameDebuff" .. (debuffWrap - 1)), "RIGHT", 3, 0)
getglobal("BenillaTargetFrameDebuff" .. (debuffWrap + 1)):ClearAllPoints()
getglobal("BenillaTargetFrameDebuff" .. (debuffWrap + 1)):SetPoint("TOPLEFT", "BenillaTargetFrameDebuff1", "BOTTOMLEFT", 0, -2)
getglobal("BenillaTargetFrameDebuff" .. (debuffWrap + 2)):ClearAllPoints()
getglobal("BenillaTargetFrameDebuff" .. (debuffWrap + 2)):SetPoint("LEFT", getglobal("BenillaTargetFrameDebuff" .. (debuffWrap + 1)), "RIGHT", 3, 0)
if debuffWrap == 5 then
BenillaTargetFrameDebuff11:ClearAllPoints()
BenillaTargetFrameDebuff11:SetPoint("TOPLEFT", "BenillaTargetFrameDebuff6", "BOTTOMLEFT", 0, -2)
else
BenillaTargetFrameDebuff11:ClearAllPoints()
BenillaTargetFrameDebuff11:SetPoint("LEFT", "BenillaTargetFrameDebuff10", "RIGHT", 3, 0)
end
end
-- The aura hover (ref TargetDebuffButtonTemplate/TargetBuffButtonTemplate OnEnter,
-- l.60-62/l.79-81): the aura tooltip, seated BOTTOMRIGHT of the button.
function BenillaTargetAuraButton_OnEnter(button, harmful)
if not GameTooltip then return end -- engine tests load file subsets
GameTooltip:SetOwner(button, "ANCHOR_BOTTOMRIGHT", 15, -25)
if harmful then
GameTooltip:SetUnitDebuff("target", button:GetID())
else
GameTooltip:SetUnitBuff("target", button:GetID())
end
end
-- Left-click the player frame to target yourself (ref PlayerFrame_OnClick, the LeftButton
-- branch: an item on the cursor auto-equips, otherwise TargetUnit("player")); right-click
-- opens the SELF menu (ref's ToggleDropDownMenu at (106, 27), decision 0434 phase 5). The
-- ref's frame is a Button; ours is a mouse-enabled Frame, which fires OnClick on release
-- with arg1 = the button. The self-target itself is the byte-verified way the client
-- selects its own unit; the world-model click is a separate path, deliberately left out
-- (director's call).
function BenillaPlayerFrame_OnClick(button)
if button == "RightButton" then
ToggleDropDownMenu(1, nil, PlayerFrameDropDown, "BenillaPlayerFrame", 106, 27)
return
end
if button ~= "LeftButton" then return end
if CursorHasItem() then
AutoEquipCursorItem()
else
TargetUnit("player")
end
end
-- The right-click dropdowns (decision 0434 phase 5; ref TargetFrame.lua l.431-455,
-- PlayerFrame.lua l.235-241): MENU displayMode over the shared kit (UIDropDownMenu.xml),
-- the menus themselves from UnitPopup.xml. The menu pick is the ref's law: self → SELF,
-- party member → PARTY, other player → PLAYER, NPC → the bare raid-target icon list.
function TargetFrameDropDown_OnLoad()
UIDropDownMenu_Initialize(this, TargetFrameDropDown_Initialize, "MENU")
end
function TargetFrameDropDown_Initialize()
local menu
local name
if UnitIsUnit("target", "player") then
menu = "SELF"
elseif UnitIsPlayer("target") then
if UnitInParty("target") then
menu = "PARTY"
else
menu = "PLAYER"
end
else
-- The ref checks UnitIsUnit("target", "pet") → PET before this branch; no "pet"
-- token exists until the pet slice (phase 4).
menu = "RAID_TARGET_ICON"
name = RAID_TARGET_ICON
end
if menu then
UnitPopup_ShowMenu(TargetFrameDropDown, menu, "target", name)
end
end
function PlayerFrameDropDown_OnLoad()
UIDropDownMenu_Initialize(this, PlayerFrameDropDown_Initialize, "MENU")
end
function PlayerFrameDropDown_Initialize()
UnitPopup_ShowMenu(PlayerFrameDropDown, "SELF", "player")
end
-- ref TargetFrame_OnClick: right-click opens the unit menu at (120, 10). The ref's
-- LeftButton branch (SpellTargetUnit / DropItemOnUnit) rides the spell-cursor and
-- item-drop slices — a bare left-click is a no-op on the ref too.
function BenillaTargetFrame_OnClick(button)
if button == "RightButton" then
ToggleDropDownMenu(1, nil, TargetFrameDropDown, "BenillaTargetFrame", 120, 10)
end
end
]]></Script>
<!--
PLAYER FRAME (ref-PlayerFrame.xml). Button 232×100 at UIParent TOPLEFT (-19,-4) (l.4-13); the
negative x trims the art's transparent left padding, seating the visible frame flush at the
screen's top-left, exactly as the real client does (our UIParent is the screen).
-->
<!-- frameStrata BACKGROUND is the ref's own (`PlayerFrame.xml` l.4) — the unit frames live
BELOW the MEDIUM panel windows on purpose, and the player frame shares the top-left corner
with the left-slot panel. See the note on PartyFrame.xml's template (decision 0597) for why
the default MEDIUM makes a unit frame's bar children draw through a window's background. -->
<Frame name="BenillaPlayerFrame" frameStrata="BACKGROUND">
<Size><AbsDimension x="232" y="100"/></Size>
<Anchors>
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="-19" y="-4"/></Offset></Anchor>
</Anchors>
<Layers>
<Layer level="BACKGROUND">
<!-- The bar trough: 119×41 black a=0.5 behind both bars (ref l.20-32). -->
<Texture name="$parentBackground">
<Size><AbsDimension x="119" y="41"/></Size>
<Anchors>
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="106" y="-22"/></Offset></Anchor>
</Anchors>
<Color r="0" g="0" b="0" a="0.5"/>
</Texture>
<!-- Portrait 64×64 TOPLEFT (42,-12) (ref l.35-46, moved ARTWORK→BACKGROUND so the ring
art draws over it). Texture set in OnLoad via SetPortraitToTexture (circle-masked). -->
<Texture name="$parentPortrait">
<Size><AbsDimension x="64" y="64"/></Size>
<Anchors>
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="42" y="-12"/></Offset></Anchor>
</Anchors>
</Texture>
</Layer>
<!-- The ring art + name/level text live in $parentTextureFrame below (declared AFTER the
bars) so they draw OVER the bar edges — the recessed-bar look. See the LAYERING note. -->
</Layers>
<Frames>
<!-- Health bar 119×12 TOPLEFT (106,-41), UI-StatusBar, green (ref l.288-315). -->
<StatusBar name="$parentHealthBar">
<Size><AbsDimension x="119" y="12"/></Size>
<Anchors>
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="106" y="-41"/></Offset></Anchor>
</Anchors>
<BarTexture file="Interface\TargetingFrame\UI-StatusBar"/>
<BarColor r="0.0" g="1.0" b="0.0"/>
</StatusBar>
<!-- Power bar 119×12 TOPLEFT (106,-52), UI-StatusBar; color set at runtime (ref l.316-341). -->
<StatusBar name="$parentPowerBar">
<Size><AbsDimension x="119" y="12"/></Size>
<Anchors>
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="106" y="-52"/></Offset></Anchor>
</Anchors>
<BarTexture file="Interface\TargetingFrame\UI-StatusBar"/>
<BarColor r="0.0" g="0.0" b="1.0"/>
</StatusBar>
<!-- The ring art + text, over the bars (ref TargetFrameTextureFrame). 232×100 fills the
parent; declared after the StatusBars so it draws over their edges — our z-model's
insertion-order equivalent of the ref's SetFrameLevel(bar, textureFrame-1). -->
<Frame name="$parentTextureFrame">
<Size><AbsDimension x="232" y="100"/></Size>
<Anchors>
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
</Anchors>
<Layers>
<Layer level="BACKGROUND">
<!-- Ring art, MIRRORED: TexCoords left=1.0 right=0.09375 top=0 bottom=0.78125 (ref l.55-57). -->
<Texture name="$parentTexture" file="Interface\TargetingFrame\UI-TargetingFrame">
<Size><AbsDimension x="232" y="100"/></Size>
<Anchors>
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
</Anchors>
<TexCoords left="1.0" right="0.09375" top="0" bottom="0.78125"/>
</Texture>
</Layer>
<Layer level="OVERLAY">
<!-- Name 100×10 CENTER (50,19) (ref l.58-69). -->
<FontString name="$parentNameText" inherits="GameFontNormalSmall" text="Unknown">
<Size><AbsDimension x="100" y="10"/></Size>
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="50" y="19"/></Offset></Anchor>
</Anchors>
</FontString>
<!-- Level number CENTER (-63,-16) (ref l.70-78). -->
<FontString name="$parentLevelText" inherits="GameFontNormalSmall">
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="-63" y="-16"/></Offset></Anchor>
</Anchors>
</FontString>
<!-- The portrait hit indicator (ref PlayerHitIndicator, decision 0576):
CENTER at (73,-42) from the frame's TOPLEFT — the portrait circle's
center. Hidden until a UNIT_COMBAT event lands; CombatFeedback drives
text/size/color and the fade. -->
<FontString name="$parentHitIndicator" hidden="true" inherits="NumberFontNormalHuge">
<Anchors>
<Anchor point="CENTER" relativePoint="TOPLEFT"><Offset><AbsDimension x="73" y="-42"/></Offset></Anchor>
</Anchors>
</FontString>
</Layer>
</Layers>
</Frame>
</Frames>
<Scripts>
<OnLoad><![CDATA[BenillaUnitFrame_OnLoad(this, "player")]]></OnLoad>
<OnEvent><![CDATA[BenillaUnitFrame_OnEvent(this)]]></OnEvent>
<OnUpdate><![CDATA[CombatFeedback_OnUpdate(this)]]></OnUpdate>
<OnEnter><![CDATA[BenillaUnitFrame_OnEnter(this)]]></OnEnter>
<OnLeave><![CDATA[BenillaUnitFrame_OnLeave(this)]]></OnLeave>
<OnClick><![CDATA[BenillaPlayerFrame_OnClick(arg1)]]></OnClick>
</Scripts>
</Frame>
<!-- The SELF-menu dropdown host (ref $parentDropDown, PlayerFrame.xml): hidden state-holder
only — the list anchors to the player frame by name, never to this. -->
<Frame name="PlayerFrameDropDown" parent="BenillaPlayerFrame" inherits="UIDropDownMenuTemplate" hidden="true">
<Size><AbsDimension x="10" y="10"/></Size>
<Anchors><Anchor point="TOP"><Offset><AbsDimension x="-20" y="-10"/></Offset></Anchor></Anchors>
<Scripts>
<OnLoad>PlayerFrameDropDown_OnLoad()</OnLoad>
</Scripts>
</Frame>
<!-- ref TargetDebuffButtonTemplate (l.26-68): 17×17 icon, 19×19 UI-Debuff-Overlays border
(dispel-tinted at update), bottom-right stack count. Sizes are re-set every update by the
shrink law (first row only, verbatim ref quirk). -->
<Button name="BenillaTargetDebuffTemplate" virtual="true">
<Size><AbsDimension x="17" y="17"/></Size>
<Layers>
<Layer level="ARTWORK">
<Texture name="$parentIcon" setAllPoints="true"/>
</Layer>
<Layer level="OVERLAY">
<Texture name="$parentBorder" file="Interface\Buttons\UI-Debuff-Overlays">
<Size><AbsDimension x="19" y="19"/></Size>
<Anchors><Anchor point="CENTER"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor></Anchors>
<TexCoords left="0.296875" right="0.5703125" top="0" bottom="0.515625"/>
</Texture>
<FontString name="$parentCount" inherits="NumberFontNormalSmall">
<Anchors><Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-1" y="0"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
</Layers>
<Scripts>
<OnEnter>BenillaTargetAuraButton_OnEnter(this, true)</OnEnter>
<OnLeave>GameTooltip:Hide()</OnLeave>
</Scripts>
</Button>
<!-- ref TargetBuffButtonTemplate (l.69-87): 21×21 icon, no border, no count. -->
<Button name="BenillaTargetBuffTemplate" virtual="true">
<Size><AbsDimension x="21" y="21"/></Size>
<Layers>
<Layer level="ARTWORK">
<Texture name="$parentIcon" setAllPoints="true"/>
</Layer>
</Layers>
<Scripts>
<OnEnter>BenillaTargetAuraButton_OnEnter(this, false)</OnEnter>
<OnLeave>GameTooltip:Hide()</OnLeave>
</Scripts>
</Button>
<!--
TARGET FRAME (ref-TargetFrame.xml). Button 232×100 at UIParent TOPLEFT (250,-4) (l.88-98). The
un-mirrored twin of the player frame: portrait window on the RIGHT, bars anchored TOPRIGHT.
-->
<!-- hidden="true" is a deliberate deviation from the ref (whose TargetFrame starts shown and
gets its no-target Hide behind the loading screen, before audio is live): our OnLoad
Update runs with audio up, so starting shown would fire OnHide's lost-target sound at
every login. Steady-state behavior is identical — Update Shows/Hides on target changes. -->
<!-- frameStrata LOW is the ref's own (`TargetFrame.xml` l.88) — one above the player frame's
BACKGROUND, still below the MEDIUM panels. Same reasoning as PlayerFrame above. -->
<Frame name="BenillaTargetFrame" hidden="true" frameStrata="LOW">
<Size><AbsDimension x="232" y="100"/></Size>
<Anchors>
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="250" y="-4"/></Offset></Anchor>
</Anchors>
<Layers>
<Layer level="BACKGROUND">
<!-- Bar trough 119×41 black a=0.5 TOPRIGHT (-106,-22) (ref l.104-116). -->
<Texture name="$parentBackground">
<Size><AbsDimension x="119" y="41"/></Size>
<Anchors>
<Anchor point="TOPRIGHT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-106" y="-22"/></Offset></Anchor>
</Anchors>
<Color r="0" g="0" b="0" a="0.5"/>
</Texture>
<!-- The level/name plate 119×19 TOPRIGHT (-106,-22) (ref l.117-128) — target only,
untinted (see the header deviation on faction tinting). -->
<Texture name="$parentNameBackground" file="Interface\TargetingFrame\UI-TargetingFrame-LevelBackground">
<Size><AbsDimension x="119" y="19"/></Size>
<Anchors>
<Anchor point="TOPRIGHT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-106" y="-22"/></Offset></Anchor>
</Anchors>
</Texture>
<!-- Portrait 64×64 TOPRIGHT (-42,-12) (ref l.131-142, BORDER→BACKGROUND for the same
ring-over-portrait layering as the player). -->
<Texture name="$parentPortrait">
<Size><AbsDimension x="64" y="64"/></Size>
<Anchors>
<Anchor point="TOPRIGHT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-42" y="-12"/></Offset></Anchor>
</Anchors>
</Texture>
</Layer>
<!-- The ring art + name/level/dead text live in $parentTextureFrame below (declared AFTER
the bars) so they draw OVER the bar edges — the recessed-bar look. -->
</Layers>
<Frames>
<!-- Health bar 119×12 TOPRIGHT (-106,-41), UI-StatusBar, green (ref l.252-273). -->
<StatusBar name="$parentHealthBar">
<Size><AbsDimension x="119" y="12"/></Size>
<Anchors>
<Anchor point="TOPRIGHT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-106" y="-41"/></Offset></Anchor>
</Anchors>
<BarTexture file="Interface\TargetingFrame\UI-StatusBar"/>
<BarColor r="0.0" g="1.0" b="0.0"/>
</StatusBar>
<!-- Power bar 119×12 TOPRIGHT (-106,-52), UI-StatusBar; color set at runtime (ref l.274-292). -->
<StatusBar name="$parentPowerBar">
<Size><AbsDimension x="119" y="12"/></Size>
<Anchors>
<Anchor point="TOPRIGHT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-106" y="-52"/></Offset></Anchor>
</Anchors>
<BarTexture file="Interface\TargetingFrame\UI-StatusBar"/>
<BarColor r="0.0" g="0.0" b="1.0"/>
</StatusBar>
<!-- The ring art + text, over the bars (ref TargetFrameTextureFrame); declared after the
StatusBars so it draws over their edges (our z-model's insertion-order equivalent of
the ref's SetFrameLevel(bar, textureFrame-1)). -->
<Frame name="$parentTextureFrame">
<Size><AbsDimension x="232" y="100"/></Size>
<Anchors>
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
</Anchors>
<Layers>
<Layer level="BACKGROUND">
<!-- Ring art, UN-mirrored: TexCoords left=0.09375 right=1.0 (ref l.146-151). -->
<Texture name="$parentTexture" file="Interface\TargetingFrame\UI-TargetingFrame">
<Size><AbsDimension x="232" y="100"/></Size>
<Anchors>
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
</Anchors>
<TexCoords left="0.09375" right="1.0" top="0" bottom="0.78125"/>
</Texture>
</Layer>
<Layer level="OVERLAY">
<!-- Name 100×10 CENTER (-50,19) (ref l.152-163). -->
<FontString name="$parentNameText" inherits="GameFontNormalSmall" text="Unknown">
<Size><AbsDimension x="100" y="10"/></Size>
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="-50" y="19"/></Offset></Anchor>
</Anchors>
</FontString>
<!-- Level number CENTER (63,-16) (ref l.164-172). -->
<FontString name="$parentLevelText" inherits="GameFontNormalSmall">
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="63" y="-16"/></Offset></Anchor>
</Anchors>
</FontString>
<!-- The "too high to tell" skull, 16×16 on the level slot (ref
TargetHighLevelTexture, l.184-195): shown for a corpse target or
UnitLevel ≤ 0, replacing the number (TargetFrame_CheckLevel). -->
<Texture name="$parentHighLevelTexture" file="Interface\TargetingFrame\UI-TargetingFrame-Skull">
<Size><AbsDimension x="16" y="16"/></Size>
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="63" y="-16"/></Offset></Anchor>
</Anchors>
</Texture>
<!-- DEAD, CENTER (-50,3), shown at zero health (ref l.173-181). Blank while alive. -->
<FontString name="$parentDeadText" inherits="GameFontNormalSmall">
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="-50" y="3"/></Offset></Anchor>
</Anchors>
</FontString>
</Layer>
</Layers>
</Frame>
<!-- The aura rows (ref l.293-481, names re-based on $parent). Static anchors are the
ref's; the update re-seats Debuff1/6/7/8/11 and Buff1 every pass (the friend swap +
the wrap law). Row 1 debuffs 1-6 (+3 gaps), row 2 debuffs 7-16, buffs below. -->
<Button name="$parentDebuff1" inherits="BenillaTargetDebuffTemplate" id="1">
<Anchors><Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="5" y="32"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff2" inherits="BenillaTargetDebuffTemplate" id="2">
<Anchors><Anchor point="LEFT" relativeTo="$parentDebuff1" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff3" inherits="BenillaTargetDebuffTemplate" id="3">
<Anchors><Anchor point="LEFT" relativeTo="$parentDebuff2" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff4" inherits="BenillaTargetDebuffTemplate" id="4">
<Anchors><Anchor point="LEFT" relativeTo="$parentDebuff3" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff5" inherits="BenillaTargetDebuffTemplate" id="5">
<Anchors><Anchor point="LEFT" relativeTo="$parentDebuff4" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff6" inherits="BenillaTargetDebuffTemplate" id="6">
<Anchors><Anchor point="LEFT" relativeTo="$parentDebuff5" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff7" inherits="BenillaTargetDebuffTemplate" id="7">
<Anchors><Anchor point="TOPLEFT" relativeTo="$parentDebuff1" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-3"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff8" inherits="BenillaTargetDebuffTemplate" id="8">
<Anchors><Anchor point="LEFT" relativeTo="$parentDebuff7" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff9" inherits="BenillaTargetDebuffTemplate" id="9">
<Anchors><Anchor point="LEFT" relativeTo="$parentDebuff8" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff10" inherits="BenillaTargetDebuffTemplate" id="10">
<Anchors><Anchor point="LEFT" relativeTo="$parentDebuff9" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff11" inherits="BenillaTargetDebuffTemplate" id="11">
<Anchors><Anchor point="LEFT" relativeTo="$parentDebuff10" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff12" inherits="BenillaTargetDebuffTemplate" id="12">
<Anchors><Anchor point="LEFT" relativeTo="$parentDebuff11" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff13" inherits="BenillaTargetDebuffTemplate" id="13">
<Anchors><Anchor point="LEFT" relativeTo="$parentDebuff12" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff14" inherits="BenillaTargetDebuffTemplate" id="14">
<Anchors><Anchor point="LEFT" relativeTo="$parentDebuff13" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff15" inherits="BenillaTargetDebuffTemplate" id="15">
<Anchors><Anchor point="LEFT" relativeTo="$parentDebuff14" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentDebuff16" inherits="BenillaTargetDebuffTemplate" id="16">
<Anchors><Anchor point="LEFT" relativeTo="$parentDebuff15" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentBuff1" inherits="BenillaTargetBuffTemplate" id="1">
<Anchors><Anchor point="TOPLEFT" relativeTo="$parentDebuff1" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentBuff2" inherits="BenillaTargetBuffTemplate" id="2">
<Anchors><Anchor point="LEFT" relativeTo="$parentBuff1" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentBuff3" inherits="BenillaTargetBuffTemplate" id="3">
<Anchors><Anchor point="LEFT" relativeTo="$parentBuff2" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentBuff4" inherits="BenillaTargetBuffTemplate" id="4">
<Anchors><Anchor point="LEFT" relativeTo="$parentBuff3" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="$parentBuff5" inherits="BenillaTargetBuffTemplate" id="5">
<Anchors><Anchor point="LEFT" relativeTo="$parentBuff4" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</Button>
</Frames>
<Scripts>
<OnLoad><![CDATA[BenillaUnitFrame_OnLoad(this, "target")]]></OnLoad>
<OnEvent><![CDATA[BenillaUnitFrame_OnEvent(this)]]></OnEvent>
<OnEnter><![CDATA[BenillaUnitFrame_OnEnter(this)]]></OnEnter>
<OnLeave><![CDATA[BenillaUnitFrame_OnLeave(this)]]></OnLeave>
<!-- The select/deselect pair rides the frame's visibility, exactly the ref
(TargetFrame_OnShow/OnHide, TargetFrame.lua l.104-117). In 5875 data all three
select kits resolve to the same iSelectTarget.wav — the reaction branch is
data-plumbing fidelity, not an audible difference. -->
<OnShow><![CDATA[
if UnitIsEnemy("target", "player") then
PlaySound("igCreatureAggroSelect")
elseif UnitIsFriend("player", "target") then
PlaySound("igCharacterNPCSelect")
else
PlaySound("igCreatureNeutralSelect")
end
]]></OnShow>
<!-- ref TargetFrame_OnHide: the lost-target sound + close any open unit menu (a
cleared target must not leave its menu acting on a stale unit). -->
<OnHide><![CDATA[
PlaySound("INTERFACESOUND_LOSTTARGETUNIT")
CloseDropDownMenus()
]]></OnHide>
<OnClick><![CDATA[BenillaTargetFrame_OnClick(arg1)]]></OnClick>
</Scripts>
</Frame>
<!-- The target-menu dropdown host (ref $parentDropDown, TargetFrame.xml): hidden
state-holder only, like the player's above. -->
<Frame name="TargetFrameDropDown" parent="BenillaTargetFrame" inherits="UIDropDownMenuTemplate" hidden="true">
<Size><AbsDimension x="10" y="10"/></Size>
<Anchors><Anchor point="TOP"><Offset><AbsDimension x="-20" y="-10"/></Offset></Anchor></Anchors>
<Scripts>
<OnLoad>TargetFrameDropDown_OnLoad()</OnLoad>
</Scripts>
</Frame>
</Ui>