- The main menu bar's two empty recesses fill. The micro menu — the eight arched buttons: character, spellbook, talents, quest log, socials, world map, main menu, help — sits in the dark panel between the page arrows and the bags, transcribed from the reference's own layout: the character button wears your portrait, a button holds itself down while its panel is up, and the talent button appears at level 10. The latency meter fills the one slot the bar art leaves transparent for exactly it, tinted green/yellow/red on the reference's 300/600 ms thresholds and drawn UNDER the bar art, seen through its window, the way 1.12 actually does it. The socials and main-menu buttons find their panels one snapshot later; help still waits. - The UI explains itself on hover: 1.12 ships its detailed two-line tooltips ON — a FrameXML default, not the opt-in extra it is often remembered as — so the micro buttons, your own portrait, the XP bar, the five bag buttons and the ping meter all answer a hover with the reference's own explanations. - The PvP flag, end to end: crossed swords on the player, target and party frames, driven by the two descriptor bits the wire actually carries, and `/pvp` speaks — the yellow toast plus the system chat line, the OFF sentence being the one that warns the flag lingers for five minutes. It really does linger: turning it off waits out the server's timer, which looks like a bug and is the reference. - Quest markers stopped freezing. The grey `!` over a questgiver above your level goes gold the moment you ding, an NPC re-entering view is asked afresh instead of wearing a session-old answer, and the marker drops when the questgiver bit goes off — the re-ask triggers mirror the reference's own set, plus one named addition (your own level changing). - Water and heights: swimming downhill no longer bounces between swim and fall ten times a second — the surface line is a two-sided constraint, so the swimmer settles with the river instead of being left behind by it. And collision height is per-unit now, read from the unit's own model: a gnome used to swim with a third of a yard of water over her head because five depth lines were quoting a human's number. - A round of pixel honesty in text and chat: the newest chat line no longer wears a wider drop shadow than every line above it (shadows move in whole pixels now), keeps its descenders, and hover boxes stop painting over the chat body. The chat input's history browse survives programmatic edits, a failed script chunk warns instead of vanishing, and recall deepens to 64 lines. Idle animation got honest too: a model's idle is Stand resolved through its own lookup table — never file order, which had the duel flag bobbing on its spawn clip — and an armed idle is culled on the box its animation authors, not the bind pose, so the flag stops vanishing at most camera angles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1092 lines
64 KiB
XML
1092 lines
64 KiB
XML
<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, yellow attackable-not-hostile, green when we can assist them but they are
|
||
PvP-flagged, else blue — a player never reads 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). The player branch's
|
||
two middle legs — yellow "attackable but not hostile" (l.158) and green "assist +
|
||
PvP-flagged" (l.163) — landed with the PvP wire (decision 0646); only the tapped-by-other
|
||
grey (l.176) is still deferred, on the tap wire. 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.
|
||
- The PVP icon landed with decision 0646 (the three-branch law in BenillaUnitFrame_UpdatePvP).
|
||
Its `PlayerPVPIconHitArea` did NOT, deliberately: the ref's hit area draws nothing at the
|
||
default settings — GameTooltip_AddNewbieTip with SHOW_NEWBIE_TIPS off and noNormalText=1
|
||
(which is exactly how it is called) shows no tooltip at all — while an enableMouse frame
|
||
over the portrait would eat the player frame's own hover and right-click. Zero pixels for a
|
||
real regression is not a trade; it comes back with the newbie-tip cvar if that ever ships.
|
||
RE-VERIFIED in 0652, both legs: ref-GameTooltip.lua l.107-123 takes the `not noNormalText`
|
||
branch when tips are off (so `noNormalText=1` → nothing), and "newbie" does not appear
|
||
anywhere in WoW.exe's strings, so SHOW_NEWBIE_TIPS has no cvar producer to be on.
|
||
- OUT (no wire / future arcs): leader/master/rest/attack icons, group indicator,
|
||
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 right-click-menu explanations the detailed-tooltip fork shows in place of the unit
|
||
-- lines (BenillaUnitFrame_OnEnter below) — ref GlobalStrings.lua l.2993/3081/2754/2755
|
||
-- (enUS), fallback form: the real localized GlobalStrings.lua runs before this file at
|
||
-- runtime (ui_script/mod.rs), so these only fill in for the tests. CombatText.xml's shape.
|
||
PARTY_OPTIONS_LABEL = PARTY_OPTIONS_LABEL or "Party Options"
|
||
PLAYER_OPTIONS_LABEL = PLAYER_OPTIONS_LABEL or "Player Options"
|
||
NEWBIE_TOOLTIP_PARTYOPTIONS = NEWBIE_TOOLTIP_PARTYOPTIONS or "If you belong to a party, you can right-click your portrait to bring up the Party Options menu. This menu will allow you to quit the party or see what looting rules the party is using. A party leader can add or remove party members, as well as change the party's looting rules."
|
||
NEWBIE_TOOLTIP_PLAYEROPTIONS = NEWBIE_TOOLTIP_PLAYEROPTIONS or "Right-click to bring up special commands for interacting with another player. You can inspect their equipment, issue a party invite, initiate a trade, or challenge a player to a duel. A group leader can promote or remove that player from the group."
|
||
|
||
-- 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)
|
||
-- The PvP flag icon (decision 0646), under the TextureFrame like the ring art it
|
||
-- draws over — see BenillaUnitFrame_UpdatePvP.
|
||
frame.pvpIcon = getglobal(tf .. "PVPIcon")
|
||
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")
|
||
-- The PvP icon's repaint wire (decision 0646 §2) — in the reference exactly the three
|
||
-- frames that draw the icon register it (PlayerFrame, TargetFrame, PartyMemberFrame)
|
||
-- and nothing else does.
|
||
frame:RegisterEvent("UNIT_FACTION")
|
||
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 TOOLTIP_UPDATE_TIME re-poll is not
|
||
-- ours: the engine's health watcher is the live half of it, and name/level don't drift
|
||
-- mid-hover yet.)
|
||
--
|
||
-- The DETAILED-TOOLTIP FORK (ref UnitFrame_OnEnter l.58-67, director-approved 0663) is the
|
||
-- surprising half: with tips on — 1.12's default, decision 0661 — the frame explains its
|
||
-- RIGHT-CLICK MENU instead of the unit, and returns BEFORE SetUnit, so the health/level
|
||
-- lines never render at all. Your own portrait always takes it; another player's takes it
|
||
-- whenever they're your target. The ref excepts PartyMemberFrame1-4 by name; ours are a
|
||
-- separate file with their own OnEnter (PartyFrame.xml), so there is nothing to except here.
|
||
--
|
||
-- One divergence, named: the ref gates the second branch on UnitPlayerControlled("target"),
|
||
-- which is true for a player OR a player's pet/charm. This engine has no such binding and no
|
||
-- flag to build one from — UnitIsPlayer is the nearest honest read, so a charmed mob or
|
||
-- another player's pet falls through to the unit tooltip where the reference would explain
|
||
-- the menu. The player case, which is the one anybody hovers, is exact.
|
||
function BenillaUnitFrame_OnEnter(frame)
|
||
local unit = frame.unit
|
||
if not UnitExists(unit) then return end
|
||
GameTooltip_SetDefaultAnchor(GameTooltip, frame)
|
||
if SHOW_NEWBIE_TIPS == "1" then
|
||
if frame:GetName() == "BenillaPlayerFrame" then
|
||
GameTooltip_AddNewbieTip(frame, PARTY_OPTIONS_LABEL, 1.0, 1.0, 1.0,
|
||
NEWBIE_TOOLTIP_PARTYOPTIONS)
|
||
return
|
||
elseif UnitIsPlayer("target") and not UnitIsUnit("target", "player")
|
||
and not UnitIsUnit("target", "pet") then
|
||
GameTooltip_AddNewbieTip(frame, PLAYER_OPTIONS_LABEL, 1.0, 1.0, 1.0,
|
||
NEWBIE_TOOLTIP_PLAYEROPTIONS)
|
||
return
|
||
end
|
||
end
|
||
if GameTooltip:SetUnit(unit) then
|
||
getglobal("GameTooltipTextLeft1"):SetTextColor(GameTooltip_UnitColor(unit))
|
||
end
|
||
end
|
||
|
||
-- ref UnitFrame_OnLeave l.84-88: the fade is the tips-OFF branch. With detailed tips on the
|
||
-- plate HIDES outright — the ref's own pairing, and the right one: an explanation that
|
||
-- lingers on a ramp reads as a stuck tooltip, where a unit readout fading is the client's
|
||
-- signature. (Items Hide, units fade — that split still holds for the world mouseover.)
|
||
function BenillaUnitFrame_OnLeave(frame)
|
||
if SHOW_NEWBIE_TIPS == "1" then
|
||
GameTooltip:Hide()
|
||
else
|
||
GameTooltip:FadeOut()
|
||
end
|
||
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)
|
||
-- The player's flag-change sound (decision 0646 §2/§4). The reference plays
|
||
-- igPVPUpdate inside PlayerFrame_UpdatePvPStatus's two show branches, which it only
|
||
-- ever reaches from UNIT_FACTION; our repaint runs from the shared Update on every
|
||
-- event, so the sound rides the (edge-fired) event instead of the repaint — one
|
||
-- sound per real flag change, which is what the reference audibly does.
|
||
if event == "UNIT_FACTION" and frame.unit == "player" and frame.pvpShown then
|
||
PlaySound("igPVPUpdate")
|
||
end
|
||
end
|
||
|
||
-- The PvP flag icon (decision 0646 §4). The reference writes this law twice with
|
||
-- identical branches — PlayerFrame_UpdatePvPStatus (ref-PlayerFrame.lua l.55-79) and the
|
||
-- tail of TargetFrame_CheckFaction (ref-TargetFrame.lua l.193-202) — so it lives once
|
||
-- here. Branch ORDER is the reference's and it is load-bearing: free-for-all wins
|
||
-- outright (an FFA-flagged player shows the FFA skull even while ordinarily flagged), and
|
||
-- the faction leg needs BOTH a resolvable side and the flag. `frame.pvpShown` is what the
|
||
-- caller sounds off.
|
||
function BenillaUnitFrame_UpdatePvP(frame)
|
||
local icon = frame.pvpIcon
|
||
if not icon then return end
|
||
local unit = frame.unit
|
||
local texture
|
||
if UnitIsPVPFreeForAll(unit) then
|
||
texture = "Interface\\TargetingFrame\\UI-PVP-FFA"
|
||
else
|
||
local factionGroup = UnitFactionGroup(unit)
|
||
if factionGroup and UnitIsPVP(unit) then
|
||
texture = "Interface\\TargetingFrame\\UI-PVP-" .. factionGroup
|
||
end
|
||
end
|
||
frame.pvpShown = texture ~= nil
|
||
if texture then
|
||
icon:SetTexture(texture)
|
||
icon:Show()
|
||
else
|
||
icon:Hide()
|
||
end
|
||
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 words come from the GLOBALS, exactly as the ref
|
||
-- builds this table (CombatFeedback.lua l.6-17: CombatFeedbackText["MISS"] = TEXT(MISS)),
|
||
-- so a localized install shows its own — the app runs the real GlobalStrings.lua off the
|
||
-- patch chain before this file (ui_script/mod.rs). The enUS literals are the fallback for
|
||
-- the test harnesses, which load no patch chain.
|
||
COMBATFEEDBACK_FADEINTIME = 0.2
|
||
COMBATFEEDBACK_HOLDTIME = 0.7
|
||
COMBATFEEDBACK_FADEOUTTIME = 0.3
|
||
|
||
CombatFeedbackText = {
|
||
INTERRUPT = INTERRUPT or "Interrupt",
|
||
MISS = MISS or "Miss",
|
||
RESIST = RESIST or "Resist",
|
||
DODGE = DODGE or "Dodge",
|
||
PARRY = PARRY or "Parry",
|
||
BLOCK = BLOCK or "Block",
|
||
EVADE = EVADE or "Evade",
|
||
IMMUNE = IMMUNE or "Immune",
|
||
DEFLECT = DEFLECT or "Deflect",
|
||
ABSORB = ABSORB or "Absorb",
|
||
REFLECT = REFLECT or "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); 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) — a player never reads the
|
||
-- reaction green, which is why the two paths differ at all.
|
||
-- The player branch runs the ref's full ladder now that the
|
||
-- PvP wire carries it (decision 0646 §4): hostile red, else attackable-but-not-hostile
|
||
-- yellow (l.158), else "we can assist them but they're PvP-flagged" green (l.163), else
|
||
-- blue. Only the tapped-by-other grey (l.176) is still deferred — it needs the tap wire.
|
||
-- The ref's first leg tests mutual attackability (UnitCanAttack both ways); our
|
||
-- UnitCanAttack carries only the player→unit direction, so UnitIsEnemy (reaction ≤ 2)
|
||
-- stands in for it — the same substitution this branch has always made. (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
|
||
local c
|
||
if UnitIsEnemy(unit, "player") then
|
||
c = BENILLA_REACTION_COLORS[2]
|
||
elseif UnitCanAttack("player", unit) then
|
||
c = BENILLA_REACTION_COLORS[4]
|
||
elseif UnitIsPVP(unit) then
|
||
c = BENILLA_REACTION_COLORS[6]
|
||
end
|
||
if c then
|
||
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 PvP flag icon, on both frames (decision 0646 §4).
|
||
BenillaUnitFrame_UpdatePvP(frame)
|
||
|
||
-- 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="ARTWORK">
|
||
<!-- The PvP flag icon, 64×64 TOPLEFT (18,-20) (ref PlayerPVPIcon,
|
||
ref-PlayerFrame.xml l.124-135) — ARTWORK over the BACKGROUND ring art,
|
||
like the ref's. Texture chosen at runtime (decision 0646 §4); the ref's
|
||
PlayerPVPIconHitArea is deliberately OUT (see the header). -->
|
||
<Texture name="$parentPVPIcon" hidden="true">
|
||
<Size><AbsDimension x="64" y="64"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="18" y="-20"/></Offset></Anchor>
|
||
</Anchors>
|
||
</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="ARTWORK">
|
||
<!-- The PvP flag icon, 64×64 TOPRIGHT (3,-20) (ref TargetPVPIcon,
|
||
ref-TargetFrame.xml l.208-219) — the player's icon mirrored to the
|
||
frame's other edge, same law (decision 0646 §4). -->
|
||
<Texture name="$parentPVPIcon" hidden="true">
|
||
<Size><AbsDimension x="64" y="64"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPRIGHT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="3" y="-20"/></Offset></Anchor>
|
||
</Anchors>
|
||
</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>
|