- the combat log's last eight wire bodies decode — PARTYKILLLOG, SPELLINSTAKILLLOG, PROCRESIST, SPELLORDAMAGE_IMMUNE, SPELLDISPELLOG, DISPEL_FAILED, ENCHANTMENTLOG, SPELLLOGEXECUTE — each layout read off vmangos's own writer and pinned by a byte-exact golden (DISPEL_FAILED carries no count, the list runs to the end of the body; SPELLLOGEXECUTE's POWER_DRAIN row is (amount, power, multiplier), verified at the client's bytes as well as the server's); with the eleven chat types that were waiting on them, 44 chat types become 55 — 29 sentence families, the death pair and the aura rows riding descriptor diffs because 1.12 has no "unit died" opcode, plus the six trailers ((glancing), (30 blocked), (12 absorbed) and kin) that had been missing from every family; also corrected: the durability-death line was going to the red error frame with hard-coded English, and SMSG_SPELLNONMELEEDAMAGELOG lacked its split-damage branch and its blocked wording
- PlayerModel is its own frame kind: SetUnit, RefreshUnit and SetRotation were published on Model, but they belong to the <PlayerModel> method table (0x84f1fc), and the client's lookup chains derived → base only, so a plain <Model> can never reach them — they got there from a strings scan read as ownership, and a strings hit answers whether a name EXISTS, not who owns it (SetUnit's one pooled string is referenced by the PlayerModel and GameTooltip tables and by no Model entry); the new PlayerModel → Model → Frame → Region chain unblocks CreateFrame("PlayerModel"), which is how pfUI builds every unit-frame portrait — and through pfUI, pfQuest and ShaguDPS
- a nil where a number goes is four shapes, not one law: a census of all 408 widget-registrar entries found 62 numeric positions that validate-or-raise, 43 that gate then stage an alternate, 64 that silently coerce to 0.0 and 5 that gate then store nothing — so SetMaxLetters(nil) completes and stores 0, "no limit" (the argument COUNT is checked exactly, the type is not), and the backdrop colour setters are asymmetric on purpose: a nil r/g/b channel is 0.0 but a nil alpha is OPAQUE, and reading every nil as 0.0 would have turned those borders transparent; both setters now also quantize ×255 into the packed 0xAARRGGBB byte quad the client actually stores, so 0.09 reads back as 23/255
- two more addon-killers: LIGHTYELLOW_FONT_COLOR_CODE was the one missing line of Fonts.xml's seven-line colour block (aux-addon concatenates it at file scope and died on its own first line), and the guard now enumerates the whole block l.4-20 instead of the names an earlier round had noticed absent; and the unit frames publish the reference's names — 1.12 has no *PowerBar anywhere, every bar is ManaBar and the colour table is ManaBarColor (8 of the top-20 addons read it by name), so ManaBarColor is the published table our own bars index into, with 22 more regions renamed to the reference's own
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2655 lines
159 KiB
XML
2655 lines
159 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, and we do the same, in BenillaUnitFrame_OnLoad:
|
||
the parent holds trough + portrait (+ target's name plate) in BACKGROUND; the
|
||
`$parentHealthBar`/`$parentManaBar` StatusBars sit at the PARENT's frame level; and
|
||
`$parentTextureFrame` (232×100, filling the parent) holds the ring art (BACKGROUND) + name/level/dead text
|
||
(OVERLAY) one level ABOVE them, so it draws over the bar edges and the portrait.
|
||
|
||
That SetFrameLevel is load-bearing, and this file used to do without it. Until decision 0884
|
||
the bars were held under the ring art by DECLARING `$parentTextureFrame` after them, leaning on
|
||
the old draw key's insertion-order tie-break between siblings at one `(strata, level)`. 0884
|
||
pinned the real law at the binary: the draw LAYER is bucket-wide and outranks the frame, so
|
||
siblings interleave by layer and the bars' ARTWORK fills painted straight over the TextureFrame's
|
||
BACKGROUND ring art (the frames read as pasted-on slabs — director report, 2026-08-02). Frame
|
||
level is the one term above the layer, which is exactly why the reference spends a level here.
|
||
Do not "simplify" this back into declaration order. 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, but its GLOBAL names are the reference's flat ones
|
||
(`PlayerName`, `TargetDeadText`, …), not `$parent`-derived — see OnLoad's `short` prefix.
|
||
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) LANDED with
|
||
decision 0782 — UnitClassification is bound and TargetFrame_CheckClassification
|
||
below runs the ref's branch table. Two things worth knowing at this altitude. The rank
|
||
`UnitClassification` reads is the client's GATED rank (`0x605620`): a pet or charmed unit
|
||
reports rank 0, so an enslaved elite wears the plain border, and because the tooltip's
|
||
ELITE/BOSS word and UnitLevel's world-boss skull read the same getter, the gate lives at
|
||
the app's single write (ui_unit.rs enrich_unit), not per reader. And UNIT_CLASSIFICATION_
|
||
CHANGED is the repaint wire, edge-fired on that rank — without it the border would only be
|
||
right on re-target, since Update is otherwise the sole caller.
|
||
- The level slot runs the full CheckLevel law (ref-TargetFrame.lua l.119-142, re-expressed 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.
|
||
- The rest/attack state icons landed with decision 1082, the hit indicator with 0576, and
|
||
the leader / master-looter pair with report B317 — the wire those three waited on has
|
||
existed since 0434/0453, and the list below is what is still genuinely unbuilt.
|
||
- The target-of-target frame landed with decision 1576 — its own block at the bottom of
|
||
this file, on the `"targettarget"` unit token the app now feeds.
|
||
- OUT (no wire / future arcs): group indicator, raid target icon, playtime.
|
||
|
||
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 are live since 1576: with that frame shown the rows wrap at 5 instead of 6 and
|
||
the buffs open under the second debuff row, because the frame stands where a six-icon row
|
||
would reach.
|
||
-->
|
||
<!-- ── TargetBuffButtonTemplate (ref TargetFrame.xml l.9-27, VERBATIM) ──────────────────────
|
||
A reference template addons INHERIT and we did not declare. `CT_UnitFrames` builds its own
|
||
`CT_UnitFrames_BuffButtonTemplate` on top of it (CT_TargetFrame.xml:4), and an unresolved
|
||
`inherits=` is SILENT — no load error, no session error, nothing in any column. The addon
|
||
simply gets a button with no size, no icon region and no tooltip scripts, and nothing
|
||
anywhere says so. That invisibility is why this row only ever appeared in the report's
|
||
missing-TEMPLATES ranking.
|
||
|
||
Transcribed verbatim because every piece of it already resolves here: `GameTooltip:SetOwner`
|
||
and `GameTooltip:SetUnitBuff` are both ours, and the icon is a plain `setAllPoints` texture.
|
||
The `$parentIcon` name matters — that is what a consumer getglobals to set the texture. -->
|
||
<Button name="TargetBuffButtonTemplate" virtual="true">
|
||
<Size><AbsDimension x="21" y="21"/></Size>
|
||
<Layers>
|
||
<Layer level="ARTWORK">
|
||
<Texture name="$parentIcon" setAllPoints="true"/>
|
||
</Layer>
|
||
</Layers>
|
||
<Scripts>
|
||
<OnEnter>
|
||
GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT", 15, -25);
|
||
GameTooltip:SetUnitBuff("target", this.id);
|
||
</OnEnter>
|
||
<OnLeave>
|
||
GameTooltip:Hide();
|
||
</OnLeave>
|
||
</Scripts>
|
||
</Button>
|
||
|
||
<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."
|
||
|
||
-- ── ManaBarColor (ref UnitFrame.lua l.1-6, verbatim) ────────────────────────────────
|
||
-- The reference's own table: keyed by the NUMERIC power type `UnitPowerType` returns, each
|
||
-- row carrying the `prefix` its bar's text is labelled with. **8 addons in the top-20 root
|
||
-- read `ManaBarColor` by name**, which is why it is the published one rather than a
|
||
-- benilla-private table — we used to keep the same five colours under
|
||
-- `BENILLA_POWER_COLORS`, keyed by token string and carrying no prefix, so every one of
|
||
-- those reads found nil.
|
||
--
|
||
-- One table, not two: the token map below is DERIVED from it rather than restated, so a
|
||
-- colour can never differ between what an addon reads and what our own bars paint.
|
||
ManaBarColor = {}
|
||
ManaBarColor[0] = { r = 0.00, g = 0.00, b = 1.00, prefix = MANA or "Mana" }
|
||
ManaBarColor[1] = { r = 1.00, g = 0.00, b = 0.00, prefix = RAGE_POINTS or "Rage" }
|
||
ManaBarColor[2] = { r = 1.00, g = 0.50, b = 0.25, prefix = FOCUS_POINTS or "Focus" }
|
||
ManaBarColor[3] = { r = 1.00, g = 1.00, b = 0.00, prefix = ENERGY_POINTS or "Energy" }
|
||
ManaBarColor[4] = { r = 0.00, g = 1.00, b = 1.00, prefix = HAPPINESS_POINTS or "Happiness" }
|
||
|
||
-- Our own bars look colours up by the TOKEN `UnitPowerType` also returns (its 2nd value),
|
||
-- so this maps token → the row above. Same objects, so `ManaBarColor` is the only source.
|
||
BENILLA_POWER_COLORS = {
|
||
MANA = ManaBarColor[0],
|
||
RAGE = ManaBarColor[1],
|
||
FOCUS = ManaBarColor[2],
|
||
ENERGY = ManaBarColor[3],
|
||
HAPPINESS = ManaBarColor[4],
|
||
}
|
||
|
||
-- ── The raid-target mark atlas (ref TargetFrame.lua l.460-463 + l.475-484) ──────────────
|
||
-- `Interface\TargetingFrame\UI-RaidTargetingIcons` is one 256x256 sheet of 4x4 64px cells,
|
||
-- in mark order: star, circle, diamond, triangle, moon, square, cross, skull. The helper
|
||
-- turns a 1..8 mark index into that cell's texcoords on a CALLER-SUPPLIED texture — it owns
|
||
-- no frame and reads no unit, which is why an addon can hand it a texture of its own.
|
||
--
|
||
-- Three corpus addons do, in two codebases (`Optional` and `oRA2` ship the same two files):
|
||
-- CustomNameplates options.lua:363 (its options preview)
|
||
-- Optional / oRA2 MainTank.lua:1216, PlayerTarget.lua:1472
|
||
-- We answer `GetRaidTargetIndex(unit)` already (0434 §6); this is the other half — what an
|
||
-- addon DRAWS with that number — and without it those lines raise while redrawing a mark.
|
||
--
|
||
-- KEPT WHOLE, INCLUDING THE REFERENCE'S OWN SLIP: the row is derived with
|
||
-- `/ RAID_TARGET_TEXTURE_ROWS` while the column uses `mod(.., RAID_TARGET_TEXTURE_COLUMNS)`.
|
||
-- Rows and columns are both 4, so the two agree for every index this sheet holds and the
|
||
-- output is right; "fixing" it to divide by COLUMNS computes the same coords today and would
|
||
-- quietly diverge from the client the day a caller changes a constant. These are globals in
|
||
-- the reference for exactly that reason — addons read them, and two of ours are addons that
|
||
-- draw their own marks.
|
||
RAID_TARGET_ICON_DIMENSION = 64
|
||
RAID_TARGET_TEXTURE_DIMENSION = 256
|
||
RAID_TARGET_TEXTURE_COLUMNS = 4
|
||
RAID_TARGET_TEXTURE_ROWS = 4
|
||
|
||
function SetRaidTargetIconTexture(texture, raidTargetIconIndex)
|
||
raidTargetIconIndex = raidTargetIconIndex - 1
|
||
local left, right, top, bottom
|
||
local coordIncrement = RAID_TARGET_ICON_DIMENSION / RAID_TARGET_TEXTURE_DIMENSION
|
||
left = mod(raidTargetIconIndex, RAID_TARGET_TEXTURE_COLUMNS) * coordIncrement
|
||
right = left + coordIncrement
|
||
top = floor(raidTargetIconIndex / RAID_TARGET_TEXTURE_ROWS) * coordIncrement
|
||
bottom = top + coordIncrement
|
||
texture:SetTexCoord(left, right, top, bottom)
|
||
end
|
||
|
||
-- 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.manabar = getglobal(name .. "ManaBar")
|
||
-- TWO prefixes, both the reference's own. The bars are named off the FRAME
|
||
-- (`PlayerFrameHealthBar`, ref-PlayerFrame.xml l.288) but the art, text and icons are
|
||
-- named off the UNIT (`PlayerName` l.58, `PlayerPortrait` l.35, `TargetDeadText`
|
||
-- ref-TargetFrame.xml l.173) — flat globals, which is what an addon reaches for.
|
||
-- `short` is the frame name minus its trailing "Frame".
|
||
local short = string.sub(name, 1, string.len(name) - 5)
|
||
-- Our ring art rides a $parentTextureFrame child (the reference nests two ANONYMOUS
|
||
-- frames there instead, ref-PlayerFrame.xml l.49-51), so the frame whose level the bars
|
||
-- are pushed under is still named off the frame.
|
||
local tf = name .. "TextureFrame"
|
||
-- The recessed-bar look, the reference's own mechanism (ref-TargetFrame.lua l.32-34):
|
||
-- push the bars one frame level BELOW the ring art's TextureFrame. Frame level is the
|
||
-- only key term above the draw layer (decision 0884), so this is the only thing that can
|
||
-- hold a BACKGROUND ring texture over an ARTWORK bar fill. See the LAYERING note up top.
|
||
local textureFrameLevel = getglobal(tf):GetFrameLevel()
|
||
frame.healthbar:SetFrameLevel(textureFrameLevel - 1)
|
||
frame.manabar:SetFrameLevel(textureFrameLevel - 1)
|
||
-- The on-bar numerals (TextStatusBar.xml, decision 1082; the switch that shows them
|
||
-- is 1140's Status Bar Text). Wiring is unconditional — a frame with no text regions
|
||
-- gets a no-op, since SetTextStatusBarText drops a nil text and UpdateTextString skips
|
||
-- a bar with no TextString — so the target frame is one FontString pair away from
|
||
-- having them the day its hover lands. `textLockable` is the REFERENCE's own split
|
||
-- and the only conditional here: 1.12 sets it on the player's two bars, the pet's two
|
||
-- and the XP bar, and on nothing else, which is exactly why Status Bar Text pins your
|
||
-- own numbers and your pet's but never the target's (decision 1143).
|
||
if TextStatusBar_Initialize then -- engine tests load file subsets
|
||
TextStatusBar_Initialize(frame.healthbar)
|
||
TextStatusBar_Initialize(frame.manabar)
|
||
SetTextStatusBarText(frame.healthbar, getglobal(name .. "HealthBarText"))
|
||
SetTextStatusBarText(frame.manabar, getglobal(name .. "ManaBarText"))
|
||
if unit == "player" then
|
||
frame.healthbar.textLockable = 1
|
||
frame.manabar.textLockable = 1
|
||
end
|
||
end
|
||
-- The hover seam (1146): who this bar belongs to, and what its click means.
|
||
local click = TargetFrame_OnClick
|
||
if unit == "player" then
|
||
click = PlayerFrame_OnClick
|
||
end
|
||
frame.healthbar.owner = frame
|
||
frame.manabar.owner = frame
|
||
frame.healthbar.ownerClick = click
|
||
frame.manabar.ownerClick = click
|
||
frame.nameText = getglobal(short .. "Name")
|
||
frame.levelText = getglobal(short .. "LevelText")
|
||
frame.deadText = getglobal(short .. "DeadText")
|
||
-- Target-only (the "??" skull on the level slot); getglobal returns nil for the
|
||
-- player, guarded at use.
|
||
frame.highLevel = getglobal(short .. "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(short .. "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(short .. "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 and the classification border are target-only (ref TargetFrame_OnLoad
|
||
-- registers UNIT_AURA and UNIT_CLASSIFICATION_CHANGED; PlayerFrame registers neither,
|
||
-- and there is no CheckClassification for the player — your own frame never wears a
|
||
-- dragon). The ring-art region is the one the border swap re-textures, so only the
|
||
-- target caches the handle: its presence is what gates the swap in Update, the same
|
||
-- nil-handle idiom highLevel/nameBackground use above.
|
||
if unit == "target" then
|
||
frame:RegisterEvent("UNIT_AURA")
|
||
frame:RegisterEvent("UNIT_CLASSIFICATION_CHANGED")
|
||
frame.frameTexture = getglobal(name .. "Texture")
|
||
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(short .. "HitIndicator")
|
||
frame.feedbackFontHeight = 30
|
||
frame:RegisterEvent("UNIT_COMBAT")
|
||
-- The status flash (ref PlayerFrame_OnLoad, decision 1082): the resting zzz /
|
||
-- auto-attack swords under the portrait, the gold/red ring, and the pulse
|
||
-- counters. Handles cached like pvpIcon above; their presence is what gates
|
||
-- PlayerFrame_UpdateStatus in the shared Update.
|
||
frame.statusTexture = getglobal(short .. "StatusTexture")
|
||
frame.attackBackground = getglobal(short .. "AttackBackground")
|
||
frame.restIcon = getglobal(short .. "RestIcon")
|
||
frame.attackIcon = getglobal(short .. "AttackIcon")
|
||
frame.statusGlow = getglobal(short .. "StatusGlow")
|
||
frame.restGlow = getglobal(short .. "RestGlow")
|
||
frame.attackGlow = getglobal(short .. "AttackGlow")
|
||
frame.statusCounter = 0
|
||
frame.statusSign = -1
|
||
frame.attackBackground:SetVertexColor(0.8, 0.1, 0.1)
|
||
frame.attackBackground:SetAlpha(0.4)
|
||
-- Its four wires (ref PlayerFrame_OnLoad's registrations): auto-attack toggling
|
||
-- (ENTER/LEAVE_COMBAT), the aggro list (REGEN_DISABLED/ENABLED), and the rest
|
||
-- flag (UPDATE_RESTING); PLAYER_ENTERING_WORLD is registered above for all units.
|
||
frame:RegisterEvent("PLAYER_ENTER_COMBAT")
|
||
frame:RegisterEvent("PLAYER_LEAVE_COMBAT")
|
||
frame:RegisterEvent("PLAYER_REGEN_DISABLED")
|
||
frame:RegisterEvent("PLAYER_REGEN_ENABLED")
|
||
frame:RegisterEvent("PLAYER_UPDATE_RESTING")
|
||
-- The leader / master-looter icons and their four group wires (ref
|
||
-- PlayerFrame_OnLoad's remaining registrations; report B317). Handle-gated in the
|
||
-- shared Update exactly like statusTexture above — the target frame declares
|
||
-- neither texture, so it never runs the law, and the party member frames run
|
||
-- their own (PartyFrame.xml, a different predicate: an INDEX comparison).
|
||
frame.leaderIcon = getglobal(short .. "LeaderIcon")
|
||
frame.masterIcon = getglobal(short .. "MasterIcon")
|
||
frame:RegisterEvent("PARTY_MEMBERS_CHANGED")
|
||
frame:RegisterEvent("PARTY_LEADER_CHANGED")
|
||
frame:RegisterEvent("PARTY_LOOT_METHOD_CHANGED")
|
||
frame:RegisterEvent("RAID_ROSTER_UPDATE")
|
||
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)
|
||
UnitFrame_Update()
|
||
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.
|
||
-- ref UnitFrame_OnEnter l.55-57, ahead of everything else: the ONLY lit/grey cursor split
|
||
-- over a UI element in 1.12, and it is authored here in Lua rather than in the engine
|
||
-- (wow-re item-target-cursor-and-dropitemonunit.md 4.3). Bag and paper-doll slots have no
|
||
-- such branch, which is why they show the ordinary pointer.
|
||
-- A BAR's own hover (decision 1146; ref TextStatusBar's OnEnter/OnLeave on a mouse-enabled
|
||
-- bar). Two jobs, and the second is the one a naive transcription drops: reveal the numbers
|
||
-- while the pointer is on the bar, then hand the hover ON to the frame underneath, because
|
||
-- a mouse-enabled child swallows the parent's OnEnter — and with it the unit tooltip AND
|
||
-- the cast-cursor split that only exists over unit frames. Same for the click: the ref
|
||
-- routes its bars' OnMouseUp straight back to the frame's own handler (PlayerFrame.xml
|
||
-- l.304-306), which is what keeps a bar click targeting.
|
||
--
|
||
-- `owner`/`ownerClick` are stamped at load rather than read from GetParent(), because the
|
||
-- pet's bars are siblings of its TextureFrame, not children of the frame they belong to.
|
||
function BenillaUnitFrameBar_OnEnter(bar)
|
||
if TextStatusBar_UpdateTextString then
|
||
TextStatusBar_UpdateTextString(bar)
|
||
ShowTextStatusBarText(bar)
|
||
end
|
||
if bar.owner and BenillaUnitFrame_OnEnter and bar.owner.unit then
|
||
BenillaUnitFrame_OnEnter(bar.owner)
|
||
end
|
||
end
|
||
|
||
function BenillaUnitFrameBar_OnLeave(bar)
|
||
if HideTextStatusBarText then
|
||
HideTextStatusBarText(bar)
|
||
end
|
||
if bar.owner and BenillaUnitFrame_OnLeave and bar.owner.unit then
|
||
BenillaUnitFrame_OnLeave(bar.owner)
|
||
end
|
||
end
|
||
|
||
function BenillaUnitFrameBar_OnMouseUp(bar, button)
|
||
if bar.ownerClick then
|
||
bar.ownerClick(button)
|
||
end
|
||
end
|
||
|
||
-- The reference's names for the two unit-frame hover hooks, `this`-shaped, delegating to
|
||
-- ours. `TipBuddy.lua:2770-2773` is the exact idiom this exists for:
|
||
-- originalUnitFrame_OnEnter = UnitFrame_OnEnter
|
||
-- function UnitFrame_OnEnter() originalUnitFrame_OnEnter() ... end
|
||
-- Against a missing global that captured nil and raised on the first hover; against a
|
||
-- RENAMED one it would have called ours with no frame. See ActionBar.xml for the full note.
|
||
function UnitFrame_OnEnter()
|
||
BenillaUnitFrame_OnEnter(this)
|
||
end
|
||
|
||
function UnitFrame_OnLeave()
|
||
BenillaUnitFrame_OnLeave(this)
|
||
end
|
||
|
||
function BenillaUnitFrame_OnEnter(frame)
|
||
local unit = frame.unit
|
||
if SpellIsTargeting() then
|
||
if SpellCanTargetUnit(unit) then
|
||
SetCursor("CAST_CURSOR")
|
||
else
|
||
SetCursor("CAST_ERROR_CURSOR")
|
||
end
|
||
end
|
||
if not UnitExists(unit) then return end
|
||
GameTooltip_SetDefaultAnchor(GameTooltip, frame)
|
||
if SHOW_NEWBIE_TIPS == "1" then
|
||
if frame:GetName() == "PlayerFrame" then
|
||
BenillaTooltip_AddNewbieTipFor(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
|
||
BenillaTooltip_AddNewbieTipFor(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)
|
||
-- ref UnitFrame_OnLeave l.86: leaving a unit frame while still targeting goes to the
|
||
-- GREY cast cursor, not to the base one — you have left the only thing the armed spell
|
||
-- could have bound.
|
||
if SpellIsTargeting() then SetCursor("CAST_ERROR_CURSOR") end
|
||
if SHOW_NEWBIE_TIPS == "1" then
|
||
GameTooltip:Hide()
|
||
else
|
||
GameTooltip:FadeOut()
|
||
end
|
||
end
|
||
|
||
function UnitFrame_OnEvent(event)
|
||
local frame = this
|
||
-- 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
|
||
-- The status flash's state arms (ref PlayerFrame_OnEvent l.100-115, decision 1082):
|
||
-- inCombat = auto-attack active, onHateList = on something's aggro list; a world
|
||
-- entry resets both (the ref's own PLAYER_ENTERING_WORLD arm). The repaint itself
|
||
-- rides the shared Update below — this file's posture (the PvP sound note).
|
||
if event == "PLAYER_ENTERING_WORLD" then
|
||
frame.inCombat = nil
|
||
frame.onHateList = nil
|
||
elseif event == "PLAYER_ENTER_COMBAT" then
|
||
frame.inCombat = 1
|
||
elseif event == "PLAYER_LEAVE_COMBAT" then
|
||
frame.inCombat = nil
|
||
elseif event == "PLAYER_REGEN_DISABLED" then
|
||
frame.onHateList = 1
|
||
elseif event == "PLAYER_REGEN_ENABLED" then
|
||
frame.onHateList = nil
|
||
end
|
||
UnitFrame_Update()
|
||
-- 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) ──────────────────────
|
||
-- Our implementation of what ref CombatFeedback.lua specifies, on frame-param style (the
|
||
-- ref reads `this`). Timings, colors and size factors are its MEASURED values. 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",
|
||
}
|
||
|
||
-- The indicator says one of two things: a NUMBER, when something moved a bar, or a WORD,
|
||
-- when nothing did. Everything below is that split. `scale` is a multiple of the frame's
|
||
-- own indicator height — 1 is "as authored" — and the multiples themselves are the
|
||
-- reference's measurements: half again for a big hit, three quarters for one that was
|
||
-- stopped, half for one that could never land.
|
||
|
||
-- The two events that move a bar the friendly way, and the colour each prints in. (A
|
||
-- WOUND prints a number too, but its colour depends on the school, so it is settled in
|
||
-- place below.)
|
||
local COMBAT_FEEDBACK_GAIN = {
|
||
HEAL = { r = 0.0, g = 1.0, b = 0.0 },
|
||
ENERGIZE = { r = 0.41, g = 0.8, b = 0.94 },
|
||
}
|
||
|
||
-- The words that do not print at the indicator's authored height. Everything else does.
|
||
local COMBAT_FEEDBACK_WORD_SCALE = {
|
||
IMMUNE = 0.5,
|
||
BLOCK = 0.75,
|
||
}
|
||
|
||
-- A wound that did no damage was stopped by one of three things, and in each case the
|
||
-- flag naming it is also the key of the word for it. Anything else was a plain miss.
|
||
local COMBAT_FEEDBACK_SOAKED = { ABSORB = 1, BLOCK = 1, RESIST = 1 }
|
||
|
||
-- What a zero-damage wound reads as: the word, and the size it prints at. Being stopped
|
||
-- reads small; missing outright reads full size, because a miss is the ordinary case.
|
||
local function CombatFeedback_Stopped(flags)
|
||
if COMBAT_FEEDBACK_SOAKED[flags] then
|
||
return CombatFeedbackText[flags], 0.75
|
||
end
|
||
return CombatFeedbackText["MISS"], 1.0
|
||
end
|
||
|
||
-- The size a landed blow reads at. A crushing blow is as loud as a critical; a glancing
|
||
-- one is as quiet as a soak.
|
||
local function CombatFeedback_HitScale(flags)
|
||
if flags == "CRITICAL" or flags == "CRUSHING" then
|
||
return 1.5
|
||
elseif flags == "GLANCING" then
|
||
return 0.75
|
||
end
|
||
return 1.0
|
||
end
|
||
|
||
function CombatFeedback_OnCombatEvent(frame, event, flags, amount, type)
|
||
local feedbackText = frame.feedbackText
|
||
if not feedbackText then return end
|
||
|
||
-- The default reading, which most events take unchanged: this event's own word, at the
|
||
-- authored height, in white. An event with no word at all prints nothing, which is the
|
||
-- honest thing to do with a feedback kind we do not have a string for.
|
||
local text = CombatFeedbackText[event]
|
||
local scale = 1.0
|
||
local r, g, b = 1.0, 1.0, 1.0
|
||
|
||
if event == "WOUND" then
|
||
if amount == 0 then
|
||
text, scale = CombatFeedback_Stopped(flags)
|
||
else
|
||
text = amount
|
||
scale = CombatFeedback_HitScale(flags)
|
||
-- A non-zero school means a spell landed it: spell damage prints yellow where
|
||
-- a physical hit prints white.
|
||
if type > 0 then
|
||
r, g, b = 1.0, 1.0, 0.0
|
||
end
|
||
end
|
||
else
|
||
local gain = COMBAT_FEEDBACK_GAIN[event]
|
||
if gain then
|
||
text = amount
|
||
r, g, b = gain.r, gain.g, gain.b
|
||
-- A critical heal or power gain reads big. Note this is the ONLY flag that
|
||
-- resizes one — a wound's crushing/glancing legs do not apply here.
|
||
if flags == "CRITICAL" then
|
||
scale = 1.5
|
||
end
|
||
else
|
||
scale = COMBAT_FEEDBACK_WORD_SCALE[event] or 1.0
|
||
end
|
||
end
|
||
|
||
frame.feedbackStartTime = GetTime()
|
||
|
||
feedbackText:SetTextHeight(frame.feedbackFontHeight * scale)
|
||
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.
|
||
-- The classification border art (TargetFrame_CheckClassification, ref-TargetFrame.lua
|
||
-- l.205-218) — re-expressed branch for branch, and the shape is the surprise: 1.12 has FIVE
|
||
-- classifications but only THREE border textures, because elite, rareelite and worldboss all
|
||
-- take the same Elite art. There is no rare-elite border in this build and no rare-elite
|
||
-- tooltip word either (the level line's rank table is {"", ELITE, ELITE, BOSS, ""}), so
|
||
-- "rareelite" is a distinction the API makes and the pixels never do. Verified rather than
|
||
-- assumed, because later expansions DO ship UI-TargetingFrame-Rare-Elite and it would be the
|
||
-- natural thing to write: that path is absent from 1.12's patch chain entirely, and the ref
|
||
-- Lua sends rareelite to the Elite art.
|
||
--
|
||
-- SetTexture is the whole operation — no TexCoords, size or anchor changes. That is only
|
||
-- sound because all three BLPs are 256x128 (checked, not assumed), so the region's
|
||
-- XML-declared TexCoords (0.09375..1.0 x 0..0.78125 = the 232x100 crop) frame all three
|
||
-- identically, and our SetTexture touches only the path (it leaves tex_coords alone).
|
||
function TargetFrame_CheckClassification()
|
||
local frame = this
|
||
local classification = UnitClassification(frame.unit)
|
||
if classification == "worldboss" or classification == "rareelite" or classification == "elite" then
|
||
frame.frameTexture:SetTexture("Interface\\TargetingFrame\\UI-TargetingFrame-Elite")
|
||
elseif classification == "rare" then
|
||
frame.frameTexture:SetTexture("Interface\\TargetingFrame\\UI-TargetingFrame-Rare")
|
||
else
|
||
frame.frameTexture:SetTexture("Interface\\TargetingFrame\\UI-TargetingFrame")
|
||
end
|
||
end
|
||
|
||
-- ── One unit's two bars, from its token ───────────────────────────────────────────────
|
||
-- Three frames in this file paint exactly this, and until 1576 each wrote it out: the
|
||
-- health bar's range and value, then the power bar's colour off BENILLA_POWER_COLORS and
|
||
-- its own range and value — or no power bar at all, this file's convention for a unit with
|
||
-- no resource. Returns whether the unit HAS one, which is what the pet frame swaps its
|
||
-- plate on (the reference has no such art for the others and leaves an empty rail drawn).
|
||
--
|
||
-- The bars are ARGUMENTS rather than fields: the player and target frames call theirs
|
||
-- `manabar` and the pet frame calls its `manabar`, following the reference's own two names
|
||
-- for one rail, and a helper that reached for a field would have to pick a side.
|
||
--
|
||
-- NO label on the power bar. 1.12 names the resource there (ref UnitFrame_UpdateManaType
|
||
-- l.129 → "Rage 0 / 100" off ManaBarColor's own prefix field, and CharacterFrame_OnLoad
|
||
-- labels health and XP the same way). Shipped in 1146, cut on the director's call the same
|
||
-- day: bare numbers read better on these bars (decision 1147). The mechanism stays —
|
||
-- SetTextStatusBarTextPrefix is live and one call brings any label back — only the calls
|
||
-- are gone.
|
||
function BenillaUnitFrame_PaintBars(healthbar, manabar, unit)
|
||
healthbar:SetMinMaxValues(0, UnitHealthMax(unit))
|
||
healthbar:SetValue(UnitHealth(unit))
|
||
local maxPower = UnitManaMax(unit)
|
||
if maxPower <= 0 then
|
||
manabar:Hide()
|
||
return false
|
||
end
|
||
manabar:Show()
|
||
local _, powerToken = UnitPowerType(unit)
|
||
local swatch = BENILLA_POWER_COLORS[powerToken] or BENILLA_POWER_COLORS.MANA
|
||
manabar:SetStatusBarColor(swatch.r, swatch.g, swatch.b)
|
||
manabar:SetMinMaxValues(0, maxPower)
|
||
manabar:SetValue(UnitMana(unit))
|
||
return true
|
||
end
|
||
|
||
function UnitFrame_Update()
|
||
local frame = this
|
||
local unit = frame.unit
|
||
if not UnitExists(unit) then
|
||
frame:Hide()
|
||
return
|
||
end
|
||
frame:Show()
|
||
|
||
BenillaUnitFrame_PaintBars(frame.healthbar, frame.manabar, unit)
|
||
|
||
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
|
||
|
||
-- The classification border (target only) — the ref's TargetFrame_Update calls
|
||
-- CheckClassification right here, between CheckFaction and CheckDead.
|
||
if frame.frameTexture then
|
||
TargetFrame_CheckClassification()
|
||
end
|
||
|
||
-- DEAD text (target only), ref TargetFrame_CheckDead (l.220-228) whole: Show/Hide a
|
||
-- region whose word the XML already carries. This site used to SetText("DEAD") a
|
||
-- LITERAL and blank it when alive, which put the KEY on screen in caps — `DEAD` is a
|
||
-- GlobalStrings key (l.898 = "Dead"), not the string, and typing a key as display text
|
||
-- is the "CREATE_MACROS" failure of 0983/0991 again. Nothing here names the word now:
|
||
-- it comes from the FontString's `text="DEAD"` attribute, which the loader resolves
|
||
-- through the global (loader/mod.rs resolve_text), locale and all.
|
||
--
|
||
-- `UnitIsConnected` is the reference's own second term and is what tells dead from
|
||
-- LINK-dead: a disconnected player reads 0 health here and is not dead. The ToT twin
|
||
-- (TargetofTarget_CheckDead) has carried it since it was written; this leg had not.
|
||
if frame.deadText then
|
||
if UnitHealth(unit) <= 0 and UnitIsConnected(unit) then
|
||
frame.deadText:Show()
|
||
else
|
||
frame.deadText:Hide()
|
||
end
|
||
end
|
||
|
||
-- The PvP flag icon, on both frames (decision 0646 §4).
|
||
BenillaUnitFrame_UpdatePvP(frame)
|
||
|
||
-- The status flash, player frame only (handle-gated like pvpIcon; decision 1082).
|
||
if frame.statusTexture then
|
||
PlayerFrame_UpdateStatus()
|
||
end
|
||
|
||
-- The leader / master-looter icons, player frame only (report B317).
|
||
if frame.leaderIcon then
|
||
BenillaPlayerFrame_UpdatePartyLeader(frame)
|
||
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), re-expressed 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 (UnitFrame_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("TargetFrameBuff" .. 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("TargetFrameDebuff" .. 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). The target-of-target frame stands at the
|
||
-- target frame's bottom-RIGHT (decision 1576), which is where a six-icon row of
|
||
-- debuffs would reach: hence the wrap at 5 and, with a full buff row, the shrink
|
||
-- below. Both legs are the reference's, and both are live now that the frame exists.
|
||
local targetofTarget = TargetofTargetFrame:IsShown()
|
||
local debuffWrap = 6
|
||
if targetofTarget then
|
||
debuffWrap = 5
|
||
end
|
||
|
||
if UnitIsFriend("player", "target") then
|
||
TargetFrameBuff1:SetPoint("TOPLEFT", "TargetFrame", "BOTTOMLEFT", 5, 32)
|
||
TargetFrameDebuff1:SetPoint("TOPLEFT", "TargetFrameBuff1", "BOTTOMLEFT", 0, -2)
|
||
else
|
||
TargetFrameDebuff1:SetPoint("TOPLEFT", "TargetFrame", "BOTTOMLEFT", 5, 32)
|
||
if targetofTarget then
|
||
-- ref l.318-326 writes this as THREE branches (< 5, 5..9, >= 10) whose first
|
||
-- two are character-for-character identical; the buffs open under the second
|
||
-- debuff row until there is a third. Two branches say the same thing.
|
||
if numDebuffs >= 10 then
|
||
TargetFrameBuff1:SetPoint("TOPLEFT", "TargetFrameDebuff11", "BOTTOMLEFT", 0, -2)
|
||
else
|
||
TargetFrameBuff1:SetPoint("TOPLEFT", "TargetFrameDebuff6", "BOTTOMLEFT", 0, -2)
|
||
end
|
||
else
|
||
TargetFrameBuff1:SetPoint("TOPLEFT", "TargetFrameDebuff7", "BOTTOMLEFT", 0, -2)
|
||
end
|
||
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("TargetFrameBuff" .. 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("TargetFrameDebuff" .. 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). Written verbatim while the wrap was
|
||
-- pinned at 6 so the ToT wrap-5 leg would drop in unchanged — it did (1576), and
|
||
-- both directions are covered: every button the wrap-5 pass moves is put back by the
|
||
-- wrap-6 pass, and Debuff5 is only ever re-seated onto the anchor it is authored with.
|
||
getglobal("TargetFrameDebuff" .. debuffWrap):ClearAllPoints()
|
||
getglobal("TargetFrameDebuff" .. debuffWrap):SetPoint("LEFT", getglobal("TargetFrameDebuff" .. (debuffWrap - 1)), "RIGHT", 3, 0)
|
||
getglobal("TargetFrameDebuff" .. (debuffWrap + 1)):ClearAllPoints()
|
||
getglobal("TargetFrameDebuff" .. (debuffWrap + 1)):SetPoint("TOPLEFT", "TargetFrameDebuff1", "BOTTOMLEFT", 0, -2)
|
||
getglobal("TargetFrameDebuff" .. (debuffWrap + 2)):ClearAllPoints()
|
||
getglobal("TargetFrameDebuff" .. (debuffWrap + 2)):SetPoint("LEFT", getglobal("TargetFrameDebuff" .. (debuffWrap + 1)), "RIGHT", 3, 0)
|
||
if debuffWrap == 5 then
|
||
TargetFrameDebuff11:ClearAllPoints()
|
||
TargetFrameDebuff11:SetPoint("TOPLEFT", "TargetFrameDebuff6", "BOTTOMLEFT", 0, -2)
|
||
else
|
||
TargetFrameDebuff11:ClearAllPoints()
|
||
TargetFrameDebuff11:SetPoint("LEFT", "TargetFrameDebuff10", "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
|
||
|
||
-- The status flash's paint (ref PlayerFrame_UpdateStatus, re-expressed onto frame-param
|
||
-- style; decision 1082). Priority is the ref's: RESTING wins over combat — sitting in an
|
||
-- inn mid-auto-attack shows the zzz — then the auto-attack state (gold ring/zzz vs red
|
||
-- ring/swords/red disc), then bare swords for on-someone's-aggro-list, then nothing.
|
||
function PlayerFrame_UpdateStatus()
|
||
local frame = this
|
||
if IsResting() then
|
||
frame.statusTexture:SetVertexColor(1.0, 0.88, 0.25, 1.0)
|
||
frame.statusTexture:Show()
|
||
frame.restIcon:Show()
|
||
frame.attackIcon:Hide()
|
||
frame.restGlow:Show()
|
||
frame.attackGlow:Hide()
|
||
frame.statusGlow:Show()
|
||
frame.attackBackground:Hide()
|
||
elseif frame.inCombat then
|
||
frame.statusTexture:SetVertexColor(1.0, 0.0, 0.0, 1.0)
|
||
frame.statusTexture:Show()
|
||
frame.attackIcon:Show()
|
||
frame.restIcon:Hide()
|
||
frame.attackGlow:Show()
|
||
frame.restGlow:Hide()
|
||
frame.statusGlow:Show()
|
||
frame.attackBackground:Show()
|
||
elseif frame.onHateList then
|
||
frame.attackIcon:Show()
|
||
frame.restIcon:Hide()
|
||
frame.statusGlow:Hide()
|
||
frame.attackBackground:Hide()
|
||
else
|
||
frame.statusTexture:Hide()
|
||
frame.restIcon:Hide()
|
||
frame.attackIcon:Hide()
|
||
frame.statusGlow:Hide()
|
||
frame.attackBackground:Hide()
|
||
end
|
||
end
|
||
|
||
-- The leader / master-looter icons on the player's own frame (ref
|
||
-- PlayerFrame_UpdatePartyLeader, re-expressed onto frame-param style; report B317).
|
||
--
|
||
-- Two DIFFERENT predicates, and the asymmetry is the reference's own. The leader icon asks
|
||
-- IsPartyLeader() — a boolean about us — where the member frames ask whether the leader
|
||
-- INDEX equals their id, because the player has no index on that scale (0 is "the player
|
||
-- leads", not "party0"). The master icon then asks whether GetLootMethod's second return
|
||
-- is 0, which is that same scale reading "the player is master looter"; the group test
|
||
-- beside it is the reference's own guard, and it is not redundant with ours — a solo
|
||
-- player's master_looter is None, so `masterId == 0` is already false, but reproducing the
|
||
-- expression keeps us honest if that ever stops being true.
|
||
function BenillaPlayerFrame_UpdatePartyLeader(frame)
|
||
if IsPartyLeader() then
|
||
frame.leaderIcon:Show()
|
||
else
|
||
frame.leaderIcon:Hide()
|
||
end
|
||
local lootMethod, masterId = GetLootMethod()
|
||
if masterId == 0 and (GetNumPartyMembers() > 0 or GetNumRaidMembers() > 0) then
|
||
frame.masterIcon:Show()
|
||
else
|
||
frame.masterIcon:Hide()
|
||
end
|
||
end
|
||
|
||
-- ref PlayerFrame_OnUpdate: the status pulse — a 0.5 s triangle wave between alpha
|
||
-- 55/255 and 255/255 on the ADD-blend status ring and the icon glow, run only while the
|
||
-- ring is visible (resting or auto-attacking); the hit-indicator fade rides the same
|
||
-- tick, exactly as before.
|
||
function PlayerFrame_OnUpdate(elapsed)
|
||
local frame = this
|
||
if frame.statusTexture:IsVisible() then
|
||
local counter = frame.statusCounter + elapsed
|
||
local sign = frame.statusSign
|
||
if counter > 0.5 then
|
||
sign = -sign
|
||
frame.statusSign = sign
|
||
end
|
||
counter = mod(counter, 0.5)
|
||
frame.statusCounter = counter
|
||
local alpha
|
||
if sign == 1 then
|
||
alpha = (55 + counter * 400) / 255
|
||
else
|
||
alpha = (255 - counter * 400) / 255
|
||
end
|
||
frame.statusTexture:SetAlpha(alpha)
|
||
frame.statusGlow:SetAlpha(alpha)
|
||
end
|
||
CombatFeedback_OnUpdate(frame)
|
||
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 PlayerFrame_OnClick(button)
|
||
if button == "RightButton" then
|
||
ToggleDropDownMenu(1, nil, PlayerFrameDropDown, "PlayerFrame", 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
|
||
|
||
-- Which menu the CURRENT target gets, and the heading that menu needs (only the bare mark
|
||
-- list carries one — the unit menus title themselves from the unit). Most specific first:
|
||
-- yourself → SELF
|
||
-- your own pet → PET. Selecting your pet and right-clicking the TARGET
|
||
-- frame is still the pet's menu; the ref's own second
|
||
-- branch (TargetFrame.lua l.440-441), which sat as a
|
||
-- comment here until the "pet" token (0990) and then the
|
||
-- menu itself (1066).
|
||
-- anything that is not a player → an NPC, which gets only the raid-mark list
|
||
-- a player, in your party or not → PARTY / PLAYER
|
||
local function TargetFrameMenu()
|
||
if UnitIsUnit("target", "player") then
|
||
return "SELF"
|
||
end
|
||
if UnitIsUnit("target", "pet") then
|
||
return "PET"
|
||
end
|
||
if not UnitIsPlayer("target") then
|
||
return "RAID_TARGET_ICON", RAID_TARGET_ICON
|
||
end
|
||
if UnitInParty("target") then
|
||
return "PARTY"
|
||
end
|
||
return "PLAYER"
|
||
end
|
||
|
||
function TargetFrameDropDown_Initialize()
|
||
local menu, name = TargetFrameMenu()
|
||
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 TargetFrame_OnClick(button)
|
||
if button == "RightButton" then
|
||
ToggleDropDownMenu(1, nil, TargetFrameDropDown, "TargetFrame", 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="PlayerFrame" 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, which puts it in ARTWORK; ours sits
|
||
in BACKGROUND, a leftover of the pre-0884 layering. The ring art now clears it by
|
||
frame LEVEL, and nothing else in this layer overlaps the portrait's square, so the
|
||
two are indistinguishable here — left alone rather than churned inside a bug fix).
|
||
Texture set in OnLoad via SetPortraitToTexture (circle-masked). -->
|
||
<Texture name="PlayerPortrait">
|
||
<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, one frame level
|
||
ABOVE the bars, so they draw OVER the bar edges — the recessed-bar look. OnLoad puts
|
||
them there (SetFrameLevel); see the LAYERING note at the top of this file. -->
|
||
</Layers>
|
||
<Frames>
|
||
<!-- Health bar 119×12 TOPLEFT (106,-41), UI-StatusBar, green (ref l.288-315). -->
|
||
<StatusBar name="$parentHealthBar" enableMouse="true">
|
||
<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"/>
|
||
<Scripts>
|
||
<OnEvent>if TextStatusBar_OnEvent then TextStatusBar_OnEvent(arg1, arg2) end</OnEvent>
|
||
<OnValueChanged>if TextStatusBar_OnValueChanged then TextStatusBar_OnValueChanged() end</OnValueChanged>
|
||
<OnEnter>BenillaUnitFrameBar_OnEnter(self)</OnEnter>
|
||
<OnLeave>BenillaUnitFrameBar_OnLeave(self)</OnLeave>
|
||
<OnMouseUp>BenillaUnitFrameBar_OnMouseUp(self, arg1)</OnMouseUp>
|
||
</Scripts>
|
||
</StatusBar>
|
||
<!-- Power bar 119×12 TOPLEFT (106,-52), UI-StatusBar; color set at runtime (ref l.316-341). -->
|
||
<StatusBar name="$parentManaBar" enableMouse="true">
|
||
<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"/>
|
||
<Scripts>
|
||
<OnEvent>if TextStatusBar_OnEvent then TextStatusBar_OnEvent(arg1, arg2) end</OnEvent>
|
||
<OnValueChanged>if TextStatusBar_OnValueChanged then TextStatusBar_OnValueChanged() end</OnValueChanged>
|
||
<OnEnter>BenillaUnitFrameBar_OnEnter(self)</OnEnter>
|
||
<OnLeave>BenillaUnitFrameBar_OnLeave(self)</OnLeave>
|
||
<OnMouseUp>BenillaUnitFrameBar_OnMouseUp(self, arg1)</OnMouseUp>
|
||
</Scripts>
|
||
</StatusBar>
|
||
<!-- The ring art + text, over the bars (ref TargetFrameTextureFrame). 232×100 fills the
|
||
parent; it draws over the bar edges because OnLoad pushes the bars one frame level
|
||
BELOW it (the ref's SetFrameLevel(bar, textureFrame-1)) — NOT because of where it
|
||
is declared. See the LAYERING note at the top of this file. -->
|
||
<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="PlayerFrameTexture" 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>
|
||
<!-- Name and level ride BACKGROUND with the ring art, like the ref's
|
||
(ref-PlayerFrame.xml l.54-97) — NOT the OVERLAY the state icons sit
|
||
in. The layer choice is load-bearing for the level number: the zzz /
|
||
crossed-swords badge (OVERLAY, below) sits exactly on the level
|
||
badge, and the client's bucket-wide texture-before-fontstring order
|
||
(decision 0884) means a fontstring can never hide UNDER a texture
|
||
of its own layer — the ref hides the number while resting by putting
|
||
it in a LOWER layer than the badge. Shipped in OVERLAY by 1082, the
|
||
number rode ON the badge (director report, 2026-08-07); decision 1093. -->
|
||
<!-- Name 100×10 CENTER (50,19) (ref l.58-69).
|
||
|
||
The NAME is the reference's own, literally — and so is every other
|
||
art/text/icon region in the three unit frames. They used to derive
|
||
from `$parent`, which published `PlayerFrameTextureFrameNameText` and
|
||
left `PlayerName` (PlayerFrame.xml:58), `TargetName`
|
||
(TargetFrame.xml:152) and `PetName` (PetFrame.xml:69) — the names an
|
||
addon actually reaches for — nil. See BenillaUnitFrame_OnLoad's
|
||
`short` prefix for how the shared binder composes them now.
|
||
|
||
**This was tried once and REJECTED, and the rejection was right on
|
||
its evidence** — the block that stood here argued: (i) not free, since
|
||
`UnitFrame_OnLoad` composes these names inside `getglobal`, so a text
|
||
search finds nothing and the rename breaks every unit frame (it broke
|
||
40 tests); and (ii) buys nothing, since a "why" read-back over the
|
||
218-addon corpus matched ZERO error rows for `TargetName`, its one
|
||
syntactic caller being a `<Anchor relativeTo=>` in an addon that dies
|
||
earlier anyway. It closed "revisit if a row ever names it" (1222's
|
||
clause: a shadowed name with no observable caller is building against
|
||
a guess).
|
||
|
||
What changed (1719): the condition it named was met, and its cost
|
||
premise expired. A grep of the top-20 root gives `ManaBarColor` 8
|
||
readers, `PlayerFrameManaBar` 3, `PlayerFrameTexture`/
|
||
`TargetFrameTexture`/`PlayerStatusTexture`/`TargetFrameManaBar` 2
|
||
each, and `PlayerName`/`TargetName`/`PetName` 1 each. ShaguTweaks
|
||
dies on `TargetFrameManaBar` at `health-numbers.lua:22`. **Fifteen of
|
||
the twenty-two renames still have zero measured readers** and were
|
||
kept for consistency with the reference and because the cost was
|
||
already paid, NOT because demand was shown — say the word and they go
|
||
back. -->
|
||
<FontString name="PlayerName" 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="PlayerLevelText" inherits="GameFontNormalSmall">
|
||
<Anchors>
|
||
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="-63" y="-16"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
<!-- The on-bar numerals, "value / max" (decision 1143). The reference's
|
||
own seats are CENTER (50,4) and (50,-8) (ref-PlayerFrame.xml l.79-96);
|
||
ours sit **1 px lower** — the director's call on the shipped result,
|
||
2026-08-08, and the deviation is recorded rather than the literals
|
||
(decision 1145). Why a digit string reads high at the ref's own offset:
|
||
a FontString centres on its LINE BOX, ascent + descent, and digits
|
||
occupy only the ascent — so the visible mass sits above the box centre
|
||
and a numerals-only string looks lifted inside a 12 px bar. The nudge
|
||
is optical, and it belongs on the seat rather than on the centring,
|
||
which every other string in the UI shares.
|
||
The LAYER is the reference's: BACKGROUND beside the name and level, not
|
||
the OVERLAY the state badges sit in, for the reason spelled out above.
|
||
They draw over the bar FILL because the whole TextureFrame outranks the
|
||
bars by a frame level. Empty until Status Bar Text is on or (later) the
|
||
bar is hovered — TextStatusBar.xml owns that decision, not this file. -->
|
||
<FontString name="PlayerFrameHealthBarText" inherits="TextStatusBarText">
|
||
<Anchors>
|
||
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="50" y="3"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
<FontString name="PlayerFrameManaBarText" inherits="TextStatusBarText">
|
||
<Anchors>
|
||
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="50" y="-9"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
</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="PlayerPVPIcon" 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>
|
||
<!-- The status ring (ref PlayerStatusTexture, ref-PlayerFrame.xml l.99-111):
|
||
the ADD-blend glow ring around the whole frame, tinted gold while
|
||
resting / red while auto-attacking, pulsed by the OnUpdate flash
|
||
(decision 1082). 190×66 TOPLEFT (35,-8), the ring art's own recess. -->
|
||
<Texture name="PlayerStatusTexture" file="Interface\CharacterFrame\UI-Player-Status" alphaMode="ADD" hidden="true">
|
||
<Size><AbsDimension x="190" y="66"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="35" y="-8"/></Offset></Anchor>
|
||
</Anchors>
|
||
<TexCoords left="0" right="0.74609375" top="0" bottom="0.53125"/>
|
||
</Texture>
|
||
<!-- ref PlayerAttackBackground (l.112-123): the dark red disc behind the
|
||
attack state icon; OnLoad tints it (0.8,0.1,0.1) at alpha 0.4. -->
|
||
<Texture name="PlayerAttackBackground" file="Interface\TargetingFrame\UI-TargetingFrame-AttackBackground" hidden="true">
|
||
<Size><AbsDimension x="32" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="37" y="-50"/></Offset></Anchor>
|
||
</Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="OVERLAY">
|
||
<!-- 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="PlayerHitIndicator" hidden="true" inherits="NumberFontNormalHuge">
|
||
<Anchors>
|
||
<Anchor point="CENTER" relativePoint="TOPLEFT"><Offset><AbsDimension x="73" y="-42"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
<!-- Leader + master-looter icons (ref PlayerLeaderIcon / PlayerMasterIcon,
|
||
ref-PlayerFrame.xml l.147-170): 16×16 at the frame's TOPLEFT (44,-10)
|
||
and (80,-10) — above the portrait ring, left to right. The reference
|
||
seats them in this same OVERLAY layer and in the same frame-filling
|
||
child the state icons use, so the offsets are directly comparable to
|
||
$parentRestIcon's below. Shown by BenillaPlayerFrame_UpdatePartyLeader.
|
||
Report B317: the member frames have carried these since 0434 phase 2;
|
||
the player frame was built before the party wire and listed them OUT. -->
|
||
<Texture name="PlayerLeaderIcon" file="Interface\GroupFrame\UI-Group-LeaderIcon" hidden="true">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="44" y="-10"/></Offset></Anchor>
|
||
</Anchors>
|
||
</Texture>
|
||
<Texture name="PlayerMasterIcon" file="Interface\GroupFrame\UI-Group-MasterLooter" hidden="true">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="80" y="-10"/></Offset></Anchor>
|
||
</Anchors>
|
||
</Texture>
|
||
<!-- The state icon pair under the portrait (ref PlayerRestIcon /
|
||
PlayerAttackIcon, ref-PlayerFrame.xml l.171-201): the two halves of
|
||
UI-StateIcon — zzz left, crossed swords right (decision 1082). -->
|
||
<Texture name="PlayerRestIcon" file="Interface\CharacterFrame\UI-StateIcon" hidden="true">
|
||
<Size><AbsDimension x="31" y="33"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="37" y="-49"/></Offset></Anchor>
|
||
</Anchors>
|
||
<TexCoords left="0" right="0.5" top="0" bottom="0.421875"/>
|
||
</Texture>
|
||
<Texture name="PlayerAttackIcon" file="Interface\CharacterFrame\UI-StateIcon" hidden="true">
|
||
<Size><AbsDimension x="32" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="PlayerRestIcon"><Offset><AbsDimension x="1" y="1"/></Offset></Anchor>
|
||
</Anchors>
|
||
<TexCoords left="0.5" right="1.0" top="0" bottom="0.5"/>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
</Frame>
|
||
<!-- The state icon's own ADD-blend glow (ref PlayerStatusGlow, ref-PlayerFrame.xml
|
||
l.216-248): a sibling FRAME over the icon so the whole thing can pulse as one
|
||
SetAlpha group with the status ring (the OnUpdate flash, decision 1082). -->
|
||
<Frame name="PlayerStatusGlow" hidden="true">
|
||
<Size><AbsDimension x="32" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="PlayerRestIcon"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="OVERLAY">
|
||
<Texture name="PlayerRestGlow" file="Interface\CharacterFrame\UI-StateIcon" alphaMode="ADD">
|
||
<Size><AbsDimension x="32" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"/>
|
||
</Anchors>
|
||
<TexCoords left="0" right="0.5" top="0.5" bottom="1.0"/>
|
||
</Texture>
|
||
<Texture name="PlayerAttackGlow" file="Interface\CharacterFrame\UI-StateIcon" alphaMode="ADD">
|
||
<Size><AbsDimension x="32" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"/>
|
||
</Anchors>
|
||
<TexCoords left="0.5" right="1.0" top="0.5" bottom="1.0"/>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
</Frame>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnLoad><![CDATA[BenillaUnitFrame_OnLoad(this, "player")]]></OnLoad>
|
||
<OnEvent><![CDATA[UnitFrame_OnEvent(event)]]></OnEvent>
|
||
<OnUpdate><![CDATA[PlayerFrame_OnUpdate(arg1)]]></OnUpdate>
|
||
<OnEnter><![CDATA[BenillaUnitFrame_OnEnter(this)]]></OnEnter>
|
||
<OnLeave><![CDATA[BenillaUnitFrame_OnLeave(this)]]></OnLeave>
|
||
<OnClick><![CDATA[PlayerFrame_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="PlayerFrame" 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="TargetFrame" 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="TargetPortrait">
|
||
<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" enableMouse="true">
|
||
<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"/>
|
||
<Scripts>
|
||
<OnEvent>if TextStatusBar_OnEvent then TextStatusBar_OnEvent(arg1, arg2) end</OnEvent>
|
||
<OnValueChanged>if TextStatusBar_OnValueChanged then TextStatusBar_OnValueChanged() end</OnValueChanged>
|
||
<OnEnter>BenillaUnitFrameBar_OnEnter(self)</OnEnter>
|
||
<OnLeave>BenillaUnitFrameBar_OnLeave(self)</OnLeave>
|
||
<OnMouseUp>BenillaUnitFrameBar_OnMouseUp(self, arg1)</OnMouseUp>
|
||
</Scripts>
|
||
</StatusBar>
|
||
<!-- Power bar 119×12 TOPRIGHT (-106,-52), UI-StatusBar; color set at runtime (ref l.274-292). -->
|
||
<StatusBar name="$parentManaBar" enableMouse="true">
|
||
<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"/>
|
||
<Scripts>
|
||
<OnEvent>if TextStatusBar_OnEvent then TextStatusBar_OnEvent(arg1, arg2) end</OnEvent>
|
||
<OnValueChanged>if TextStatusBar_OnValueChanged then TextStatusBar_OnValueChanged() end</OnValueChanged>
|
||
<OnEnter>BenillaUnitFrameBar_OnEnter(self)</OnEnter>
|
||
<OnLeave>BenillaUnitFrameBar_OnLeave(self)</OnLeave>
|
||
<OnMouseUp>BenillaUnitFrameBar_OnMouseUp(self, arg1)</OnMouseUp>
|
||
</Scripts>
|
||
</StatusBar>
|
||
<!-- The ring art + text, over the bars (ref TargetFrameTextureFrame); it draws over the
|
||
bar edges because OnLoad pushes the bars one frame level BELOW it (the ref's
|
||
SetFrameLevel(bar, textureFrame-1)) — NOT because of where it is declared. See the
|
||
LAYERING note at the top of this file. -->
|
||
<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="TargetFrameTexture" 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="TargetPVPIcon" 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="TargetName" 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="TargetLevelText" 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="TargetHighLevelTexture" 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), spelled
|
||
the reference's own way: `text="DEAD"` is a GLOBAL-STRING LOOKUP, so
|
||
the word rendered is GlobalStrings.lua l.898's `DEAD = "Dead"` — the
|
||
key never reaches the screen. `hidden` like the ToT twin below, a
|
||
deliberate divergence: the reference leaves the region shown and
|
||
relies on CheckDead landing before the frame does. -->
|
||
<FontString name="TargetDeadText" inherits="GameFontNormalSmall" text="DEAD" hidden="true">
|
||
<Anchors>
|
||
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="-50" y="3"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
<!-- The target's numerals (decision 1146), mirrored from the player's seats
|
||
the way the whole frame is: x = −50 against the player's +50, the same
|
||
1 px optical drop (1145). 1143 wired these bars and left the regions
|
||
out because nothing could ever paint them — 1.12 never makes the target
|
||
bars textLockable, so Status Bar Text does not pin them. The HOVER is
|
||
what they were waiting for, and it is the reference's behaviour whole:
|
||
on the target, the numbers are something you go and look at. -->
|
||
<FontString name="TargetFrameHealthBarText" inherits="TextStatusBarText">
|
||
<Anchors>
|
||
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="-50" y="3"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
<FontString name="TargetFrameManaBarText" inherits="TextStatusBarText">
|
||
<Anchors>
|
||
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="-50" y="-9"/></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[UnitFrame_OnEvent(event)]]></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>
|
||
<!-- The target-of-target reconcile (decision 1576, ref TargetFrame_OnUpdate): one
|
||
boolean compare, and only while a target exists. See its note below. -->
|
||
<OnUpdate><![CDATA[TargetFrame_OnUpdate()]]></OnUpdate>
|
||
<OnClick><![CDATA[TargetFrame_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="TargetFrame" 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>
|
||
|
||
<!--
|
||
THE TARGET-OF-TARGET FRAME (decision 1576). The 93×45 satellite at the target frame's
|
||
bottom-right: who the unit you have selected is fighting. Geometry, art and law are
|
||
MEASURED off the reference (ref-TargetFrame.xml l.515-690, ref-TargetFrame.lua l.494-585);
|
||
the code is our own, on this file's snapshot → Unit* → event data flow.
|
||
|
||
THE TOKEN IS THE SLICE. `"targettarget"` is 1.12's own token grammar and our resolver
|
||
already RECOGNISED it (the `target` prefix — benilla-ui unit/mod.rs); it simply never named
|
||
a unit, because nothing fed one. The app now does: the selected unit's own
|
||
`UNIT_FIELD_TARGET`, one hop through the guid→entity index (ui_unit.rs), with the aura list
|
||
(ui_aura.rs) and the portrait booth (portrait/mod.rs) following the same hop. Everything
|
||
below is then ordinary FrameXML over an ordinary unit token.
|
||
|
||
DRIVEN BY EVENTS, NOT BY A POLL. The reference calls `TargetofTarget_Update()` from this
|
||
frame's own OnUpdate, every frame, unconditionally — it has no other way to notice, since
|
||
its engine fires no UNIT_* event for this token. Ours fires them (the feed diffs the
|
||
snapshot and fires per field, exactly as it does for `"target"`), so the repaint rides the
|
||
events. What events cannot carry is a token going AWAY — the feed clears it silently, by
|
||
the same convention `"target"` uses — and that is precisely what the reference's own
|
||
reconcile line covers (`TargetFrame_OnUpdate`, ref l.256-260): one boolean compare per
|
||
frame, on a frame that only ticks while you have a target. That line we keep.
|
||
|
||
DEVIATIONS, noted:
|
||
- No hover tooltip and no right-click menu. The reference gives this frame neither — its
|
||
Scripts are OnLoad/OnEvent/OnUpdate/OnClick and nothing else — so neither is built here.
|
||
The four debuff buttons do carry the reference's own aura tooltip.
|
||
- The five display modes are a PREDICATE, not the reference's ladder. ref l.505-528 tests
|
||
them as an if/elseif chain in which the solo leg (state "3") calls neither Show nor Hide
|
||
when you are in a raid: the frame keeps whatever state it had, which is a leaked state,
|
||
not a rule. `TargetofTarget_ModeAllows` answers the question the five modes ask.
|
||
- A powerless unit hides its mana bar, this file's convention on every frame; the
|
||
reference leaves an empty 0/0 rail drawn.
|
||
- `TargetofTargetHealthCheck` guards its own divide. The reference divides by
|
||
`UnitManaMax`'s sibling `unitHPMax` with no zero test.
|
||
-->
|
||
<!-- The debuff button — ref TargetofTargetBuffButtonTemplate (TargetFrame.xml l.4-27): the
|
||
party template shrunk to 12×12 with a 13×13 border, which is why the reference re-sets
|
||
both in an OnLoad rather than authoring them. We author them. No stack-count region: the
|
||
reference's party template has none, and at 12 px a numeral is not readable anyway. -->
|
||
<Button name="BenillaTargetofTargetDebuffTemplate" virtual="true" hidden="true">
|
||
<Size><AbsDimension x="12" y="12"/></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="13" y="13"/></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>
|
||
</Layer>
|
||
</Layers>
|
||
<Scripts>
|
||
<OnEnter><![CDATA[
|
||
if GameTooltip then
|
||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||
GameTooltip:SetUnitDebuff("targettarget", this:GetID())
|
||
end
|
||
]]></OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Button>
|
||
|
||
<Script><![CDATA[
|
||
-- The two saved settings (ref UIOptionsFrame.lua l.116-119), with the reference's own
|
||
-- defaults: the frame ships OFF, and its mode ships at "always". They are saved-variable
|
||
-- globals rather than CVars because that is what they are in 1.12 — the Interface panel's
|
||
-- `uvar` store (OptionsFrame.xml's three-store note), and RegisterForSave is what persists
|
||
-- them into benilla-config (saved.rs).
|
||
SHOW_TARGET_OF_TARGET = "0"
|
||
RegisterForSave("SHOW_TARGET_OF_TARGET")
|
||
SHOW_TARGET_OF_TARGET_STATE = "5"
|
||
RegisterForSave("SHOW_TARGET_OF_TARGET_STATE")
|
||
|
||
-- Does the group state allow the frame right now? The five modes, in the reference's own
|
||
-- numbering (its dropdown writes these strings, and OPTION_TOOLTIP_TARGETOFTARGET1..5
|
||
-- describes them in this order): 1 raid, 2 party, 3 solo, 4 party or raid, 5 always.
|
||
-- Written as a predicate rather than the reference's if/elseif ladder — see the DEVIATIONS
|
||
-- note above for the leaked state that ladder has in its solo leg. LOCAL: the reference has
|
||
-- no such function, and inventing a global for it would put a name in `_G` that no addon
|
||
-- written against 1.12 can expect (the `Benilla*` prefix is for the ones we must expose).
|
||
local function TargetofTarget_ModeAllows()
|
||
local mode = SHOW_TARGET_OF_TARGET_STATE
|
||
local raid = GetNumRaidMembers()
|
||
local party = GetNumPartyMembers()
|
||
if mode == "1" then return raid > 0 end
|
||
if mode == "2" then return party > 0 and raid == 0 end
|
||
if mode == "3" then return raid == 0 and party == 0 end
|
||
if mode == "4" then return raid > 0 or party > 0 end
|
||
if mode == "5" then return true end
|
||
return false
|
||
end
|
||
|
||
-- The visibility law + the repaint (ref TargetofTarget_Update, l.503-541). Four gates
|
||
-- besides the switch and the mode, all the reference's: there must BE a target, it must
|
||
-- have a target, it must not be YOU (your own target's target is your own target — the
|
||
-- frame would restate the frame it hangs off), and it must be alive.
|
||
function TargetofTarget_Update()
|
||
local frame = TargetofTargetFrame
|
||
local was = frame:IsShown()
|
||
local show = SHOW_TARGET_OF_TARGET == "1"
|
||
and UnitExists("target")
|
||
and UnitExists("targettarget")
|
||
and not UnitIsUnit("player", "target")
|
||
and UnitHealth("target") > 0
|
||
and TargetofTarget_ModeAllows()
|
||
if show then
|
||
frame:Show()
|
||
BenillaUnitFrame_PaintBars(frame.healthbar, frame.manabar, "targettarget")
|
||
TargetofTargetName:SetText(UnitName("targettarget") or "")
|
||
TargetofTarget_CheckDead()
|
||
-- The reference's own line (l.536), and it is not redundant with the health
|
||
-- check's tint: that one only ever touches a PLAYER's portrait, so a creature
|
||
-- inherits whatever alpha the last player left behind without this.
|
||
TargetofTargetPortrait:SetAlpha(1.0)
|
||
BenillaTargetofTarget_UpdateDebuffs()
|
||
else
|
||
frame:Hide()
|
||
end
|
||
-- The target's OWN aura rows wrap at 5 instead of 6 while this frame stands beside
|
||
-- them (BenillaTargetAuras_Update), so a change of shown state re-lays them out. The
|
||
-- reference re-runs them on the SHOW leg only (l.534) and never on the hide, which
|
||
-- leaves the rows wrapped short after the frame goes away.
|
||
if frame:IsShown() ~= was and UnitExists("target") then
|
||
BenillaTargetAuras_Update()
|
||
end
|
||
end
|
||
|
||
-- ref TargetofTarget_CheckDead (l.559-567): the DEAD word over a dimmed trough. The
|
||
-- connected test is the reference's and it is what tells "dead" from "linkdead" —
|
||
-- a disconnected player reads 0 health here and is not dead.
|
||
function TargetofTarget_CheckDead()
|
||
local down = UnitHealth("targettarget") <= 0 and UnitIsConnected("targettarget")
|
||
TargetofTargetBackground:SetAlpha(down and 0.9 or 1)
|
||
if down then
|
||
TargetofTargetDeadText:Show()
|
||
else
|
||
TargetofTargetDeadText:Hide()
|
||
end
|
||
end
|
||
|
||
-- The four readings a PLAYER's portrait can carry here (ref TargetofTargetHealthCheck's
|
||
-- own four SetVertexColor calls, l.573-583): a corpse greys, a released ghost goes blue,
|
||
-- a fifth of health or less runs red, and anything else is untinted.
|
||
local TOT_PORTRAIT_TINT = {
|
||
dead = { r = 0.35, g = 0.35, b = 0.35 },
|
||
ghost = { r = 0.2, g = 0.2, b = 0.75 },
|
||
hurt = { r = 1.0, g = 0.0, b = 0.0 },
|
||
alive = { r = 1.0, g = 1.0, b = 1.0 },
|
||
}
|
||
|
||
-- ref TargetofTargetHealthCheck (l.569-585), the health bar's OnValueChanged: the
|
||
-- portrait tints with the unit's state — grey dead, blue ghost, red under a fifth of its
|
||
-- health, white otherwise. PLAYERS ONLY, which is the reference's gate and not an
|
||
-- oversight: a creature's portrait carries no such reading anywhere in 1.12.
|
||
function TargetofTargetHealthCheck()
|
||
if not UnitIsPlayer("targettarget") then return end
|
||
local bar = this
|
||
local _, maxHP = bar:GetMinMaxValues()
|
||
local percent = 0
|
||
if maxHP > 0 then
|
||
percent = bar:GetValue() / maxHP
|
||
end
|
||
bar:GetParent().unitHPPercent = percent
|
||
-- The reading, then one paint. Priority is the reference's ladder: dead outranks
|
||
-- ghost outranks hurt, and anything else is untinted.
|
||
local tint = TOT_PORTRAIT_TINT.alive
|
||
if UnitIsDead("targettarget") then
|
||
tint = TOT_PORTRAIT_TINT.dead
|
||
elseif UnitIsGhost("targettarget") then
|
||
tint = TOT_PORTRAIT_TINT.ghost
|
||
elseif percent > 0 and percent <= 0.2 then
|
||
tint = TOT_PORTRAIT_TINT.hurt
|
||
end
|
||
TargetofTargetPortrait:SetVertexColor(tint.r, tint.g, tint.b, 1.0)
|
||
end
|
||
|
||
-- The four debuff buttons (ref calls RefreshBuffs(this, 0, "targettarget"), BuffFrame.lua
|
||
-- l.262-312, over MAX_PARTY_DEBUFFS = 4). Same shape as the pet frame's row, plus the
|
||
-- dispel tint the party/pet rows do not carry — RefreshBuffs colours its border by
|
||
-- debuffType exactly as the target frame's row does.
|
||
function BenillaTargetofTarget_UpdateDebuffs()
|
||
for i = 1, 4 do
|
||
local button = getglobal("TargetofTargetFrameDebuff" .. i)
|
||
local _, icon, _, debuffType = UnitDebuff("targettarget", i)
|
||
if icon then
|
||
getglobal(button:GetName() .. "Icon"):SetTexture(icon)
|
||
local color = DebuffTypeColor[debuffType] or DebuffTypeColor["none"]
|
||
getglobal(button:GetName() .. "Border"):SetVertexColor(color.r, color.g, color.b)
|
||
button:Show()
|
||
else
|
||
button:Hide()
|
||
end
|
||
end
|
||
end
|
||
|
||
function TargetofTarget_OnLoad()
|
||
local frame = this
|
||
frame.unit = "targettarget"
|
||
frame.healthbar = TargetofTargetHealthBar
|
||
frame.manabar = TargetofTargetManaBar
|
||
-- The recessed-bar look, the same mechanism every frame in this file uses (decision
|
||
-- 0884): the bars one frame level BELOW the art's TextureFrame, because the draw layer
|
||
-- is bucket-wide and the frame level is the one key term above it. The reference
|
||
-- spends its level the other way round (RaiseFrameLevel on the TextureFrame); same
|
||
-- ordering, and ours matches its siblings here.
|
||
local level = TargetofTargetTextureFrame:GetFrameLevel()
|
||
frame.healthbar:SetFrameLevel(level - 1)
|
||
frame.manabar:SetFrameLevel(level - 1)
|
||
-- The live model portrait, bound once by token (see BenillaUnitFrame_OnLoad's note):
|
||
-- the app keeps this slot's bake current, and it only bakes at all while this frame is
|
||
-- on screen (portrait/mod.rs's `"targettarget"` arm).
|
||
SetPortraitTexture(TargetofTargetPortrait, "targettarget")
|
||
-- Every event the shown frame's paint reads. The `"target"` half is not incidental:
|
||
-- the visibility law gates on the TARGET being alive, so the target's own health has
|
||
-- to reach this frame too.
|
||
for _, wire in ipairs({
|
||
"PLAYER_ENTERING_WORLD", "PLAYER_TARGET_CHANGED", "PARTY_MEMBERS_CHANGED",
|
||
"RAID_ROSTER_UPDATE", "UNIT_HEALTH", "UNIT_MAXHEALTH", "UNIT_POWER_UPDATE",
|
||
"UNIT_MAXPOWER", "UNIT_DISPLAYPOWER", "UNIT_NAME_UPDATE", "UNIT_AURA",
|
||
}) do
|
||
frame:RegisterEvent(wire)
|
||
end
|
||
frame:EnableMouse(true)
|
||
TargetofTarget_Update()
|
||
end
|
||
|
||
-- Every UNIT_* event this frame registers names a unit in arg1, and exactly two of them
|
||
-- concern it: its own, and the target whose health gates its visibility. Everything else
|
||
-- (a party member's tick, the player's own) is somebody else's update.
|
||
function TargetofTarget_OnEvent(event)
|
||
if string.sub(event, 1, 5) == "UNIT_"
|
||
and arg1 ~= "targettarget" and arg1 ~= "target" then
|
||
return
|
||
end
|
||
TargetofTarget_Update()
|
||
end
|
||
|
||
-- ref TargetofTarget_OnClick (l.543-557).
|
||
function TargetofTarget_OnClick(button)
|
||
local armed = SpellIsTargeting()
|
||
if button ~= "LeftButton" then
|
||
-- Right-click cancels an armed spell and does nothing else: unlike the player,
|
||
-- target and pet frames, this one has no unit menu in the reference.
|
||
if armed and button == "RightButton" then SpellStopTargeting() end
|
||
return
|
||
end
|
||
-- Most specific first, the pet frame's order: an armed spell binds to the unit, then
|
||
-- a held item drops on it, and with an empty cursor the click SELECTS it — which is
|
||
-- the whole point of the frame, and the one leg the target frame's click does not have.
|
||
if armed then
|
||
SpellTargetUnit("targettarget")
|
||
return
|
||
end
|
||
if CursorHasItem() then
|
||
DropItemOnUnit("targettarget")
|
||
return
|
||
end
|
||
TargetUnit("targettarget")
|
||
end
|
||
|
||
-- ref TargetFrame_OnUpdate (l.256-260) — the one thing the event feed cannot carry. A
|
||
-- token that goes away is CLEARED, not announced (ui_unit.rs: "clearing a token isn't a
|
||
-- UNIT_* event"), so this reconciles the frame's shown state against the token's existence
|
||
-- once per frame. It runs only while the target frame itself is shown, which is only while
|
||
-- you have a target — the cost is one compare, and it is the reference's own line.
|
||
-- Both sides are normalised to booleans on purpose. They happen to agree today (a frame's
|
||
-- IsShown answers true/false, and so does UnitExists), but a REGION's IsShown answers the
|
||
-- 1.12 shape of 1/nil, and the day UnitExists is moved onto it a bare `~=` here would be
|
||
-- true forever — a silent per-frame full repaint, which is the one thing this line exists
|
||
-- to avoid.
|
||
function TargetFrame_OnUpdate()
|
||
local shown = TargetofTargetFrame:IsShown() and true or false
|
||
local exists = UnitExists("targettarget") and true or false
|
||
if shown ~= exists then
|
||
TargetofTarget_Update()
|
||
end
|
||
end
|
||
]]></Script>
|
||
|
||
<!-- 93×45 at the target frame's BOTTOMRIGHT (-35,-10) — ref TargetFrame.xml l.515-525. A
|
||
Frame, not the reference's Button: every unit frame in this file is one, and this engine
|
||
dispatches OnClick on a mouse-enabled frame (see TargetFrame/PetFrame above). -->
|
||
<Frame name="TargetofTargetFrame" parent="TargetFrame" hidden="true" frameStrata="LOW">
|
||
<Size><AbsDimension x="93" y="45"/></Size>
|
||
<Anchors>
|
||
<Anchor point="BOTTOMRIGHT" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="-35" y="-10"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<!-- The bars' black trough, 46×15 BOTTOMLEFT (42,13) (ref l.528-541). Its alpha is
|
||
what CheckDead dims. -->
|
||
<Texture name="TargetofTargetBackground">
|
||
<Size><AbsDimension x="46" y="15"/></Size>
|
||
<Anchors>
|
||
<Anchor point="BOTTOMLEFT" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="42" y="13"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Color r="0" g="0" b="0" a="0.5"/>
|
||
</Texture>
|
||
<!-- Portrait 35×35 TOPLEFT (6,-6) (ref l.543-556, BORDER→BACKGROUND for the same
|
||
ring-over-portrait layering the player and target frames use). -->
|
||
<Texture name="TargetofTargetPortrait">
|
||
<Size><AbsDimension x="35" y="35"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="6" y="-6"/></Offset></Anchor>
|
||
</Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<!-- Health bar 46×7 TOPRIGHT (-2,-15), UI-StatusBar, green (ref l.593-611). -->
|
||
<StatusBar name="TargetofTargetHealthBar">
|
||
<Size><AbsDimension x="46" y="7"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPRIGHT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-2" y="-15"/></Offset></Anchor>
|
||
</Anchors>
|
||
<BarTexture file="Interface\TargetingFrame\UI-StatusBar"/>
|
||
<BarColor r="0.0" g="1.0" b="0.0"/>
|
||
<Scripts>
|
||
<OnValueChanged>TargetofTargetHealthCheck()</OnValueChanged>
|
||
</Scripts>
|
||
</StatusBar>
|
||
<!-- Power bar 46×7 TOPRIGHT (-2,-23); colour set at runtime (ref l.612-625). -->
|
||
<StatusBar name="TargetofTargetManaBar">
|
||
<Size><AbsDimension x="46" y="7"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPRIGHT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-2" y="-23"/></Offset></Anchor>
|
||
</Anchors>
|
||
<BarTexture file="Interface\TargetingFrame\UI-StatusBar"/>
|
||
<BarColor r="0.0" g="0.0" b="1.0"/>
|
||
</StatusBar>
|
||
<!-- The plate art + text, over the bars (ref TargetofTargetTextureFrame l.558-591) —
|
||
it draws over the bar edges because OnLoad pushes the bars one frame level below
|
||
it, exactly as on the player and target frames. -->
|
||
<Frame name="TargetofTargetTextureFrame">
|
||
<Size><AbsDimension x="93" y="45"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<!-- The small plate, its own art file. The TexCoords are the reference's
|
||
(l.561-563) and they cut the 128×64 file to exactly this frame's
|
||
93×45: 0.015625..0.7265625 is x 2..93, 0..0.703125 is y 0..45. -->
|
||
<Texture name="TargetofTargetTexture" file="Interface\TargetingFrame\UI-TargetofTargetFrame">
|
||
<Size><AbsDimension x="93" y="45"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
<TexCoords left="0.015625" right="0.7265625" top="0" bottom="0.703125"/>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="OVERLAY">
|
||
<!-- Name 100×10 BOTTOMLEFT (42,2) (ref l.564-576). -->
|
||
<FontString name="TargetofTargetName" inherits="GameFontNormalSmall" justifyH="LEFT">
|
||
<Size><AbsDimension x="100" y="10"/></Size>
|
||
<Anchors>
|
||
<Anchor point="BOTTOMLEFT" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="42" y="2"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
<!-- DEAD, over the trough, CENTER (15,1) (ref l.576-587). -->
|
||
<FontString name="TargetofTargetDeadText" inherits="GameFontNormalSmall" text="DEAD" hidden="true">
|
||
<Anchors>
|
||
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="15" y="1"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
</Frame>
|
||
<!-- The four debuffs, ref geometry (l.626-663): the first hangs off the frame's
|
||
TOPRIGHT, then a 2×2 block. -->
|
||
<Button name="$parentDebuff1" inherits="BenillaTargetofTargetDebuffTemplate" id="1">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="4" y="-10"/></Offset></Anchor>
|
||
</Anchors>
|
||
</Button>
|
||
<Button name="$parentDebuff2" inherits="BenillaTargetofTargetDebuffTemplate" id="2">
|
||
<Anchors>
|
||
<Anchor point="LEFT" relativeTo="TargetofTargetFrameDebuff1" relativePoint="RIGHT"><Offset><AbsDimension x="1" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
</Button>
|
||
<Button name="$parentDebuff3" inherits="BenillaTargetofTargetDebuffTemplate" id="3">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="TargetofTargetFrameDebuff1" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-1"/></Offset></Anchor>
|
||
</Anchors>
|
||
</Button>
|
||
<Button name="$parentDebuff4" inherits="BenillaTargetofTargetDebuffTemplate" id="4">
|
||
<Anchors>
|
||
<Anchor point="LEFT" relativeTo="TargetofTargetFrameDebuff3" relativePoint="RIGHT"><Offset><AbsDimension x="1" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
</Button>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnLoad><![CDATA[TargetofTarget_OnLoad()]]></OnLoad>
|
||
<OnEvent><![CDATA[TargetofTarget_OnEvent(event)]]></OnEvent>
|
||
<OnClick><![CDATA[TargetofTarget_OnClick(arg1)]]></OnClick>
|
||
</Scripts>
|
||
</Frame>
|
||
|
||
<!--
|
||
THE PET FRAME (decision 0990). Geometry and art are MEASURED off ref PetFrame.xml +
|
||
PetFrame.lua and the behaviour is theirs; the code is our own. 128×53 parented to
|
||
the player frame at TOPLEFT (80,-60) — a static anchor, deliberately: the reference does not
|
||
list PetFrame in UIPARENT_MANAGED_FRAME_POSITIONS at all, because it rides its parent.
|
||
|
||
The art is the SMALL targeting frame, and it has two states the ref swaps at runtime
|
||
(PetFrame.lua's Update): `UI-SmallTargetingFrame` when the pet has a power bar, `-NoMana`
|
||
when it has none. That is not decoration — a warlock's imp has mana and a hunter's boar has
|
||
focus, but a skeleton has neither, and the plain art draws a mana rail with nothing in it.
|
||
|
||
HAPPINESS is no longer deferred — decision 1005 built it, off the carve 0990 was waiting
|
||
for. `GetPetHappiness 0x4be900` returns a bucket the CLIENT has already computed from
|
||
PetPersonality.dbc's own three thresholds, so nothing here is derived from vmangos and
|
||
0988's warning was answered rather than dodged. Its three siblings (`GetPetLoyalty`,
|
||
`GetPetTrainingPoints`, `GetPetExperience`) landed with it.
|
||
|
||
The PET RIGHT-CLICK MENU is no longer deferred either — decision 1066 built the last of
|
||
the five things it was waiting on (the pet paper doll, the fifth, landed as 1057). The
|
||
frame's non-left-click arm opens PetFrameDropDown below; the rows, their fork and their
|
||
dialogs are UnitPopup.xml's.
|
||
|
||
STILL DEFERRED, with its reason:
|
||
- PartyMemberBuffTooltip on hover (the ref's OnEnter panel): it belongs to the party
|
||
frames' own deferred buff panel, not here.
|
||
-->
|
||
<Script><![CDATA[
|
||
function PetFrame_OnLoad()
|
||
local frame = this
|
||
frame.portrait = PetPortrait
|
||
frame.healthbar = PetFrameHealthBar
|
||
frame.manabar = PetFrameManaBar
|
||
frame.nameText = PetName
|
||
-- The recessed-bar look, the same mechanism the player/target/party frames use
|
||
-- (decision 0884): push the bars one frame level BELOW the art's TextureFrame, because
|
||
-- the draw layer is bucket-wide and the frame level is the one key term above it.
|
||
local level = PetFrameTextureFrame:GetFrameLevel()
|
||
frame.healthbar:SetFrameLevel(level - 1)
|
||
frame.manabar:SetFrameLevel(level - 1)
|
||
-- The pet's numerals, textLockable like the ref's (PetFrame.xml l.139/164; 1143).
|
||
if TextStatusBar_Initialize then -- engine tests load file subsets
|
||
TextStatusBar_Initialize(frame.healthbar)
|
||
TextStatusBar_Initialize(frame.manabar)
|
||
SetTextStatusBarText(frame.healthbar, PetFrameHealthBarText)
|
||
SetTextStatusBarText(frame.manabar, PetFrameManaBarText)
|
||
frame.healthbar.textLockable = 1
|
||
frame.manabar.textLockable = 1
|
||
end
|
||
-- The pet's bars hover for their numbers, but hand nothing on: the pet frame has no
|
||
-- OnEnter of its own, and its click is its own handler (1146).
|
||
frame.healthbar.ownerClick = PetFrame_OnClick
|
||
frame.manabar.ownerClick = PetFrame_OnClick
|
||
frame.attackModeCounter = 0
|
||
frame.attackModeSign = -1
|
||
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||
frame:RegisterEvent("UNIT_PET")
|
||
frame:RegisterEvent("UNIT_AURA")
|
||
frame:RegisterEvent("UNIT_HEALTH")
|
||
frame:RegisterEvent("UNIT_MAXHEALTH")
|
||
frame:RegisterEvent("UNIT_POWER_UPDATE")
|
||
frame:RegisterEvent("UNIT_MAXPOWER")
|
||
frame:RegisterEvent("UNIT_DISPLAYPOWER")
|
||
frame:RegisterEvent("UNIT_NAME_UPDATE")
|
||
frame:RegisterEvent("PET_ATTACK_START")
|
||
frame:RegisterEvent("PET_ATTACK_STOP")
|
||
frame:RegisterEvent("UNIT_HAPPINESS")
|
||
frame:EnableMouse(true)
|
||
PetFrame_Update()
|
||
end
|
||
|
||
-- ref PetFrame_OnEvent, with both of its argument gates kept: UNIT_PET names the OWNER
|
||
-- ("player" — wow-re §9 carves the arg), every other UNIT_* names the pet itself. Getting
|
||
-- those backwards is how a pet frame ends up repainting off the player's health.
|
||
function PetFrame_OnEvent(event)
|
||
local frame = this
|
||
if event == "PET_ATTACK_START" then
|
||
PetAttackModeTexture:SetVertexColor(1.0, 1.0, 1.0, 1.0)
|
||
PetAttackModeTexture:Show()
|
||
return
|
||
elseif event == "PET_ATTACK_STOP" then
|
||
PetAttackModeTexture:Hide()
|
||
return
|
||
elseif event == "UNIT_HAPPINESS" then
|
||
-- The ref's own last arm: repaint the icon alone, not the whole frame.
|
||
PetFrame_SetHappiness()
|
||
return
|
||
elseif event == "UNIT_PET" then
|
||
if arg1 ~= "player" then return end
|
||
elseif string.sub(event, 1, 5) == "UNIT_" and arg1 ~= "pet" then
|
||
return
|
||
end
|
||
PetFrame_Update()
|
||
end
|
||
|
||
function PetFrame_Update()
|
||
local frame = this
|
||
if not UnitExists("pet") then
|
||
frame:Hide()
|
||
return
|
||
end
|
||
frame:Show()
|
||
SetPortraitTexture(frame.portrait, "pet")
|
||
frame.nameText:SetText(UnitName("pet") or "")
|
||
|
||
-- The ref's art swap (PetFrame.lua's Update): a powerless pet wears the -NoMana plate,
|
||
-- which has no mana rail to leave empty. Which plate is exactly the shared paint's
|
||
-- return — the pet is the only frame here whose art carries that distinction.
|
||
if BenillaUnitFrame_PaintBars(frame.healthbar, frame.manabar, "pet") then
|
||
PetFrameTexture:SetTexture("Interface\\TargetingFrame\\UI-SmallTargetingFrame")
|
||
else
|
||
PetFrameTexture:SetTexture("Interface\\TargetingFrame\\UI-SmallTargetingFrame-NoMana")
|
||
end
|
||
|
||
BenillaPetFrame_UpdateDebuffs()
|
||
PetFrame_SetHappiness()
|
||
end
|
||
|
||
-- ── The happiness face (decision 1005; ref PetFrame_SetHappiness, PetFrame.lua l.132-156)
|
||
--
|
||
-- `Interface\PetPaperDollFrame\UI-PetHappiness` is one row of three faces, and each cut is
|
||
-- a MEASURED value off that file. The buckets run RIGHT to left across the strip — bucket
|
||
-- 1 (unhappy) takes the rightmost cut and bucket 3 (happy) sits at x=0 — which is why the
|
||
-- left edges below descend as the index climbs. A bucket outside 1..3 selects no cell at
|
||
-- all and the icon keeps whatever face it was already wearing, deliberately: see the note
|
||
-- on bucket 0 below.
|
||
local PET_HAPPINESS_CELL = {
|
||
{ 0.375, 0.5625, 0, 0.359375 },
|
||
{ 0.1875, 0.375, 0, 0.359375 },
|
||
{ 0, 0.1875, 0, 0.359375 },
|
||
}
|
||
|
||
-- `GetPetHappiness` hands back a PRE-BUCKETED 1/2/3 — the client does the thresholding
|
||
-- against PetPersonality.dbc, not this function — plus the two numbers that bucket
|
||
-- selects. Three things here are load-bearing and each is the reference's own:
|
||
-- * the hide test is on the bucket being ABSENT, not on it being zero, so a bucket of
|
||
-- **0** (truthy in Lua) leaves the icon SHOWING with whatever texcoords it had.
|
||
-- Testing `happiness == 0` or coercing 0 to nil would hide a frame the reference shows.
|
||
-- * only a HUNTER's pet has a face at all — `HasPetUI`'s second return is the whole gate,
|
||
-- and its first return is nothing to do with this frame.
|
||
-- * the loyalty line is chosen by the SIGN of loyaltyRate, which is why that return is
|
||
-- unscaled and allowed to be negative. Exactly zero means "steady", and says nothing.
|
||
function PetFrame_SetHappiness()
|
||
local happiness, damagePercentage, loyaltyRate = GetPetHappiness()
|
||
local _, isHunterPet = HasPetUI()
|
||
if not (happiness and isHunterPet) then
|
||
PetFrameHappiness:Hide()
|
||
return
|
||
end
|
||
PetFrameHappiness:Show()
|
||
|
||
local cell = PET_HAPPINESS_CELL[happiness]
|
||
if cell then
|
||
PetFrameHappinessTexture:SetTexCoord(cell[1], cell[2], cell[3], cell[4])
|
||
end
|
||
|
||
PetFrameHappiness.tooltip = getglobal("PET_HAPPINESS" .. happiness)
|
||
PetFrameHappiness.tooltipDamage = format(PET_DAMAGE_PERCENTAGE, damagePercentage)
|
||
if loyaltyRate < 0 then
|
||
PetFrameHappiness.tooltipLoyalty = getglobal("LOSING_LOYALTY")
|
||
elseif loyaltyRate > 0 then
|
||
PetFrameHappiness.tooltipLoyalty = getglobal("GAINING_LOYALTY")
|
||
else
|
||
PetFrameHappiness.tooltipLoyalty = nil
|
||
end
|
||
end
|
||
|
||
-- The happiness face's hover. Both extra lines are drawn the same way — plain white,
|
||
-- nothing in the right-hand column — and either can be absent, so the stanza is written
|
||
-- once. The whole tooltip waits on the headline: until SetHappiness has run there is
|
||
-- nothing to say, and an empty plate is worse than none.
|
||
local function AddHappinessTooltipLine(text)
|
||
if text then
|
||
GameTooltip:AddLine(text, "", 1, 1, 1)
|
||
end
|
||
end
|
||
|
||
function BenillaPetHappiness_OnEnter(frame)
|
||
if not frame.tooltip then
|
||
return
|
||
end
|
||
GameTooltip:SetOwner(frame, "ANCHOR_RIGHT")
|
||
GameTooltip:SetText(frame.tooltip)
|
||
AddHappinessTooltipLine(frame.tooltipDamage)
|
||
AddHappinessTooltipLine(frame.tooltipLoyalty)
|
||
GameTooltip:Show()
|
||
end
|
||
|
||
-- The four debuff buttons (ref PetFrame.xml's $parentDebuff1..4 over RefreshBuffs). The pet
|
||
-- carries no aura timers — no unit but yourself does on the 1.12 wire (decision 0257 B6) —
|
||
-- so these are icon + border + stack count, the party rows' shape exactly.
|
||
function BenillaPetFrame_UpdateDebuffs()
|
||
for i = 1, 4 do
|
||
local button = getglobal("PetFrameDebuff" .. i)
|
||
local _, icon, count = UnitDebuff("pet", i)
|
||
if icon then
|
||
getglobal(button:GetName() .. "Icon"):SetTexture(icon)
|
||
local text = getglobal(button:GetName() .. "Count")
|
||
if count and count > 1 then
|
||
text:SetText(count)
|
||
text:Show()
|
||
else
|
||
text:Hide()
|
||
end
|
||
button:Show()
|
||
else
|
||
button:Hide()
|
||
end
|
||
end
|
||
end
|
||
|
||
-- ref PetFrame_OnUpdate: the attack overlay's alpha ramp — a 0.5 s-per-leg triangle between
|
||
-- 55/255 and 1, sign-flipped at each end (the ref's own counter shape, transcribed).
|
||
function PetFrame_OnUpdate(elapsed)
|
||
local frame = this
|
||
if not PetAttackModeTexture:IsVisible() then return end
|
||
local counter = frame.attackModeCounter + elapsed
|
||
local sign = frame.attackModeSign
|
||
if counter > 0.5 then
|
||
sign = -sign
|
||
frame.attackModeSign = sign
|
||
end
|
||
counter = mod(counter, 0.5)
|
||
frame.attackModeCounter = counter
|
||
local alpha
|
||
if sign == 1 then
|
||
alpha = (55 + (counter * 400)) / 255
|
||
else
|
||
alpha = (255 - (counter * 400)) / 255
|
||
end
|
||
PetAttackModeTexture:SetVertexColor(1.0, 1.0, 1.0, alpha)
|
||
end
|
||
|
||
-- Clicking the pet frame (decision 1066 built the right-hand half). The LEFT button hands
|
||
-- the pet whatever the cursor is carrying; every other button opens its menu — except for
|
||
-- the one case that is easy to drop, a RIGHT click with a spell already armed, which puts
|
||
-- the spell away instead. That exception is the right button's alone: a middle click while
|
||
-- casting still opens the menu.
|
||
function PetFrame_OnClick(button)
|
||
if button ~= "LeftButton" then
|
||
if button == "RightButton" and SpellIsTargeting() then
|
||
SpellStopTargeting()
|
||
else
|
||
-- No anchor arguments, unlike the player and target frames: this menu opens at
|
||
-- the CURSOR (ref PetFrame.lua l.128).
|
||
ToggleDropDownMenu(1, nil, PetFrameDropDown)
|
||
end
|
||
return
|
||
end
|
||
-- Most specific first: an armed spell binds to the pet, then a held item drops on it
|
||
-- (this is how you feed one), and with an empty cursor the click simply selects it.
|
||
if SpellIsTargeting() then
|
||
SpellTargetUnit("pet")
|
||
elseif CursorHasItem() then
|
||
DropItemOnUnit("pet")
|
||
else
|
||
TargetUnit("pet")
|
||
end
|
||
end
|
||
|
||
-- The PET menu's owner (ref PetFrame.lua l.158-166). The UnitExists gate is the ref's own,
|
||
-- and it is not redundant with the frame being hidden: this dropdown is also what the
|
||
-- TARGET frame's menu falls through to when you have your own pet selected.
|
||
function PetFrameDropDown_OnLoad()
|
||
UIDropDownMenu_Initialize(this, PetFrameDropDown_Initialize, "MENU")
|
||
end
|
||
|
||
function PetFrameDropDown_Initialize()
|
||
if UnitExists("pet") then
|
||
UnitPopup_ShowMenu(PetFrameDropDown, "PET", "pet")
|
||
end
|
||
end
|
||
|
||
function BenillaPetFrame_OnEnter(frame)
|
||
-- The pet frame is a unit frame in the reference, and takes the same cursor split:
|
||
-- hovering it with a spell armed lights or greys exactly as the target frame does.
|
||
if SpellIsTargeting() then
|
||
if SpellCanTargetUnit("pet") then
|
||
SetCursor("CAST_CURSOR")
|
||
else
|
||
SetCursor("CAST_ERROR_CURSOR")
|
||
end
|
||
end
|
||
if not UnitExists("pet") then return end
|
||
GameTooltip_SetDefaultAnchor(GameTooltip, frame)
|
||
if GameTooltip:SetUnit("pet") then
|
||
getglobal("GameTooltipTextLeft1"):SetTextColor(GameTooltip_UnitColor("pet"))
|
||
end
|
||
end
|
||
]]></Script>
|
||
|
||
<!-- The debuff button — ref PartyBuffButtonTemplate (PartyFrameTemplates.xml l.3-35), which is
|
||
what the ref's PetFrame inherits for its four: a 15×15 icon under a 17×17
|
||
UI-Debuff-Overlays border. Untinted: the ref's party/pet buttons never recolor it (only the
|
||
target frame's dispel-tint law does), so this stays the flat swatch the TexCoords select. -->
|
||
<Button name="BenillaPetDebuffTemplate" virtual="true" hidden="true">
|
||
<Size><AbsDimension x="15" y="15"/></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="17" y="17"/></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><![CDATA[
|
||
if GameTooltip then
|
||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||
GameTooltip:SetUnitDebuff("pet", this:GetID())
|
||
end
|
||
]]></OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Button>
|
||
|
||
<!-- frameStrata LOW matches the party frames' and the target's — above the player frame's
|
||
BACKGROUND (which it overlaps), still below the MEDIUM panels. -->
|
||
<Frame name="PetFrame" parent="PlayerFrame" hidden="true" frameStrata="LOW">
|
||
<Size><AbsDimension x="128" y="53"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="80" y="-60"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Frames>
|
||
<!-- The PET-menu dropdown host (ref $parentDropDown, PetFrame.xml l.35-51): a hidden
|
||
state-holder, like the player and target frames'. It carries the ref's own id=1
|
||
and its 10×10 size; nothing anchors to it, because this menu opens at the cursor.
|
||
It lives INSIDE the pet frame, which means it is only ever initialized once the
|
||
pet frame exists — the same nesting the reference uses. -->
|
||
<Frame name="PetFrameDropDown" inherits="UIDropDownMenuTemplate" id="1" hidden="true">
|
||
<Size><AbsDimension x="10" y="10"/></Size>
|
||
<Anchors><Anchor point="TOP"><Offset><AbsDimension x="-20" y="-20"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnLoad>PetFrameDropDown_OnLoad()</OnLoad>
|
||
</Scripts>
|
||
</Frame>
|
||
<!-- Bars first, pushed one frame level under the TextureFrame at load (see OnLoad). -->
|
||
<StatusBar name="$parentHealthBar" enableMouse="true">
|
||
<Size><AbsDimension x="70" y="8"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="47" y="-22"/></Offset></Anchor></Anchors>
|
||
<BarTexture file="Interface\TargetingFrame\UI-StatusBar"/>
|
||
<BarColor r="0" g="1.0" b="0"/>
|
||
<Scripts>
|
||
<OnEvent>if TextStatusBar_OnEvent then TextStatusBar_OnEvent(arg1, arg2) end</OnEvent>
|
||
<OnValueChanged>if TextStatusBar_OnValueChanged then TextStatusBar_OnValueChanged() end</OnValueChanged>
|
||
<OnEnter>BenillaUnitFrameBar_OnEnter(self)</OnEnter>
|
||
<OnLeave>BenillaUnitFrameBar_OnLeave(self)</OnLeave>
|
||
<OnMouseUp>BenillaUnitFrameBar_OnMouseUp(self, arg1)</OnMouseUp>
|
||
</Scripts>
|
||
</StatusBar>
|
||
<StatusBar name="$parentManaBar" enableMouse="true">
|
||
<Size><AbsDimension x="70" y="8"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="47" y="-29"/></Offset></Anchor></Anchors>
|
||
<BarTexture file="Interface\TargetingFrame\UI-StatusBar"/>
|
||
<BarColor r="0" g="0" b="1.0"/>
|
||
<Scripts>
|
||
<OnEvent>if TextStatusBar_OnEvent then TextStatusBar_OnEvent(arg1, arg2) end</OnEvent>
|
||
<OnValueChanged>if TextStatusBar_OnValueChanged then TextStatusBar_OnValueChanged() end</OnValueChanged>
|
||
<OnEnter>BenillaUnitFrameBar_OnEnter(self)</OnEnter>
|
||
<OnLeave>BenillaUnitFrameBar_OnLeave(self)</OnLeave>
|
||
<OnMouseUp>BenillaUnitFrameBar_OnMouseUp(self, arg1)</OnMouseUp>
|
||
</Scripts>
|
||
</StatusBar>
|
||
<Frame name="$parentTextureFrame">
|
||
<Size><AbsDimension x="128" y="53"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<!-- Portrait 37×37 TOPLEFT (7,-6), under the art's round window (ref
|
||
PetPortrait, PetFrame.xml l.16-27). -->
|
||
<Texture name="PetPortrait">
|
||
<Size><AbsDimension x="37" y="37"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="7" y="-6"/></Offset></Anchor></Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="BORDER">
|
||
<!-- 128×64 art at (0,-2) — it hangs 13 px below the frame rect, as
|
||
authored. The file is swapped at update (see the header). -->
|
||
<Texture name="PetFrameTexture" file="Interface\TargetingFrame\UI-SmallTargetingFrame">
|
||
<Size><AbsDimension x="128" y="64"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="ARTWORK">
|
||
<!-- The attacking overlay (ref PetAttackModeTexture, l.104-118): the right
|
||
29.7% of UI-Player-AttackStatus, ADD-blended, pulsed by OnUpdate. -->
|
||
<Texture name="PetAttackModeTexture" file="Interface\TargetingFrame\UI-Player-AttackStatus" alphaMode="ADD" hidden="true">
|
||
<Size><AbsDimension x="76" y="64"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="6" y="-9"/></Offset></Anchor></Anchors>
|
||
<TexCoords left="0.703125" right="1.0" top="0" bottom="1.0"/>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="OVERLAY">
|
||
<FontString name="PetName" inherits="GameFontNormalSmall" justifyH="LEFT">
|
||
<Anchors><Anchor point="BOTTOMLEFT"><Offset><AbsDimension x="50" y="33"/></Offset></Anchor></Anchors>
|
||
</FontString>
|
||
<!-- The pet's numerals: the reference's LITERAL seats, CENTER-from-TOPLEFT
|
||
(82,-27) and (82,-38) (PetFrame.xml l.87-104). 1143 anchored these to
|
||
the bars' own centres instead, on a comparison that was simply wrong —
|
||
it read the ref's TEXT offsets against OUR BAR offsets and called the
|
||
pitch different. The bars are byte-identical to the reference's, 70x8
|
||
at (47,-22) and (47,-29), so the ref's text literals apply unchanged.
|
||
The overlap that produced (director report, 2026-08-08, decision 1147)
|
||
is what the literals exist to avoid: a 7 px bar pitch has no room for
|
||
two centred ~12 px strings, so the reference drops the SECOND one clear
|
||
of its bar (the mana bar ends at -37; its numerals sit at -38, just
|
||
under it) rather than centring it. Centring both is what collided.
|
||
No 1145 nudge here: that pixel was for a string centred in a bar, and
|
||
the reference has already placed these deliberately off-centre. -->
|
||
<FontString name="PetFrameHealthBarText" inherits="TextStatusBarText">
|
||
<Anchors><Anchor point="CENTER" relativePoint="TOPLEFT"><Offset><AbsDimension x="82" y="-27"/></Offset></Anchor></Anchors>
|
||
</FontString>
|
||
<FontString name="PetFrameManaBarText" inherits="TextStatusBarText">
|
||
<Anchors><Anchor point="CENTER" relativePoint="TOPLEFT"><Offset><AbsDimension x="82" y="-38"/></Offset></Anchor></Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
</Frame>
|
||
<!-- HAPPINESS (ref PetFrameHappiness, PetFrame.xml l.233-253; decision 1005). 24×23
|
||
anchored LEFT to the frame's RIGHT at (-7,-4), so it sits just inside the plate's
|
||
right edge. The file is a three-icon strip and the AUTHORED TexCoords are
|
||
happiness 3's — the ref ships the happy face and re-cuts it at update. -->
|
||
<Frame name="$parentHappiness" enableMouse="true">
|
||
<Size><AbsDimension x="24" y="23"/></Size>
|
||
<Anchors><Anchor point="LEFT" relativePoint="RIGHT"><Offset><AbsDimension x="-7" y="-4"/></Offset></Anchor></Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="$parentTexture" file="Interface\PetPaperDollFrame\UI-PetHappiness" setAllPoints="true">
|
||
<Size><AbsDimension x="24" y="23"/></Size>
|
||
<TexCoords left="0" right="0.1875" top="0" bottom="0.359375"/>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<Scripts>
|
||
<OnEnter>
|
||
BenillaPetHappiness_OnEnter(this)
|
||
</OnEnter>
|
||
<OnLeave>
|
||
GameTooltip:Hide()
|
||
</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
<!-- The four debuffs, ref geometry: the first at (48,-42), then +2 px chained right. -->
|
||
<Button name="$parentDebuff1" inherits="BenillaPetDebuffTemplate" id="1">
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="48" y="-42"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="$parentDebuff2" inherits="BenillaPetDebuffTemplate" id="2">
|
||
<Anchors><Anchor point="LEFT" relativeTo="PetFrameDebuff1" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="$parentDebuff3" inherits="BenillaPetDebuffTemplate" id="3">
|
||
<Anchors><Anchor point="LEFT" relativeTo="PetFrameDebuff2" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
<Button name="$parentDebuff4" inherits="BenillaPetDebuffTemplate" id="4">
|
||
<Anchors><Anchor point="LEFT" relativeTo="PetFrameDebuff3" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor></Anchors>
|
||
</Button>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnLoad>PetFrame_OnLoad()</OnLoad>
|
||
<OnEvent>PetFrame_OnEvent(event)</OnEvent>
|
||
<OnUpdate>PetFrame_OnUpdate(arg1)</OnUpdate>
|
||
<OnEnter>BenillaPetFrame_OnEnter(this)</OnEnter>
|
||
<OnLeave>if SpellIsTargeting() then SetCursor("CAST_ERROR_CURSOR") end GameTooltip:FadeOut()</OnLeave>
|
||
<OnClick>PetFrame_OnClick(arg1)</OnClick>
|
||
</Scripts>
|
||
</Frame>
|
||
</Ui>
|