Files
benilla-octo/crates/benilla-app/assets/ui/PetActionBar.xml
T
SamandClaude Fable 5 a683d5524f the interface code is ours: the transcription debt is paid
- forty files that carried the reference's literal Lua are rewritten as our own expression of the behaviour — worst identical run 61 lines down to 7 — and the README now says exactly that
- the overlap gate measures the code itself across all four reference corpora, and ratchets: the number may only go down
- a lost session ends at the login screen instead of silently re-authenticating and stealing the account back
- a building's skybox is a real model: Caverns of Time's white ceiling becomes its painted nebula and additive stars

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 19:21:03 +07:00

640 lines
38 KiB
XML

<Ui>
<!--
The pet action bar (ref PetActionBarFrame.xml/.lua, extracted from patch.MPQ; decision
0982). Hunter, warlock, and every possessed/charmed unit: ten buttons over the ten packed
words the server sends in SMSG_PET_SPELLS, driven by `crate::ui_pet`'s feed through the
engine's pet seam (GetPetActionInfo & co.).
THE THING TO KNOW ABOUT THIS BAR: the server owns its CONTENTS (all ten slots arrive in
one SMSG_PET_SPELLS) but the CLIENT owns its lit state. A press repaints immediately and
is never confirmed, because the server does not answer these packets at all — the real
client writes its own state before the send (decision 0988). So a button whose ring does
not light on click is BROKEN, not patient.
The ten slots are not a layout. `GetPetActionInfo` reports each slot's own class off its
type byte, and the familiar 3 commands / 4 spells / 3 reactions arrangement is just what
vmangos's CharmInfo::InitPetActionBar happens to write; a charmed creature arrives with a
different mix through the same ten words. Nothing below indexes by position.
Token slots (the commands and reactions) return the NAMES OF GLOBALS rather than values,
which is the reference's own convention (`PetActionBarFrame.lua:98-104`): the name keys are
GlobalStrings' (PET_ACTION_ATTACK, PET_MODE_DEFENSIVE, …), and the texture globals are
declared below. Both halves of that mapping are a contract we do not get to choose — the
names are what `ui_pet/bar.rs` hands back and the icons are real files in the install — so
the specification for them is `PetActionBarFrame.lua:6-12`; see the declaration's own note
for what IS ours about the layout.
Geometry QUOTED from the ref (PetActionBarFrame.xml): frame 509x43; the two UI-PetBar shelf
strips (256x44 at TOPLEFT with TexCoords 0..1 / 0.015625..0.359375, then 184x44 to its
right with 0..0.71875 / 0.375..0.71875); buttons 30x30, button1 at frame BOTTOMLEFT +(36,2),
chained +8 (with the ref's own +7 between 6 and 7); the 54x54 UI-Quickslot2 ring CENTER
(0,-1). The bar rides `MainMenuBar` BOTTOMLEFT — our MainMenuBar, the same 1024x53
frame at screen BOTTOM the ref anchors to — at the managed PETACTIONBAR_YPOS (UIParent.xml
declares the row already).
Ref divergences, each deliberate:
- ONE refresh event. The app diffs the whole pushed bar and fires PET_BAR_UPDATE on any
change, so the ref's PET_BAR_UPDATE / PET_BAR_UPDATE_COOLDOWN / UNIT_PET / the
UNIT_FLAGS+UNIT_AURA pair it filters for arg1=="pet" collapse into one — the same
collapse StanceBar.xml makes, for the same reason.
- The drag is ONE verb. PickupPetAction serves shift-click, OnDragStart and OnReceiveDrag
alike (decision 1010) — that is the reference's own shape, not a collapse of ours: the
binding itself forks on whether the cursor is already carrying a pet payload, so there is
no "place" counterpart the way PlaceAction answers PickupAction on the main bar. Nothing
else can produce that payload, which is why the pet bar can be rearranged and never
populated: its contents are the server's.
- PET_BAR_SHOWGRID/HIDEGRID are live, and are the PET payload's alone. The engine derives
them off the same cursor transition that drives ACTIONBAR_SHOWGRID/HIDEGRID but on a
disjoint predicate, so a held spell lights the action bar's empty slots and a held pet
action lights this bar's — never both, because neither payload can land on the other bar.
- NO HotKey corner, and none is missing (1052). The ref's button inherits
ActionButtonTemplate's $parentHotKey and repaints it on UPDATE_BINDINGS — but
PetActionButton_SetHotkeys (l.311-319) writes the label only when
`bindingSuffix == this:GetID()`, a STRING against a NUMBER, which Lua never equates. So
the real 1.12 pet bar renders an empty hotkey on every slot, always, and the reporter's
own reference shot for B218 shows exactly that: CTRL-1..CTRL-5 bound in the window, no
glyph on any pet button. An empty corner is the faithful render; a filled one would be
the divergence. (Our own action bar's corners are real — ActionBar.xml — because the
ref's ActionButton_UpdateHotkeys has no such bug.)
- The autocast sparkle is TEXTURES, not the ref's <Model>. benilla has no Model widget
(the Cooldown and the bag ItemAnim took the same posture): model the mechanism, let our
own renderer draw it. The numbers below are the shipped model's own — see BENILLA_PET_
AUTOCAST_* .
-->
<Script><![CDATA[
NUM_PET_ACTION_SLOTS = 10
-- ── The token icons ───────────────────────────────────────────────────────────────────
--
-- These are GLOBALS on purpose, and the names are a contract, not a choice:
-- `GetPetActionInfo` hands back the NAME of one of them for a token slot and the paint
-- below resolves it with getglobal — the reference's own convention, and the one
-- `ui_pet/bar.rs` writes against. The icon paths are the reference's too; they name real
-- .blp files in the install, so there is nothing to choose there either.
--
-- What IS ours is the grouping. The bar's tokens come in two families and `ui_pet/bar.rs`
-- already separates them at the seam that names these — `command_token` (the orders you
-- give, keyed `PET_ACTION_*`) and `reaction_token` (the stance the pet holds, keyed
-- `PET_MODE_*`) — so they are laid out in those two families, in that file's own order,
-- and the two halves of the mapping read side by side. The reference keeps them in one
-- undifferentiated block, which hides the split.
-- Commands — the four orders, in `ui_pet/bar.rs`'s `command_token` order.
PET_WAIT_TEXTURE = "Interface\\Icons\\Spell_Nature_TimeStop"
PET_FOLLOW_TEXTURE = "Interface\\Icons\\Ability_Tracking"
PET_ATTACK_TEXTURE = "Interface\\Icons\\Ability_GhoulFrenzy"
PET_DISMISS_TEXTURE = "Interface\\Icons\\Spell_Shadow_Teleport"
-- Reactions — the three stances, in `ui_pet/bar.rs`'s `reaction_token` order.
PET_PASSIVE_TEXTURE = "Interface\\Icons\\Ability_Seal"
PET_DEFENSIVE_TEXTURE = "Interface\\Icons\\Ability_Defend"
PET_AGGRESSIVE_TEXTURE = "Interface\\Icons\\Ability_Racial_BloodRage"
-- ── The autocast sparkle ──────────────────────────────────────────────────────────────
--
-- The reference's "autocast is on" mark is a <Model> playing
-- Interface\Buttons\UI-AutoCastButton.mdx, and what that model actually is came out of the
-- shipped .m2 with `benilla-extract m2bones`/`m2part`:
--
-- 4 bones, pivoting at the corners of a 0.02 x 0.02 unit square, each keyed with the
-- SAME 5-key 2.000 s translation loop walking the square edge by edge (0.5 s per edge) —
-- the four phase-shifted by one corner each, so they chase one another round it.
-- 4 particle emitters, one per bone: 300/s, life 1.000 s, Add blend, unlit, texture
-- Interface\Buttons\GlowStar.blp, size half-extent 0.005 -> 0.002 -> 0.001 over life,
-- colour (0.976,0.875,0.192,1) -> (0.996,0.945,0.745,1) -> (1,1,1,0). Its spline has 4
-- control points ALL AT THE ORIGIN, so a particle never travels: it is born where the
-- bone is and shrinks away there. The effect is therefore four comet trails running
-- round the button's rim, not a rotating ring.
--
-- Model units -> pixels is the same 1200 px/unit calibration BagFrame.xml derived from the
-- Cooldown model (its own header carries the derivation), and it CROSS-CHECKS here: the
-- lap square is 0.02 units, the ref's Model carries scale="1.2", and 0.02 * 1200 * 1.2 =
-- 28.8 px — the rim of a 30 px button, which is where the sparkles visibly run. That
-- agreement between two independently-authored UI models is the reason to trust the
-- number rather than eyeball a size.
--
-- OUR ONE DEPARTURE is the particle COUNT: 300 live per emitter is 12 000 quads across a
-- full bar, for a trail that reads identically at a fraction of that. We draw
-- BENILLA_PET_AUTOCAST_TRAIL per emitter, evenly spaced in AGE, which samples the same
-- position/size/colour ramps at the same times — the trail's shape, speed and colour are
-- the file's; only its density is ours.
BENILLA_PET_AUTOCAST_PX_PER_UNIT = 1200
BENILLA_PET_AUTOCAST_SCALE = 1.2 -- the ref Model's
BENILLA_PET_AUTOCAST_PERIOD = 2.0 -- seq0 loop, seconds
BENILLA_PET_AUTOCAST_LIFE = 1.0 -- particle lifespan
BENILLA_PET_AUTOCAST_TRAIL = 8 -- ours (see above)
BENILLA_PET_AUTOCAST_EMITTERS = 4
-- 0.02 units square, in pixels.
BENILLA_PET_AUTOCAST_SIZE = 0.02 * BENILLA_PET_AUTOCAST_PX_PER_UNIT * BENILLA_PET_AUTOCAST_SCALE
-- Half-extent -> full width, in pixels: {0.005, 0.002, 0.001} * 2 * K * scale.
BENILLA_PET_AUTOCAST_STAR = {
0.010 * BENILLA_PET_AUTOCAST_PX_PER_UNIT * BENILLA_PET_AUTOCAST_SCALE,
0.004 * BENILLA_PET_AUTOCAST_PX_PER_UNIT * BENILLA_PET_AUTOCAST_SCALE,
0.002 * BENILLA_PET_AUTOCAST_PX_PER_UNIT * BENILLA_PET_AUTOCAST_SCALE,
}
-- The over-life colour ramp's three keys (the file's), as {r, g, b, a}.
BENILLA_PET_AUTOCAST_COLOR = {
{0.976, 0.875, 0.192, 1.0},
{0.996, 0.945, 0.745, 1.0},
{1.000, 1.000, 1.000, 0.0},
}
-- Where an emitter sits at lap fraction `f` (0..1), as an offset from the button's CENTER
-- in pixels. The bone tracks walk the square anticlockwise from its bottom-left corner,
-- one edge per quarter-lap; this is that walk, re-centred (the model's square runs 0..0.02
-- in both axes, so its centre is at 0.01).
function BenillaPetAutocast_Point(f)
local h = BENILLA_PET_AUTOCAST_SIZE / 2
f = f - math.floor(f) -- wrap into 0..1
local edge = f * 4
local leg = math.floor(edge)
local t = edge - leg -- 0..1 along this edge
if leg == 0 then return -h, -h + t * 2 * h -- bottom-left -> top-left
elseif leg == 1 then return -h + t * 2 * h, h -- top-left -> top-right
elseif leg == 2 then return h, h - t * 2 * h -- top-right -> bottom-right
else return h - t * 2 * h, -h -- bottom-right -> bottom-left
end
end
-- Sample a 3-key over-life ramp (the M2's own {k0, k1 at mid, k2} shape, linear between).
function BenillaPetAutocast_Ramp(keys, age)
local a, b, t
if age <= 0.5 then a, b, t = keys[1], keys[2], age * 2
else a, b, t = keys[2], keys[3], (age - 0.5) * 2 end
if type(a) == "number" then return a + (b - a) * t end
local out = {}
for i = 1, table.getn(a) do out[i] = a[i] + (b[i] - a[i]) * t end
return out
end
-- Build one button's sparkle pool: EMITTERS x TRAIL textures, hidden until autocast is on.
function BenillaPetAutocast_Create(button)
button.sparks = {}
for i = 1, BENILLA_PET_AUTOCAST_EMITTERS * BENILLA_PET_AUTOCAST_TRAIL do
local t = button:CreateTexture(nil, "OVERLAY")
t:SetTexture("Interface\\Buttons\\GlowStar")
t:SetBlendMode("ADD")
t:Hide()
table.insert(button.sparks, t)
end
end
-- Advance one button's sparkles to the bar's shared clock. Shared on purpose: the four
-- emitters are one model, and every autocasting button plays the same loop in step, which
-- is what the reference's per-button Models do too (each starts at sequence time 0 and
-- they never drift, the loop being exact).
function BenillaPetAutocast_Update(button, clock)
if not button.sparks then return end
if not button.autocasting then
for i = 1, table.getn(button.sparks) do button.sparks[i]:Hide() end
return
end
local n = 1
for e = 0, BENILLA_PET_AUTOCAST_EMITTERS - 1 do
-- The four bones are the same lap, a quarter apart.
local phase = clock / BENILLA_PET_AUTOCAST_PERIOD + e / BENILLA_PET_AUTOCAST_EMITTERS
for k = 0, BENILLA_PET_AUTOCAST_TRAIL - 1 do
-- Evenly spaced in AGE: this star was emitted `age` seconds ago, so it sits
-- where the bone was then and has aged that far along the ramps.
local age = (k / BENILLA_PET_AUTOCAST_TRAIL) * BENILLA_PET_AUTOCAST_LIFE
local x, y = BenillaPetAutocast_Point(phase - age / BENILLA_PET_AUTOCAST_PERIOD)
local life = age / BENILLA_PET_AUTOCAST_LIFE
local size = BenillaPetAutocast_Ramp(BENILLA_PET_AUTOCAST_STAR, life)
local c = BenillaPetAutocast_Ramp(BENILLA_PET_AUTOCAST_COLOR, life)
local spark = button.sparks[n]
n = n + 1
spark:SetWidth(size)
spark:SetHeight(size)
spark:ClearAllPoints()
spark:SetPoint("CENTER", button, "CENTER", x, y)
spark:SetVertexColor(c[1], c[2], c[3])
spark:SetAlpha(c[4])
spark:Show()
end
end
end
-- ── The buttons ───────────────────────────────────────────────────────────────────────
function BenillaPetActionButton_OnLoad(button, index)
-- Ref PetActionButton_OnLoad l.234-235. The right-click registration is load-bearing,
-- not boilerplate: it is the whole autocast toggle, and without it a right click is
-- simply never delivered.
button:RegisterForClicks("LeftButtonUp", "RightButtonUp")
-- The drag trio (decision 1010). Both buttons drag, ref-verbatim
-- (PetActionButton_OnLoad l.234) — and unlike the action bar's, ALL THREE gestures here
-- reach the same verb: the reference's OnClick-under-shift, OnDragStart and
-- OnReceiveDrag all call PickupPetAction, which forks on whether the cursor is already
-- carrying. Both drag ends honour LOCK_ACTIONBAR since 1136 — the same uvar the main bar
-- declares (ActionBar.xml), which is the ref's own arrangement too: PetActionBarFrame.lua
-- reads a global UIOptionsFrame owns.
button:RegisterForDrag("LeftButton", "RightButton")
button.index = index
button.icon = getglobal(button:GetName() .. "Icon")
button.cooldown = getglobal(button:GetName() .. "Cooldown")
button.autocastable = getglobal(button:GetName() .. "AutoCastable")
BenillaPetAutocast_Create(button)
end
-- Ref PetActionButton_OnClick. The CheckButton widget toggles itself BEFORE OnClick fires,
-- and the reference immediately clears that (`this:SetChecked(0)`) — nothing about this bar
-- is locally latched, because the server owns every bit of it. Left = run the slot, unless
-- the pet is already on the attack, in which case it calls off; right = flip autocast.
function PetActionButton_OnClick(mouseButton)
local button = this
button:SetChecked(0)
-- Shift outranks the button fork entirely (ref l.253-255): a shift-click on the pet bar
-- is a pick-up whichever mouse button it came from, and it never toggles autocast.
if IsShiftKeyDown() then
PickupPetAction(button.index)
PetActionBar_Update()
elseif mouseButton == "RightButton" then
TogglePetAutocast(button.index)
elseif IsPetAttackActive(button.index) then
PetStopAttack()
else
CastPetAction(button.index)
end
end
-- The KEYBIND pair (decision 1052) — ref PetActionButtonDown/Up l.218-231, which is where
-- the BONUSACTIONBUTTONn bindings land: their bodies call BonusActionButtonDown/Up, and
-- those are one-liners onto these (BonusActionBarFrame.lua:106-112). The ref's own
-- runOnUp shape: DOWN only shows the pushed art, UP fires.
--
-- Note what the key path does NOT do — it is a bare CastPetAction, with none of the click
-- path's forks: no shift pick-up, no right-button autocast, and no IsPetAttackActive
-- call-off. That is the reference's split, not a simplification of ours: the fork lives in
-- PetActionButton_OnClick, which a key press never reaches.
--
-- On a PET bar none of that is visible, because the call-off fork is DEAD CODE there:
-- IsPetAttackActive needs the [0xb714b0] latch, and `0x4bd420` raises it only when the
-- bar's unit is the one you are POSSESSING (ui_pet.rs's `possessing` carries the carve —
-- UNIT_FLAG_POSSESSED, never a hunter's or warlock's pet). A pet's Attack button can never
-- latch lit, so click and key both simply order the attack. The split is visible on a
-- POSSESS bar (Mind Control, Eye of Kilrogg, Eyes of the Beast): there a click sends
-- PetStopAttack and CTRL-1 re-issues the order instead.
function PetActionButtonDown(id)
local button = getglobal("PetActionButton" .. id)
if ( button:GetButtonState() == "NORMAL" ) then
button:SetButtonState("PUSHED")
end
end
function PetActionButtonUp(id)
local button = getglobal("PetActionButton" .. id)
if ( button:GetButtonState() == "PUSHED" ) then
button:SetButtonState("NORMAL")
CastPetAction(id)
end
end
-- Both drag ends are the SAME verb (ref l.269-283, byte-verified at `0x4be180`): with an
-- empty cursor it picks the slot up, with a pet payload already held it drops onto this
-- slot. There is no separate "place" binding on this bar, which is also why a spell or an
-- item can never land here — nothing but a pet slot produces the payload it accepts.
--
-- Both also honour the lock (the ref wraps each body in `if LOCK_ACTIONBAR ~= "1" then …`).
-- The shift-click in OnClick above is unguarded on this bar too, exactly as the reference
-- leaves it (l.253-255): the lock stops the drag, shift-click stays the way through.
function PetActionButton_OnDragStart()
local button = this
if LOCK_ACTIONBAR == "1" then return end
PickupPetAction(button.index)
PetActionBar_Update()
end
function PetActionButton_OnReceiveDrag()
local button = this
if LOCK_ACTIONBAR == "1" then return end
PickupPetAction(button.index)
PetActionBar_Update()
end
-- Ref PetActionButton_OnEnter. A token slot has no spell to look up, so its tooltip is
-- built from the two strings the info call already returned (the ref's own `isToken` fork);
-- a spell slot goes through the engine's tooltip channel.
function PetActionButton_OnEnter()
local button = this
if not GameTooltip then return end -- engine tests load file subsets
if not button.tooltipName then return end
GameTooltip_SetDefaultAnchor(GameTooltip, button)
if button.isToken then
GameTooltip:SetText(button.tooltipName, 1.0, 1.0, 1.0)
if button.tooltipSubtext then
GameTooltip:AddLine(button.tooltipSubtext, "", 0.5, 0.5, 0.5)
end
GameTooltip:Show()
else
GameTooltip:SetPetAction(button.index)
end
end
function PetActionButton_OnLeave()
local button = this
if GameTooltip then GameTooltip:Hide() end
end
-- ── The bar ───────────────────────────────────────────────────────────────────────────
-- Ref PetActionBar_Update + PetActionBar_UpdateCooldowns, fused (one refresh event, see the
-- header). Every branch below is the reference's, including the two that are easy to lose:
-- an unnamed slot HIDES its button, and a slot with no texture swaps the button's ring from
-- the filled UI-Quickslot2 to the empty UI-Quickslot.
function PetActionBar_Update()
local usable = GetPetActionsUsable()
for i = 1, NUM_PET_ACTION_SLOTS do
local button = getglobal("PetActionButton" .. i)
local name, subtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled = GetPetActionInfo(i)
button.isToken = isToken
button.tooltipSubtext = subtext
if isToken then
-- The token convention: what came back are the NAMES of globals.
button.icon:SetTexture(getglobal(texture))
button.tooltipName = getglobal(name)
else
button.icon:SetTexture(texture)
button.tooltipName = name
end
if isActive then button:SetChecked(1) else button:SetChecked(0) end
if autoCastAllowed then button.autocastable:Show() else button.autocastable:Hide() end
button.autocasting = autoCastEnabled
-- An unnamed slot hides — UNLESS the drop grid is up, which is the whole point of
-- the grid: while a pet action rides the cursor every slot shows, so you can see
-- where it may land (ref PetActionBar_Update l.122-128 + PetActionBar_ShowGrid).
if name or (PetActionBarFrame.showgrid or 0) > 0 then
button:Show()
else
button:Hide()
end
if texture then
SetDesaturation(button.icon, not usable and 1 or nil)
button.icon:Show()
button:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2")
else
button.icon:Hide()
button:SetNormalTexture("Interface\\Buttons\\UI-Quickslot")
end
local start, duration, enable = GetPetActionCooldown(i)
CooldownFrame_SetTimer(button.cooldown, start, duration, enable)
end
end
function PetActionBar_OnLoad()
local frame = this
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("PET_BAR_UPDATE")
-- The drop grid (decision 1010). A counter rather than a flag, ref-verbatim
-- (PetActionBar_ShowGrid/_HideGrid): our engine derives the pair off one payload edge
-- so they strictly alternate, but the counter is what the reference keeps and it costs
-- nothing to keep it too.
frame:RegisterEvent("PET_BAR_SHOWGRID")
frame:RegisterEvent("PET_BAR_HIDEGRID")
frame.showgrid = 0
frame.clock = 0
PetActionBar_OnEvent()
end
-- The bar exists exactly while PetHasActionBar() does. Its show/hide re-fires the managed
-- bottom-stack pass (UIParent.xml, decision 0272) — the pet term of that stack is what
-- lifts the cast bar and the chat frame clear of it, which is the ref's own
-- PetActionBarFrame.xml:205-210.
-- Declares nothing and reads the ambient `event`, exactly as the reference does
-- (PetActionBarFrame.lua:33 / .xml's bare `PetActionBar_OnEvent();`). PetActionBar_OnLoad's
-- own tail call therefore lands with `event` nil, which is the no-grid-edge path — the same
-- thing the old explicit `eventName` argument did when omitted.
function PetActionBar_OnEvent()
local frame = this
if event == "PET_BAR_SHOWGRID" then
frame.showgrid = frame.showgrid + 1
elseif event == "PET_BAR_HIDEGRID" and frame.showgrid > 0 then
frame.showgrid = frame.showgrid - 1
end
if PetHasActionBar() then
PetActionBar_Update()
frame:Show()
else
frame:Hide()
end
end
-- The shared sparkle clock. One OnUpdate for the whole bar rather than ten: the four
-- emitters are one looping model, so every autocasting button is in step by construction.
function BenillaPetActionBar_OnUpdate(frame, elapsed)
frame.clock = (frame.clock or 0) + elapsed
if frame.clock > BENILLA_PET_AUTOCAST_PERIOD then
frame.clock = frame.clock - BENILLA_PET_AUTOCAST_PERIOD
end
for i = 1, NUM_PET_ACTION_SLOTS do
BenillaPetAutocast_Update(getglobal("PetActionButton" .. i), frame.clock)
end
end
-- ── The pet menu's two dialogs (ref StaticPopup.lua l.738-748 + l.1069-1102 + l.365-374,
-- decision 1066) ────────────────────────────────────────────────────────────────────
-- They live here rather than with the rows that raise them (UnitPopup.xml) for the same
-- reason every other dialog in this codebase lives with its arc's frame file: registering
-- into StaticPopupDialogs is a LOAD-TIME dependency on UiPanels.xml's engine, and
-- UnitPopup.xml is a shared engine that eight isolation prefixes load without it. Raising
-- them is not a dependency — StaticPopup_Show resolves at click time.
--
-- ABANDON_PET is the plain confirm. RENAME_PET is a CHAIN of two, which is the reference's
-- own shape and not a flourish: the first collects the name, the second reads it back
-- ("Name your pet 'Bruce'?") before anything is sent — because a rename is one-shot, so a
-- typo is permanent in exactly the way the abandon is. That second Show happens while the
-- first dialog is still up, which is why the engine's two instances are both needed.
StaticPopupDialogs["ABANDON_PET"] = {
text = ABANDON_PET,
button1 = OKAY,
button2 = CANCEL,
OnAccept = function()
PetAbandon();
end,
timeout = 0,
exclusive = 1,
hideOnEscape = 1,
}
StaticPopupDialogs["RENAME_PET"] = {
text = PET_RENAME_LABEL,
button1 = ACCEPT,
button2 = CANCEL,
hasEditBox = 1,
-- The 12-letter cap is the reference's, and it is the client's only name check: the
-- server re-validates with CheckPetName and refuses with SMSG_PET_NAME_INVALID.
maxLetters = 12,
OnAccept = function()
-- `this` is the clicked button, so its parent is THIS dialog — never
-- StaticPopup1 by name, which would read the wrong box the moment this dialog
-- lands in the second instance (the chain below is exactly that case).
local text = getglobal(this:GetParent():GetName() .. "EditBox"):GetText()
local dialog = StaticPopup_Show("PETRENAMECONFIRM", text)
if dialog then
dialog.data = text
end
end,
EditBoxOnEnterPressed = function()
local text = getglobal(this:GetParent():GetName() .. "EditBox"):GetText()
local dialog = StaticPopup_Show("PETRENAMECONFIRM", text)
if dialog then
dialog.data = text
end
this:GetParent():Hide()
end,
OnShow = function()
getglobal(this:GetName() .. "EditBox"):SetFocus()
end,
timeout = 0,
exclusive = 1,
hideOnEscape = 1,
}
StaticPopupDialogs["PETRENAMECONFIRM"] = {
-- "Name your pet '%s'?" — the name arrives as text_arg1 AND as `data`; the first
-- fills the question, the second is what actually gets sent.
text = PET_RENAME_CONFIRMATION,
button1 = YES,
button2 = NO,
OnAccept = function(data)
PetRename(data);
end,
timeout = 0,
hideOnEscape = 1,
}
]]></Script>
<!--
One pet button (ref PetActionButtonTemplate: 30x30, inherits ActionButtonTemplate's art
set, plus the AutoCastable ring on OVERLAY at 58x58 CENTER). The sparkle textures are
created in OnLoad rather than declared: there are 32 of them per button, and they are the
one thing here whose count is ours rather than the reference's.
-->
<CheckButton name="BenillaPetActionButtonTemplate" virtual="true">
<Size><AbsDimension x="30" y="30"/></Size>
<Layers>
<Layer level="BACKGROUND">
<Texture name="$parentIcon"/>
</Layer>
<Layer level="OVERLAY">
<Texture name="$parentAutoCastable" file="Interface\Buttons\UI-AutoCastableOverlay" hidden="true">
<Size><AbsDimension x="58" y="58"/></Size>
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
</Anchors>
</Texture>
</Layer>
</Layers>
<Frames>
<!-- Ref PetActionButton_OnLoad sizes this 33x33 at CENTER (-2,-1) by hand rather than
taking the template's own rect — quoted. -->
<Cooldown name="$parentCooldown" hidden="true">
<Size><AbsDimension x="33" y="33"/></Size>
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="-2" y="-1"/></Offset></Anchor>
</Anchors>
</Cooldown>
</Frames>
<NormalTexture name="$parentNormalTexture" file="Interface\Buttons\UI-Quickslot2">
<Size><AbsDimension x="54" y="54"/></Size>
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="0" y="-1"/></Offset></Anchor>
</Anchors>
</NormalTexture>
<PushedTexture file="Interface\Buttons\UI-Quickslot-Depress"/>
<HighlightTexture file="Interface\Buttons\ButtonHilight-Square" alphaMode="ADD"/>
<CheckedTexture file="Interface\Buttons\CheckButtonHilight" alphaMode="ADD"/>
<Scripts>
<OnClick>PetActionButton_OnClick(arg1)</OnClick>
<OnDragStart>PetActionButton_OnDragStart()</OnDragStart>
<OnReceiveDrag>PetActionButton_OnReceiveDrag()</OnReceiveDrag>
<OnEnter>PetActionButton_OnEnter()</OnEnter>
<OnLeave>PetActionButton_OnLeave()</OnLeave>
</Scripts>
</CheckButton>
<Frame name="PetActionBarFrame" hidden="true">
<Size><AbsDimension x="509" y="43"/></Size>
<!-- This anchor is the LOAD-TIME value only: UIParent_ManageFramePositions owns the real
one and re-seats the frame on every pass (OnShow/OnHide below fire it), because the
Y is a managed var — the ref's own PETACTIONBAR_YPOS, 97 here and 97+43 with the
bottom-left multibar up. Leaving it seated at the base 97 is what drew the bar across
that bar's buttons (decision 0988). The X likewise: 36, or past the last stance
button when the two bars share this row.
The ref also slides the frame in over 0.09 s; we don't. The slide's start and end
anchors are the same two points, and a frame that only ever appears with a pet reads
the same arriving seated as it does arriving in 90 ms (the stance bar's own posture,
decision 0270). -->
<Anchors>
<Anchor point="TOPLEFT" relativeTo="MainMenuBar" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="36" y="97"/></Offset></Anchor>
</Anchors>
<Layers>
<Layer level="OVERLAY">
<Texture name="SlidingActionBarTexture0" file="Interface\PetActionBar\UI-PetBar">
<Size><AbsDimension x="256" y="44"/></Size>
<Anchors><Anchor point="TOPLEFT"/></Anchors>
<TexCoords left="0" right="1.0" top="0.015625" bottom="0.359375"/>
</Texture>
<Texture name="SlidingActionBarTexture1" file="Interface\PetActionBar\UI-PetBar">
<Size><AbsDimension x="184" y="44"/></Size>
<Anchors><Anchor point="LEFT" relativeTo="SlidingActionBarTexture0" relativePoint="RIGHT"/></Anchors>
<TexCoords left="0" right="0.71875" top="0.375" bottom="0.71875"/>
</Texture>
</Layer>
</Layers>
<Frames>
<CheckButton name="PetActionButton1" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="BOTTOMLEFT" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="36" y="2"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 1)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton2" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton1" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 2)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton3" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton2" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 3)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton4" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton3" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 4)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton5" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton4" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 5)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton6" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton5" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 6)</OnLoad></Scripts>
</CheckButton>
<!-- The ref's own +7 here, not +8 (PetActionBarFrame.xml l.158-166). -->
<CheckButton name="PetActionButton7" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton6" relativePoint="RIGHT"><Offset><AbsDimension x="7" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 7)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton8" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton7" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 8)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton9" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton8" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 9)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton10" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton9" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 10)</OnLoad></Scripts>
</CheckButton>
</Frames>
<Scripts>
<OnLoad>PetActionBar_OnLoad()</OnLoad>
<OnEvent>PetActionBar_OnEvent()</OnEvent>
<OnUpdate>BenillaPetActionBar_OnUpdate(self, arg1)</OnUpdate>
<OnShow>UIParent_ManageFramePositions()</OnShow>
<OnHide>UIParent_ManageFramePositions()</OnHide>
</Scripts>
</Frame>
</Ui>