- the character sheet is the reference's own — `CharacterFrame.xml`, `PaperDollFrame.xml` and `PetPaperDollFrame.xml` off the player's chain in the reference's own order, 2800 lines of ours deleted — and the one engine verb it needed had no pixels: a bare `<PlayerModel name="CharacterModelFrame">` drew nothing here, so the swap as written would have replaced a working paper doll with an empty rectangle, invisible to every loader and gap check; a `<Model>`/`<PlayerModel>` widget now draws its window's booth, keyed on the reference's own frame names, and the booth mirrors take their yaw off `PlayerModel:SetRotation`'s own state, which is what the reference's `Model_Rotate*` write - what the stock file calls that was not there: `MicroButtonTooltipText(text, action)` (all five tab hovers), the two scripts `CharacterFrameTabButtonTemplate` carries — without them the five tabs are DEAD, since the reference's `PaperDollFrame` is `enableMouse` with a hit inset the tabs sit inside — `GetGuildInfo` on a recognised-but-unresolvable token answering `(nil, nil, 0)` rather than raising (`0x4c9330`), and `UnitAttackBothHands` pushing four values not two (`0x518810` calls the hand accessor twice; the reference's own Lua destructures two and says `FIXME` about the off hand, so no amount of running the sheet could expose it) — both skill verbs clamp a debuff deeper than the skill to zero as `0x5188a7` and `0x519298` do - a gate for the question that bites AFTER a swap: for every chain window, census the bare `name(` call sites across it and its `.lua`, subtract what those define, keep what the reference's own `_G` carries, and require every survivor non-nil in a VM with the whole interface up — `LootFrame.xml` had shipped load-clean and raised at line 85, and the stock character sheet would have raised on the first tab hover; the stock sheet is driven end to end over a level-60 body with a +105 buff, a −12 debuff and a resistance school whose debuff outweighs its buff, at the rendered strings Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
716 lines
42 KiB
XML
716 lines
42 KiB
XML
<Ui>
|
|
<!--
|
|
benilla's stable-master window (decision 1676) — the hunter's three-slot pet stable, dressed
|
|
from the real 1.12 `PetStable.xml`/`PetStable.lua`. Every rule below is cited back to that
|
|
extraction: `xml l.N` is `ref-PetStable.xml`, `lua l.N` is `ref-PetStable.lua`, and
|
|
`GlobalStrings l.N` is the shipped `GlobalStrings.lua`.
|
|
|
|
**The Lua lives here, inline.** The reference splits it (`<Script file="PetStable.lua"/>`,
|
|
xml l.3); benilla ships one file per window, so the transcription sits in the CDATA block
|
|
below in the reference's own order (OnLoad, OnEvent, Update, NoPetsAllowed).
|
|
|
|
**An NPC session, reached through gossip**, and it behaves like every other one here
|
|
(TrainerFrame.xml/MerchantFrame.xml's shape): a left-slot UIPanel (registered in
|
|
UiPanels.xml, the reference's own `UIParent.lua:39` row), a "npc" portrait bake, and an
|
|
`OnHide` that ends the session. The one thing that differs from its neighbours is that the
|
|
close sends NO PACKET: `ClosePetStables()` is purely client-side (there is no close opcode
|
|
in 5875 — `benilla-ui/src/script/stable.rs`'s own note), so the hide just clears the app's
|
|
session.
|
|
|
|
**Three slots, indices 0..2.** `NUM_PET_STABLE_SLOTS = 2` (lua l.1) is the count of *stable*
|
|
slots a hunter can buy; slot 0 is the pet at their side, which is why every loop below runs
|
|
`1..NUM_PET_STABLE_SLOTS` for the stabled pair and treats 0 separately.
|
|
|
|
Named deferrals / deviations, each re-cited at its own site below:
|
|
|
|
- **The model pane is the reference's own `<PlayerModel name="PetStableModel">`**
|
|
(xml l.182-280), and so are the four scripts on it: `Model_OnLoad`, `Model_OnUpdate`
|
|
and the two `Model_Rotate*` the buttons call (UIParent.xml). It was a plain `<Frame>`
|
|
sampling a booth bake through `BenillaSetBoothTexture`, with the rotate quartet borrowed
|
|
from our CharacterFrame.xml — both went when the character window migrated (decision
|
|
1751): the widget draws its own booth now, and the yaw the app mirrors is the pane's
|
|
own `SetRotation` state.
|
|
One thing the reference's pane does that ours still does not: `DISPLAY_SIZE_CHANGED` →
|
|
`this:RefreshUnit()` (xml l.196-200) is a no-op here, because the pane holds the unit
|
|
TOKEN and the app re-resolves it every frame — there is no cached appearance to
|
|
invalidate (`script/modelframe.rs`'s `RefreshUnit`).
|
|
- The pane's booth is the `"stable"` slot in `crates/benilla-app/src/portrait`, joined to
|
|
the pane by name in that module's `MODEL_PANE_BOOTHS`. The app resolves
|
|
`GetSelectedStablePet()`'s slot into either the live pet's entity or — for a stabled or
|
|
dismissed pet, which has no world object at all — the creature query's
|
|
`CreatureDisplayInfo` id, and the booth mirrors whichever it got.
|
|
- **`GetPetIcon()` does not exist in this client** (reference/1.12-globals.tsv has it as
|
|
an engine global; benilla has never needed it). `PetStable_Update` calls it at lua l.51
|
|
and l.161-162 and it is transcribed verbatim rather than guarded: the window's current
|
|
pet slot cannot paint without it, and a guard here would hide a missing binding instead
|
|
of reporting it.
|
|
- `parent="UIParent"` (xml l.72) is ours too, and it attaches for real: the loader does
|
|
read a cross-file `parent=`, resolving the name by global lookup at load (decision
|
|
1734), and the frame below declares it. The frame's own `<Anchors>` are kept verbatim even
|
|
though `ShowUIPanel` re-`SetPoint`s it into the left slot — they name the same
|
|
TOPLEFT (0,-104) the slot manager writes, so the two agree (ItemTextFrame.xml keeps its
|
|
reference anchor for the same reason).
|
|
- `PetStableMoneyFrame`'s instance `<OnLoad>SmallMoneyFrame_OnLoad()</OnLoad>` (xml
|
|
l.360-362) is dropped, and the cost frame's OnLoad keeps BOTH calls. benilla's
|
|
`SmallMoneyFrameTemplate` already wires `SmallMoneyFrame_OnLoad` as its own OnLoad
|
|
(MoneyFrame.xml) where the reference's template does not, and an instance `<Scripts>`
|
|
block SetScripts over the inherited handler rather than adding to it — so the purse
|
|
wants no OnLoad at all, and any instance that needs `MoneyFrame_SetType` must re-call
|
|
the loader beside it. That is the idiom AuctionFrame.xml and MailFrame.xml already use.
|
|
- Two housekeeping fixes inside `PetStable_Update`, both behaviour-identical: `background`
|
|
is a LOCAL here (lua l.76 leaks it as a global), and the reference's `numSlots`/`numPets`
|
|
locals (lua l.69-70) are dropped because nothing reads them — the loop asks
|
|
`GetNumStableSlots()` directly, exactly as the reference's own l.79 does.
|
|
- The GlobalStrings the window reads are declared below with the `X = X or "..."` idiom
|
|
this codebase uses: the real localized file loads off the patch chain before our own
|
|
files (`ui_script/lifecycle.rs`'s `load_global_strings`) and always wins; the fallbacks
|
|
are what make the strings resolve in the test harnesses, which load no chain. They
|
|
matter at LOAD, not just at runtime: a `text=` attribute is a global-string lookup
|
|
performed while the frames below are being built.
|
|
-->
|
|
<Script><![CDATA[
|
|
-- ── GlobalStrings this window uses (values + line cites from the shipped file) ───────────
|
|
STABLES = STABLES or "Stables" -- :4189
|
|
CURRENT_PET = CURRENT_PET or "Current Pet:" -- :881
|
|
STABLED_PETS = STABLED_PETS or "Stabled Pets:" -- :4188
|
|
EMPTY_STABLE_SLOT = EMPTY_STABLE_SLOT or "Empty Stable Slot" -- :1434
|
|
STABLE_SLOT_TEXT = STABLE_SLOT_TEXT
|
|
or "Do you wish to purchase another stable slot?" -- :4190
|
|
COSTS_LABEL = COSTS_LABEL or "Cost:" -- :870
|
|
PURCHASE = PURCHASE or "Purchase" -- :3128
|
|
PET_DIET_TEMPLATE = PET_DIET_TEMPLATE or "Diet: %s" -- :3037
|
|
UNIT_LEVEL_TEMPLATE = UNIT_LEVEL_TEMPLATE or "Level %d" -- :4436
|
|
|
|
-- lua l.1. The count of STABLE slots (the two a hunter buys), not of window slots: the
|
|
-- buttons run 0..2 with 0 being the pet at the player's side, which is why the loops below
|
|
-- iterate 1..NUM_PET_STABLE_SLOTS and handle 0 on its own.
|
|
NUM_PET_STABLE_SLOTS = 2
|
|
|
|
-- ── The window ──────────────────────────────────────────────────────────────────────────
|
|
-- lua l.3-9, verbatim. UNIT_PET is here because a pet summoned or dismissed while the
|
|
-- window is open changes slot 0 without the server sending a stable list.
|
|
function PetStable_OnLoad()
|
|
this:RegisterEvent("PET_STABLE_SHOW")
|
|
this:RegisterEvent("PET_STABLE_UPDATE")
|
|
this:RegisterEvent("PET_STABLE_UPDATE_PAPERDOLL")
|
|
this:RegisterEvent("PET_STABLE_CLOSED")
|
|
this:RegisterEvent("UNIT_PET")
|
|
end
|
|
|
|
-- lua l.11-32, all four arms. NOTE the SHOW arm's shape: `ShowUIPanel` can REFUSE (dead
|
|
-- player, a native-center frame holding the screen — UiPanels.xml's `AdmitPanel`), and the
|
|
-- reference reads that refusal back off `IsVisible()` and ends the session rather than
|
|
-- leaving one open behind a window nobody can see. Ours can refuse for the same reasons and
|
|
-- has the same session to end (`crate::ui_stable`'s), so the check is load-bearing here
|
|
-- too, not defensive.
|
|
function PetStable_OnEvent()
|
|
if event == "PET_STABLE_SHOW" then
|
|
ShowUIPanel(this)
|
|
if not this:IsVisible() then
|
|
ClosePetStables()
|
|
return
|
|
end
|
|
PetStable_Update()
|
|
elseif event == "PET_STABLE_UPDATE" or (event == "UNIT_PET" and arg1 == "player") then
|
|
PetStable_Update()
|
|
elseif event == "PET_STABLE_UPDATE_PAPERDOLL" then
|
|
-- So warlock pets don't show (lua l.23-27, the reference's own comment). A warlock
|
|
-- standing at a stable master has a pet but no pet UI, and the window must not
|
|
-- offer to stable it.
|
|
if UnitExists("pet") and not HasPetUI() then
|
|
PetStable_NoPetsAllowed()
|
|
return
|
|
end
|
|
SetPetStablePaperdoll(PetStableModel)
|
|
elseif event == "PET_STABLE_CLOSED" then
|
|
HideUIPanel(this)
|
|
end
|
|
end
|
|
|
|
-- lua l.34-209 — the whole repaint, in the reference's own order and with its own
|
|
-- structure. Read it as five passes: the portrait, the warlock gate, the auto-selection,
|
|
-- the two stabled slots, the current-pet slot, and the purchase row.
|
|
function PetStable_Update()
|
|
-- Set stablemaster portrait (lua l.35-36): the "npc" unit token, the same live model
|
|
-- bake every other NPC-session window binds.
|
|
SetPortraitTexture(PetStableFramePortrait, "npc")
|
|
|
|
-- So warlock pets don't show (lua l.38-46, the reference's own comment). NOTE this arm
|
|
-- reads `isHunterPet`, the SECOND return, where the paperdoll arm above reads the
|
|
-- first: a warlock's minion answers hasPetUI true and isHunterPet false, and it is the
|
|
-- second that decides whether this window has anything to do.
|
|
local hasPetUI, isHunterPet = HasPetUI()
|
|
if UnitExists("pet") and not isHunterPet then
|
|
PetStable_NoPetsAllowed()
|
|
PetStableCurrentPet:Disable()
|
|
return
|
|
else
|
|
PetStableCurrentPet:Enable()
|
|
end
|
|
|
|
-- If no selected pet try to set one (lua l.48-63). The -1 sentinel IS the API
|
|
-- (`GetSelectedStablePet` returns it, and `stable.rs` defaults its selection to it for
|
|
-- exactly this branch): a freshly opened window picks the current pet if there is one,
|
|
-- otherwise the first occupied stable slot, so the pane is never blank when it has
|
|
-- something to show.
|
|
local selectedPet = GetSelectedStablePet()
|
|
if selectedPet == -1 then
|
|
if GetPetIcon() then
|
|
selectedPet = 0
|
|
ClickStablePet(0)
|
|
else
|
|
for i = 0, NUM_PET_STABLE_SLOTS do
|
|
if GetStablePetInfo(i) then
|
|
selectedPet = i
|
|
ClickStablePet(i)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Set slot cost (lua l.65-66).
|
|
MoneyFrame_Update("PetStableCostMoneyFrame", GetNextStableSlotCost())
|
|
|
|
-- Set slot statuseses (lua l.68-119, the reference's own spelling of the comment).
|
|
-- An UNBOUGHT slot is drawn differently from an EMPTY one: bought-and-empty keeps its
|
|
-- white empty-slot art and stays clickable, unbought goes red and disabled. That is the
|
|
-- whole visual difference between "you may stable a pet here" and "buy this first".
|
|
local button
|
|
local background
|
|
local icon, name, level, family, loyalty
|
|
for i = 1, NUM_PET_STABLE_SLOTS do
|
|
button = getglobal("PetStableStabledPet" .. i)
|
|
background = getglobal("PetStableStabledPet" .. i .. "Background")
|
|
icon, name, level, family, loyalty = GetStablePetInfo(i)
|
|
SetItemButtonTexture(button, icon)
|
|
if i <= GetNumStableSlots() then
|
|
background:SetVertexColor(1.0, 1.0, 1.0)
|
|
button:Enable()
|
|
if icon then
|
|
button.tooltip = name
|
|
button.tooltipSubtext = format(TEXT(UNIT_LEVEL_TEMPLATE), level) .. " " .. family
|
|
else
|
|
button.tooltip = EMPTY_STABLE_SLOT
|
|
button.tooltipSubtext = ""
|
|
end
|
|
if i == selectedPet then
|
|
if icon then
|
|
button:SetChecked(1)
|
|
PetStableLevelText:SetText(
|
|
name .. " " .. format(TEXT(UNIT_LEVEL_TEMPLATE), level) .. " " .. family)
|
|
PetStableLoyaltyText:SetText(loyalty)
|
|
SetPetStablePaperdoll(PetStableModel)
|
|
PetStablePetInfo.tooltip =
|
|
format(PET_DIET_TEMPLATE, BuildListString(GetStablePetFoodTypes(i)))
|
|
if not PetStableModel:IsShown() then
|
|
PetStableModel:Show()
|
|
end
|
|
else
|
|
button:SetChecked(nil)
|
|
PetStableLevelText:SetText("")
|
|
PetStableLoyaltyText:SetText("")
|
|
PetStableModel:Hide()
|
|
end
|
|
else
|
|
button:SetChecked(nil)
|
|
end
|
|
-- lua l.109-114: if this button's tooltip is the one currently up, rebuild it
|
|
-- in place. A repaint that changed a slot under a resting cursor would
|
|
-- otherwise leave the previous pet's name on screen.
|
|
if GameTooltip:IsOwned(button) then
|
|
GameTooltip:SetOwner(button, "ANCHOR_RIGHT")
|
|
GameTooltip:SetText(button.tooltip)
|
|
GameTooltip:AddLine(button.tooltipSubtext, "", 1.0, 1.0, 1.0)
|
|
GameTooltip:Show()
|
|
end
|
|
else
|
|
background:SetVertexColor(1.0, 0.1, 0.1)
|
|
button:Disable()
|
|
end
|
|
end
|
|
|
|
-- Current pet slot (lua l.121-159). Three cases, and the middle one is the interesting
|
|
-- one: a hunter whose pet is dismissed still has a slot-0 row from the server (read off
|
|
-- the character-pet cache), so the reference falls back to `GetStablePetInfo(0)` rather
|
|
-- than showing an empty current-pet slot for a pet that merely is not out.
|
|
if selectedPet == 0 then
|
|
if UnitExists("pet") then
|
|
PetStableCurrentPet:SetChecked(1)
|
|
local family = UnitCreatureFamily("pet")
|
|
if not family then
|
|
family = ""
|
|
end
|
|
PetStableLevelText:SetText(
|
|
UnitName("pet") .. " " .. format(TEXT(UNIT_LEVEL_TEMPLATE), UnitLevel("pet"))
|
|
.. " " .. family)
|
|
PetStableLoyaltyText:SetText(GetPetLoyalty())
|
|
SetPetStablePaperdoll(PetStableModel)
|
|
if not PetStableModel:IsShown() then
|
|
PetStableModel:Show()
|
|
end
|
|
if GetPetFoodTypes() then
|
|
PetStablePetInfo.tooltip =
|
|
format(PET_DIET_TEMPLATE, BuildListString(GetPetFoodTypes()))
|
|
end
|
|
elseif GetStablePetInfo(0) then
|
|
-- If pet doesn't exist it might be dismissed, so check stable slot 0 for
|
|
-- current pet info (lua l.139, the reference's own comment).
|
|
PetStableCurrentPet:SetChecked(1)
|
|
icon, name, level, family, loyalty = GetStablePetInfo(0)
|
|
PetStableLevelText:SetText(
|
|
name .. " " .. format(TEXT(UNIT_LEVEL_TEMPLATE), level) .. " " .. family)
|
|
PetStableLoyaltyText:SetText(loyalty)
|
|
SetPetStablePaperdoll(PetStableModel)
|
|
if not PetStableModel:IsShown() then
|
|
PetStableModel:Show()
|
|
end
|
|
if GetStablePetFoodTypes(0) then
|
|
PetStablePetInfo.tooltip =
|
|
format(PET_DIET_TEMPLATE, BuildListString(GetStablePetFoodTypes(0)))
|
|
end
|
|
else
|
|
PetStableCurrentPet:SetChecked(nil)
|
|
PetStableLevelText:SetText("")
|
|
PetStableLoyaltyText:SetText("")
|
|
PetStableModel:Hide()
|
|
end
|
|
else
|
|
PetStableCurrentPet:SetChecked(nil)
|
|
end
|
|
|
|
-- Set tooltip and icon info (lua l.160-181). The same live/dismissed/absent ladder as
|
|
-- the block above, for the BUTTON's own icon and hover rather than the pane.
|
|
if GetPetIcon() then
|
|
SetItemButtonTexture(PetStableCurrentPet, GetPetIcon())
|
|
PetStableCurrentPet.tooltip = UnitName("pet")
|
|
PetStableCurrentPet.tooltipSubtext =
|
|
format(TEXT(UNIT_LEVEL_TEMPLATE), UnitLevel("pet")) .. " " .. UnitCreatureFamily("pet")
|
|
elseif GetStablePetInfo(0) then
|
|
icon, name, level, family, loyalty = GetStablePetInfo(0)
|
|
SetItemButtonTexture(PetStableCurrentPet, icon)
|
|
PetStableCurrentPet.tooltip = name
|
|
PetStableCurrentPet.tooltipSubtext =
|
|
format(TEXT(UNIT_LEVEL_TEMPLATE), level) .. " " .. family
|
|
else
|
|
SetItemButtonTexture(PetStableCurrentPet, "")
|
|
PetStableCurrentPet.tooltip = EMPTY_STABLE_SLOT
|
|
PetStableCurrentPet.tooltipSubtext = ""
|
|
PetStableCurrentPet:SetChecked(nil)
|
|
end
|
|
if GameTooltip:IsOwned(PetStableCurrentPet) then
|
|
GameTooltip:SetOwner(PetStableCurrentPet, "ANCHOR_RIGHT")
|
|
GameTooltip:SetText(PetStableCurrentPet.tooltip)
|
|
GameTooltip:AddLine(PetStableCurrentPet.tooltipSubtext, "", 1.0, 1.0, 1.0)
|
|
GameTooltip:Show()
|
|
end
|
|
|
|
-- If no selected pet clear everything out (lua l.183-189). Reachable even after the
|
|
-- auto-selection above: a hunter with no pet and an empty stable has nothing to select.
|
|
if selectedPet == -1 then
|
|
-- no pet
|
|
PetStableModel:Hide()
|
|
PetStableLevelText:SetText("")
|
|
PetStableLoyaltyText:SetText("")
|
|
end
|
|
|
|
-- Enable, disable, or hide purchase button (lua l.191-208). Three states, and the first
|
|
-- is a HIDE of the whole row: with both slots bought there is nothing left to sell, so
|
|
-- the button, the "Cost:" label, its coins and the slot-purchase line all go. Below
|
|
-- that the button only enables when the player can afford it, and the coins turn red
|
|
-- when they cannot. The leading unconditional Show() is the reference's own and it is
|
|
-- what brings the button back after a NoPetsAllowed() pass hid it.
|
|
PetStablePurchaseButton:Show()
|
|
if GetNumStableSlots() == NUM_PET_STABLE_SLOTS then
|
|
PetStablePurchaseButton:Hide()
|
|
PetStableCostLabel:Hide()
|
|
PetStableCostMoneyFrame:Hide()
|
|
PetStableSlotText:Hide()
|
|
elseif GetMoney() >= GetNextStableSlotCost() then
|
|
PetStablePurchaseButton:Enable()
|
|
PetStableCostLabel:Show()
|
|
PetStableCostMoneyFrame:Show()
|
|
SetMoneyFrameColor("PetStableCostMoneyFrame",
|
|
HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
|
|
else
|
|
PetStablePurchaseButton:Disable()
|
|
PetStableCostLabel:Show()
|
|
PetStableCostMoneyFrame:Show()
|
|
SetMoneyFrameColor("PetStableCostMoneyFrame",
|
|
RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b)
|
|
end
|
|
end
|
|
|
|
-- lua l.211-229 — the warlock face of the window, called from BOTH the paperdoll event arm
|
|
-- and the top of Update. It is not "clear the window": it is the window with nothing on
|
|
-- offer — every slot unchecked and tooltipped as empty, the pane hidden, and the whole
|
|
-- purchase row gone, because a warlock cannot buy a stable slot either.
|
|
function PetStable_NoPetsAllowed()
|
|
local button
|
|
for i = 1, NUM_PET_STABLE_SLOTS do
|
|
button = getglobal("PetStableStabledPet" .. i)
|
|
button.tooltip = EMPTY_STABLE_SLOT
|
|
button:SetChecked(nil)
|
|
end
|
|
|
|
PetStableCurrentPet:SetChecked(nil)
|
|
PetStableLevelText:SetText("")
|
|
PetStableLoyaltyText:SetText("")
|
|
PetStableModel:Hide()
|
|
SetItemButtonTexture(PetStableCurrentPet, "")
|
|
PetStableCurrentPet.tooltip = EMPTY_STABLE_SLOT
|
|
PetStableCurrentPet:SetChecked(nil)
|
|
PetStablePurchaseButton:Hide()
|
|
PetStableCostLabel:Hide()
|
|
PetStableCostMoneyFrame:Hide()
|
|
PetStableSlotText:Hide()
|
|
end
|
|
]]></Script>
|
|
|
|
<!--
|
|
One slot button (xml l.4-71): a 37x37 CheckButton wearing the standard quickslot ring, with
|
|
the empty-slot art behind it and the pet's icon in front. `$parentIconTexture` is the name
|
|
`SetItemButtonTexture` addresses (ItemButtonTemplate.xml's naming contract), and
|
|
`$parentBackground` is the one Update tints red for an unbought slot.
|
|
|
|
**`ClickStablePet` is wired to BOTH `OnClick` and `OnReceiveDrag`, and the frame repaints
|
|
only when it returns true.** That is the reference's own arrangement and it is transcribed
|
|
exactly, deliberately: the binding carries two jobs behind one name (commit a drag if one is
|
|
in flight, otherwise select) and its drag half is still being reverse-engineered
|
|
(`benilla-ui/src/script/stable.rs`'s `drag_verb`, INFERRED). Baking an assumption about
|
|
which it does into this XML would put the wrong answer in two places instead of one, so the
|
|
XML asks the same question the reference asks and repaints on the same answer.
|
|
|
|
`RegisterForDrag("LeftButton")` is what arms `OnDragStart` on a press-and-move; the pickup
|
|
it starts is FRAME-LOCAL, not the global cursor (there is no stable-pet cursor mode in
|
|
1.12 — stable.rs's module doc), so nothing else on screen can be holding a stable pet.
|
|
-->
|
|
<CheckButton name="PetStableSlotTemplate" virtual="true">
|
|
<Size><AbsDimension x="37" y="37"/></Size>
|
|
<Layers>
|
|
<Layer level="BORDER">
|
|
<Texture name="$parentIconTexture"/>
|
|
</Layer>
|
|
<Layer level="BACKGROUND">
|
|
<Texture name="$parentBackground" file="Interface\Buttons\UI-EmptySlot">
|
|
<Size><AbsDimension x="64" y="64"/></Size>
|
|
<Anchors>
|
|
<Anchor point="TOPLEFT"><Offset><AbsDimension x="-13" y="13"/></Offset></Anchor>
|
|
</Anchors>
|
|
</Texture>
|
|
</Layer>
|
|
</Layers>
|
|
<Scripts>
|
|
<OnLoad>this:RegisterForDrag("LeftButton")</OnLoad>
|
|
<OnEnter>
|
|
-- xml l.31-38. Guarded on `this.tooltip` because Update has not necessarily run
|
|
-- yet: a hover before the first repaint has nothing to say and says nothing.
|
|
if this.tooltip then
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:SetText(this.tooltip)
|
|
GameTooltip:AddLine(this.tooltipSubtext, "", 1.0, 1.0, 1.0)
|
|
GameTooltip:Show()
|
|
end
|
|
</OnEnter>
|
|
<OnLeave>GameTooltip:Hide()</OnLeave>
|
|
<OnClick>
|
|
if ClickStablePet(this:GetID()) then
|
|
PetStable_Update()
|
|
end
|
|
</OnClick>
|
|
<OnDragStart>PickupStablePet(this:GetID())</OnDragStart>
|
|
<OnReceiveDrag>
|
|
if ClickStablePet(this:GetID()) then
|
|
PetStable_Update()
|
|
end
|
|
</OnReceiveDrag>
|
|
</Scripts>
|
|
<NormalTexture name="$parentNormalTexture" file="Interface\Buttons\UI-Quickslot2">
|
|
<Size><AbsDimension x="64" y="64"/></Size>
|
|
<Anchors>
|
|
<Anchor point="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"/>
|
|
</CheckButton>
|
|
|
|
<Frame name="PetStableFrame" parent="UIParent" toplevel="true" movable="true" enableMouse="true" hidden="true">
|
|
<!-- xml l.72-85: the standard 384x512 panel window. The HitRectInsets pull the mouse rect
|
|
in from the art's transparent right edge and bottom skirt. -->
|
|
<Size><AbsDimension x="384" y="512"/></Size>
|
|
<Anchors>
|
|
<Anchor point="TOPLEFT"><Offset><AbsDimension x="0" y="-104"/></Offset></Anchor>
|
|
</Anchors>
|
|
<HitRectInsets><AbsInset left="0" right="34" top="0" bottom="75"/></HitRectInsets>
|
|
<Layers>
|
|
<!-- The stable master's portrait, 60x60 TOPLEFT (7,-6) (xml l.88-99). Bound in Update
|
|
via SetPortraitTexture(region, "npc") — the live model bake of whoever we are
|
|
talking to, the same binding every NPC-session window here uses. -->
|
|
<Layer level="BACKGROUND">
|
|
<Texture name="PetStableFramePortrait">
|
|
<Size><AbsDimension x="60" y="60"/></Size>
|
|
<Anchors>
|
|
<Anchor point="TOPLEFT"><Offset><AbsDimension x="7" y="-6"/></Offset></Anchor>
|
|
</Anchors>
|
|
</Texture>
|
|
</Layer>
|
|
<Layer level="BORDER">
|
|
<!-- The four quadrant slabs that ARE the window art (xml l.102-133): 256-wide left
|
|
halves, 128-wide right halves, each 256 tall, pinned to their own corner. -->
|
|
<Texture file="Interface\PetStableFrame\UI-PetStable-TopLeft">
|
|
<Size><AbsDimension x="256" y="256"/></Size>
|
|
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
|
</Texture>
|
|
<Texture file="Interface\PetStableFrame\UI-PetStable-TopRight">
|
|
<Size><AbsDimension x="128" y="256"/></Size>
|
|
<Anchors><Anchor point="TOPRIGHT"/></Anchors>
|
|
</Texture>
|
|
<Texture name="PetStableFrameBottomLeft" file="Interface\PetStableFrame\UI-PetStable-BottomLeft">
|
|
<Size><AbsDimension x="256" y="256"/></Size>
|
|
<Anchors><Anchor point="BOTTOMLEFT"/></Anchors>
|
|
</Texture>
|
|
<Texture name="PetStableFrameBottomRight" file="Interface\PetStableFrame\UI-PetStable-BottomRight">
|
|
<Size><AbsDimension x="128" y="256"/></Size>
|
|
<Anchors><Anchor point="BOTTOMRIGHT"/></Anchors>
|
|
</Texture>
|
|
<!-- Title (xml l.134-142). -->
|
|
<FontString name="PetStableTitleLabel" inherits="GameFontHighlight" text="STABLES">
|
|
<Anchors>
|
|
<Anchor point="TOP"><Offset><AbsDimension x="0" y="-18"/></Offset></Anchor>
|
|
</Anchors>
|
|
</FontString>
|
|
<!-- The selected pet's name/level/family line and its loyalty line (xml l.143-160).
|
|
The reference ships them carrying PLACEHOLDER text ("Bobby The Fleshripper
|
|
(Level 99 Raptor)", "Loyalty: Mans Best Friend") — an authoring artifact that
|
|
is on screen for the instant before the first Update, and one this engine would
|
|
render literally (a `text=` that misses the global string table falls back to
|
|
the literal). Dropped: the strings are Update's to write. -->
|
|
<FontString name="PetStableLevelText" inherits="GameFontNormal">
|
|
<Anchors>
|
|
<Anchor point="TOP" relativeTo="PetStableTitleLabel" relativePoint="BOTTOM">
|
|
<Offset><AbsDimension x="0" y="-8"/></Offset>
|
|
</Anchor>
|
|
</Anchors>
|
|
</FontString>
|
|
<FontString name="PetStableLoyaltyText" inherits="GameFontNormalSmall">
|
|
<Anchors>
|
|
<Anchor point="TOP" relativeTo="PetStableLevelText" relativePoint="BOTTOM">
|
|
<Offset><AbsDimension x="0" y="-1"/></Offset>
|
|
</Anchor>
|
|
</Anchors>
|
|
</FontString>
|
|
<!-- "Do you wish to purchase another stable slot?" and the "Cost:" label
|
|
(xml l.161-178) — both hidden by Update once both slots are bought. -->
|
|
<FontString name="PetStableSlotText" inherits="GameFontHighlightSmall" text="STABLE_SLOT_TEXT">
|
|
<Anchors>
|
|
<Anchor point="BOTTOM"><Offset><AbsDimension x="-10" y="132"/></Offset></Anchor>
|
|
</Anchors>
|
|
</FontString>
|
|
<FontString name="PetStableCostLabel" inherits="GameFontNormal" text="COSTS_LABEL">
|
|
<Anchors>
|
|
<Anchor point="BOTTOMLEFT"><Offset><AbsDimension x="74" y="110"/></Offset></Anchor>
|
|
</Anchors>
|
|
</FontString>
|
|
</Layer>
|
|
</Layers>
|
|
<Frames>
|
|
<!-- The model pane (ref `<PlayerModel name="PetStableModel">`, xml l.182-280): 313x223
|
|
at TOPLEFT (23,-76), the reference's own widget type since decision 1751 gave
|
|
`<Model>`/`<PlayerModel>` panes a draw of their own (portrait/mod.rs's
|
|
`MODEL_PANE_BOOTHS`).
|
|
|
|
The `$parentTexture` this used to carry is gone with it, and so is decision 1070's
|
|
BACKGROUND-not-ARTWORK note: that workaround existed because a booth *texture* is
|
|
an opaque region in a BUCKET-WIDE draw layer that outranks the frame (0884), so at
|
|
ARTWORK it painted over the diet icon below. The pane's own draw slot has no such
|
|
problem — it emits at the FRAME's z, which is what puts every child above it, and
|
|
that is the reference's own layering rather than a tie we had to arrange.
|
|
|
|
The pane keeps its reference NAME because the Lua passes it by name to
|
|
`SetPetStablePaperdoll` at four sites, an addon may too, and the app now joins the
|
|
booth to the pane by that name. -->
|
|
<PlayerModel name="PetStableModel">
|
|
<Size><AbsDimension x="313" y="223"/></Size>
|
|
<Anchors>
|
|
<Anchor point="TOPLEFT"><Offset><AbsDimension x="23" y="-76"/></Offset></Anchor>
|
|
</Anchors>
|
|
<Frames>
|
|
<!-- Rotate buttons (xml l.206-247): 35x35, the left one at the pane's own
|
|
TOPLEFT and the right one chained off its TOPRIGHT, calling the
|
|
reference's own `Model_RotateLeft`/`Model_RotateRight` (UIParent.xml). -->
|
|
<Button name="PetStableModelRotateLeftButton">
|
|
<Size><AbsDimension x="35" y="35"/></Size>
|
|
<Anchors>
|
|
<Anchor point="TOPLEFT"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
|
|
</Anchors>
|
|
<NormalTexture file="Interface\Buttons\UI-RotationLeft-Button-Up"/>
|
|
<PushedTexture file="Interface\Buttons\UI-RotationLeft-Button-Down"/>
|
|
<HighlightTexture file="Interface\Buttons\ButtonHilight-Round" alphaMode="ADD"/>
|
|
<Scripts>
|
|
<OnLoad>this:RegisterForClicks("LeftButtonDown", "LeftButtonUp")</OnLoad>
|
|
<OnClick>Model_RotateLeft(this:GetParent())</OnClick>
|
|
</Scripts>
|
|
</Button>
|
|
<Button name="PetStableModelRotateRightButton">
|
|
<Size><AbsDimension x="35" y="35"/></Size>
|
|
<Anchors>
|
|
<Anchor point="TOPLEFT" relativeTo="PetStableModelRotateLeftButton" relativePoint="TOPRIGHT"/>
|
|
</Anchors>
|
|
<NormalTexture file="Interface\Buttons\UI-RotationRight-Button-Up"/>
|
|
<PushedTexture file="Interface\Buttons\UI-RotationRight-Button-Down"/>
|
|
<HighlightTexture file="Interface\Buttons\ButtonHilight-Round" alphaMode="ADD"/>
|
|
<Scripts>
|
|
<OnLoad>this:RegisterForClicks("LeftButtonDown", "LeftButtonUp")</OnLoad>
|
|
<OnClick>Model_RotateRight(this:GetParent())</OnClick>
|
|
</Scripts>
|
|
</Button>
|
|
<!-- The diet icon (xml l.248-278): 24x23 under the rotate buttons, its texcoords
|
|
STATIC. This is the pet's DIET affordance (its tooltip lists what it eats),
|
|
not a happiness readout — the happiness face that does move lives on the pet
|
|
unit frame (decisions 1005/0990), and PetPaperDollFrame.xml's twin carries
|
|
the same note.
|
|
|
|
Kept as a CHILD of the pane, where the reference has it, and not lifted to a
|
|
sibling the way the pet paper doll's was: Update hides the pane whenever
|
|
there is no pet to show, and being a child is what makes the icon go with
|
|
it. Declared after the pane's own quad, so at the shared BACKGROUND layer
|
|
the link stamp draws it on top. -->
|
|
<Frame name="PetStablePetInfo" enableMouse="true" hidden="false">
|
|
<Size><AbsDimension x="24" y="23"/></Size>
|
|
<Anchors>
|
|
<Anchor point="TOPLEFT" relativeTo="PetStableModelRotateLeftButton" relativePoint="BOTTOMLEFT">
|
|
<Offset><AbsDimension x="7" y="0"/></Offset>
|
|
</Anchor>
|
|
</Anchors>
|
|
<Layers>
|
|
<Layer level="BACKGROUND">
|
|
<Texture 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>
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:SetText(this.tooltip)
|
|
</OnEnter>
|
|
<OnLeave>GameTooltip:Hide()</OnLeave>
|
|
</Scripts>
|
|
</Frame>
|
|
</Frames>
|
|
<Scripts>
|
|
<!-- The reference's own pair (xml l.194-203): `Model_OnLoad` seeds the 0.61
|
|
default facing through `SetRotation`, and `Model_OnUpdate` is the
|
|
held-button continuous spin. The app mirrors this pane's facing onto the
|
|
"stable" booth each frame (`ui_stable`). -->
|
|
<OnLoad>Model_OnLoad()</OnLoad>
|
|
<OnUpdate>Model_OnUpdate(arg1, this)</OnUpdate>
|
|
</Scripts>
|
|
</PlayerModel>
|
|
|
|
<!-- The three slots (xml l.281-333). `id=` is what `this:GetID()` reads in the
|
|
template's handlers, and it IS the binding's slot index: 0 is the pet at your side,
|
|
1 and 2 the bought stable slots. The two labels are extra regions on the instances,
|
|
anchored above their buttons; note the reference hangs "Stabled Pets:" off slot 1
|
|
offset +24 so it centres over the PAIR. -->
|
|
<CheckButton name="PetStableCurrentPet" inherits="PetStableSlotTemplate" id="0">
|
|
<Anchors>
|
|
<Anchor point="BOTTOMLEFT"><Offset><AbsDimension x="95" y="150"/></Offset></Anchor>
|
|
</Anchors>
|
|
<Layers>
|
|
<Layer level="BACKGROUND">
|
|
<FontString inherits="GameFontNormalSmall" text="CURRENT_PET">
|
|
<Anchors>
|
|
<Anchor point="BOTTOM" relativePoint="TOP">
|
|
<Offset><AbsDimension x="0" y="6"/></Offset>
|
|
</Anchor>
|
|
</Anchors>
|
|
</FontString>
|
|
</Layer>
|
|
</Layers>
|
|
</CheckButton>
|
|
<CheckButton name="PetStableStabledPet1" inherits="PetStableSlotTemplate" id="1">
|
|
<Anchors>
|
|
<Anchor point="LEFT" relativeTo="PetStableCurrentPet" relativePoint="RIGHT">
|
|
<Offset><AbsDimension x="53" y="0"/></Offset>
|
|
</Anchor>
|
|
</Anchors>
|
|
<Layers>
|
|
<Layer level="BACKGROUND">
|
|
<FontString inherits="GameFontNormalSmall" text="STABLED_PETS">
|
|
<Anchors>
|
|
<Anchor point="BOTTOM" relativePoint="TOP">
|
|
<Offset><AbsDimension x="24" y="6"/></Offset>
|
|
</Anchor>
|
|
</Anchors>
|
|
</FontString>
|
|
</Layer>
|
|
</Layers>
|
|
</CheckButton>
|
|
<CheckButton name="PetStableStabledPet2" inherits="PetStableSlotTemplate" id="2">
|
|
<Anchors>
|
|
<Anchor point="LEFT" relativeTo="PetStableStabledPet1" relativePoint="RIGHT">
|
|
<Offset><AbsDimension x="15" y="0"/></Offset>
|
|
</Anchor>
|
|
</Anchors>
|
|
</CheckButton>
|
|
|
|
<!-- Buy the next stable slot (xml l.334-350). The server prices it and picks which slot
|
|
it is; Update decides whether this button is even on screen. -->
|
|
<Button name="PetStablePurchaseButton" inherits="UIPanelButtonTemplate" text="PURCHASE">
|
|
<Size><AbsDimension x="80" y="22"/></Size>
|
|
<Anchors>
|
|
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-110" y="105"/></Offset></Anchor>
|
|
</Anchors>
|
|
<Scripts>
|
|
<OnClick>BuyStableSlot()</OnClick>
|
|
</Scripts>
|
|
</Button>
|
|
<!-- The player's purse (xml l.351-364). NO <Scripts> here: benilla's
|
|
SmallMoneyFrameTemplate already wires SmallMoneyFrame_OnLoad as its own OnLoad
|
|
where the reference's template does not, and an instance block would SetScript OVER
|
|
it rather than add to it (header, deferral 5). Type PLAYER by default, which is
|
|
what a purse wants. -->
|
|
<Frame name="PetStableMoneyFrame" inherits="SmallMoneyFrameTemplate">
|
|
<Anchors>
|
|
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-45" y="84"/></Offset></Anchor>
|
|
</Anchors>
|
|
</Frame>
|
|
<!-- The next slot's price (xml l.365-379): a STATIC frame, so it shows whatever
|
|
MoneyFrame_Update last parked on it rather than tracking the purse. This one DOES
|
|
declare an OnLoad, and therefore has to re-call the template's loader beside
|
|
MoneyFrame_SetType (the AuctionFrame.xml/MailFrame.xml idiom). -->
|
|
<Frame name="PetStableCostMoneyFrame" inherits="SmallMoneyFrameTemplate">
|
|
<Anchors>
|
|
<Anchor point="LEFT" relativeTo="PetStableCostLabel" relativePoint="RIGHT">
|
|
<Offset><AbsDimension x="5" y="0"/></Offset>
|
|
</Anchor>
|
|
</Anchors>
|
|
<Scripts>
|
|
<OnLoad>SmallMoneyFrame_OnLoad() MoneyFrame_SetType("STATIC")</OnLoad>
|
|
</Scripts>
|
|
</Frame>
|
|
<!-- xml l.380-388. UIPanelCloseButton's own OnClick is HideUIPanel(this:GetParent()),
|
|
and this window's OnHide is what ends the session — so the X needs no handler. -->
|
|
<Button name="PetStableFrameCloseButton" inherits="UIPanelCloseButton">
|
|
<Anchors>
|
|
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-29" y="-8"/></Offset></Anchor>
|
|
</Anchors>
|
|
</Button>
|
|
</Frames>
|
|
<!-- xml l.390-404. The NPC-session lifecycle: OnHide fires ClosePetStables() so the session
|
|
ends whenever this window hides for ANY reason (ESC, the X, PET_STABLE_CLOSED, or
|
|
another left-slot window displacing it). Unlike its neighbours that close is purely
|
|
client-side — no packet exists for it in 5875. -->
|
|
<Scripts>
|
|
<OnLoad>PetStable_OnLoad()</OnLoad>
|
|
<OnShow>PlaySound("igCharacterInfoOpen")</OnShow>
|
|
<OnHide>ClosePetStables() PlaySound("igCharacterInfoClose")</OnHide>
|
|
<OnEvent>PetStable_OnEvent()</OnEvent>
|
|
</Scripts>
|
|
</Frame>
|
|
</Ui>
|