Files
benilla-octo/crates/benilla-app/assets/ui/TaxiFrame.xml
T
SamandClaude Fable 5 d1140829d7 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

558 lines
32 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<Ui>
<!--
benilla's taxi-map window (decision 0484 phase 2), written on the taxi engine seam
(`crates/benilla-ui/src/script/taxi.rs`) fed by `crates/benilla/src/ui_taxi.rs`.
This is **our implementation**. Its specification is the real 1.12 `TaxiFrame.{xml,lua}`
(patch chain; scratchpad `ref-TaxiFrame.xml/.lua`, line citations inline as `ref l.N`): the
globals, the art paths, the geometry and the behaviour of every branch are the
specification's, because addons and the engine both address this window by those names, and
because the numbers are measurements of the shipped art (384×512, the four TaxiFrame
quadrant slabs at a 256/128 split, the 316×352 map rect at TOP -13,-75) rather than
anything for us to re-derive. How it is written is ours. The deviations below are where
THIS ENGINE, not taste, draws the line, each flagged and reasoned like every other window
of ours (Gossip/Merchant/Trainer's own header notes).
Modeled on MerchantFrame.xml/TrainerFrame.xml for the framing: the NPC-session lifecycle
(RegisterEvent pair, OPENED/CLOSED dispatch, OnShow/OnHide PlaySound with a client-side
close on hide), the "npc" model-portrait binding, the minimize-button close X (this engine
ships no UIPanelCloseButton), and the UIPanel slot system (NO hardcoded `<Anchors>` on the
root — ShowUIPanel/SetLeftFrame SetPoints it to the left slot on open, exactly like every
other NPC window).
DEVIATIONS from the literal reference, each forced by this engine (never taste):
1. NODE BUTTONS ARE A STATIC POOL, not `CreateFrame(..., "TaxiButtonTemplate")` grown on
demand (ref l.36-42/TaxiFrame.lua:36-42). This engine's runtime `CreateFrame`'s 4th
`inherits` argument is explicitly a no-op — template expansion is the XML LOADER's job
only (`benilla-ui/src/script/object.rs`'s `create_frame`: "inherits/template expansion
is the XML loader's job — ignored"). Every other list window in this codebase hits the
identical wall and solves it identically: Gossip's 8-row pool, Trainer's service-row
pool, QuestLog's row pool — a fixed set of concrete `inherits=` instances (which DOES
expand at LOAD time), shown/hidden/repositioned per repaint instead of created on
demand. `BENILLA_TAXI_MAX_NODES = 50` below comfortably covers the real 5875
`TaxiNodes.dbc`'s per-continent max (43 nodes on Eastern Kingdoms, 39 on Kalimdor —
counted this session, `crate::ui_taxi`'s module doc). The route-line textures stay
genuinely dynamic (`TaxiRouteMap:CreateTexture(...)`, ref l.101/142) — plain
region creation carries no template/inherits, so that half is created on demand exactly
as specified.
2. THE TITLE READS AN EVENT ARG, not `UnitName("npc")` (ref TaxiFrame.lua:27). This
engine's `"npc"` unit token (decision 0421) only feeds the PORTRAIT bake — there is no
backing `UnitState` for `UnitName` to read from it. Gossip/Merchant/Trainer hit the
identical gap and all solve it the same way: the app resolves the flight master's name
through the `NameCache` and rides it in as the open event's arg1 — done here too
(`BenillaTaxiFrame_SetTitle`, fed by `TAXIMAP_OPENED`'s arg1).
3. (Retired.) The "no single-hop destination" refusal (`DrawOneHopLines`, ref l.161-164)
used to call a plain `BenillaErrorsFrame_AddMessage` global because this engine had no
MessageFrame widget for `UIErrorsFrame` to be. It has one now, so the refusal below posts
to `UIErrorsFrame` under the specified name. `ErrorsFrame.xml` loads well before this file
in the shipped list (`ui_script/mod.rs`), so the frame already exists by the time this
ever runs.
`<TaxiRouteFrame>` (an intrinsic type this engine doesn't have) becomes a plain `<Frame>`
(`TaxiRouteMap`) per the house rule for every non-shipped intrinsic (decision 0246's
patch-chain rule; the `Cooldown`/`GameTooltip`/etc. precedents) — it carries no behavior of
its own beyond being a Frame that owns child regions/buttons, so the plain type loses
nothing.
Money (the fare) renders as plain text via `SetTooltipMoney` on the shared `GameTooltip`
(`GameTooltip.xml`'s own seam) exactly as specified — no `BenillaMoney_*` coin rig needed
here (that lives on the merchant/trainer purse displays, a different job).
-->
<Script><![CDATA[
-- The continent map's size in pixels — a published pair, because scaling a node's
-- normalised position onto the map needs them and so does anything that wants to put its own
-- marker there. The values are the specification's (ref TaxiFrame.lua:2-3): a measurement of
-- the shipped map art.
TAXI_MAP_WIDTH = 316
TAXI_MAP_HEIGHT = 352
-- The static node-button pool's size — see deviation 1 above.
BENILLA_TAXI_MAX_NODES = 50
-- What each node state draws (ref TaxiFrame.lua:7-16): CURRENT green, REACHABLE white,
-- DISTANT yellow — all three art files verified present in the MPQ. Published as a table of
-- `{ file = }` keyed by the state string `TaxiNodeGetType` answers, which is both the shape
-- the node paint below reads and the shape an addon adding a state of its own writes.
TaxiButtonTypes = {
CURRENT = { file = "Interface\\TaxiFrame\\UI-Taxi-Icon-Green" },
REACHABLE = { file = "Interface\\TaxiFrame\\UI-Taxi-Icon-White" },
DISTANT = { file = "Interface\\TaxiFrame\\UI-Taxi-Icon-Yellow" },
}
-- The two strings this window shows, byte-exact from the 1.12 enUS GlobalStrings
-- (interface.MPQ `GlobalStrings.lua`, line-cited) — user-visible text, so its bytes are the
-- spec, not our expression. `TEXT(x)` (ref l.121) is the real client's build-time no-op
-- localization macro, so every `TEXT(FOO)` resolves to the bare `FOO` global
-- (CharacterFrame.xml's own convention); with no engine `TEXT` global at all, and nothing
-- outside this window reading either string, they are plain locals here instead.
local TAXINODEYOUAREHERE = "You are here" -- GlobalStrings.lua:4232
-- GlobalStrings.lua:1864 — the reference's own byte, including its curly apostrophe
-- (U+2019, "don\226\128\153t"), not a straight one.
local ERR_TAXINOPATHS = "You don\226\128\153t know any flight locations connected to this one."
function TaxiFrame_OnLoad()
local frame = this
-- The "npc" portrait token (decision 0421): the open taxi session's flight master
-- bakes into the ring, resolved exactly like Gossip/Merchant/Trainer's own OnLoad.
SetPortraitTexture(getglobal(frame:GetName() .. "Portrait"), "npc")
frame:RegisterEvent("TAXIMAP_OPENED")
frame:RegisterEvent("TAXIMAP_CLOSED")
end
-- Deviation 2 above: the flight master's name rides TAXIMAP_OPENED's arg1 (the app
-- resolves it through the NameCache) rather than a live UnitName("npc") read.
function BenillaTaxiFrame_SetTitle(name)
getglobal("TaxiNameText"):SetText(name or "")
end
-- Paint the node buttons (ref l.44-63): every pool slot up to the pushed node count takes
-- that node's state icon and its pushed position, and the rest of the pool hides. Positions
-- arrive normalised 0..1 from the map's BOTTOMLEFT, which is what the map size above scales.
local function PaintNodes()
local pushed = NumTaxiNodes()
for index = 1, BENILLA_TAXI_MAX_NODES do
local button = getglobal("TaxiButton" .. index)
local state = TaxiNodeGetType(index)
if index <= pushed and state ~= "NONE" then
local x, y = TaxiNodePosition(button:GetID())
button:ClearAllPoints()
button:SetPoint("CENTER", "TaxiMap", "BOTTOMLEFT", x * TAXI_MAP_WIDTH, y * TAXI_MAP_HEIGHT)
button:SetNormalTexture(TaxiButtonTypes[state].file)
button:Show()
else
button:Hide()
end
end
end
-- Opening and closing the window (ref TaxiFrame.lua:24-80). Opening repaints everything from
-- the pushed snapshot and then asks the panel manager for a slot; if it will not give one,
-- the session is closed again rather than left running behind a window nobody can see.
function TaxiFrame_OnEvent(event)
local frame = this
if event == "TAXIMAP_OPENED" then
BenillaTaxiFrame_SetTitle(arg1)
-- Reset the map's sampling, then hand the region to the app's continent-art pick.
TaxiMap:SetTexCoord(0, 1, 0, 1)
SetTaxiMap(TaxiMap)
PaintNodes()
ShowUIPanel(frame)
if not frame:IsVisible() then
CloseTaxiMap()
end
return
end
if event == "TAXIMAP_CLOSED" then
HideUIPanel(frame)
return
end
end
-- ── The route-line pool ──────────────────────────────────────────────────────────────────
--
-- A flight path draws as a chain of straight segments, one texture per segment. Those
-- textures are a pool that grows to the longest chain ever asked for and is never shrunk:
-- `NUM_TAXI_ROUTES` is how many exist and `TaxiRoute<n>` is each one's global name — both
-- published, since the count is a global the specification exports and the names are what
-- `getglobal` finds. Unlike the node buttons, this half really is created on demand: plain
-- region creation carries no template, so deviation 1's CreateFrame wall never applies.
NUM_TAXI_ROUTES = 0
-- Grow the pool to hold at least `n` lines; answer what it holds.
local function GrowRoutePool(n)
while NUM_TAXI_ROUTES < n do
NUM_TAXI_ROUTES = NUM_TAXI_ROUTES + 1
local line = TaxiRouteMap:CreateTexture("TaxiRoute" .. NUM_TAXI_ROUTES, "BACKGROUND")
line:SetTexture("Interface\\TaxiFrame\\UI-Taxi-Line")
end
return NUM_TAXI_ROUTES
end
-- Draw one line per `{ node, hop }` in `segments`, and hide whatever the pool holds beyond
-- them. Each hop's endpoints arrive normalised on the map, so they scale by the canvas the
-- lines live on. 32 is the specified line thickness (ref l.114/153), in those same pixels.
local function PaintRoutes(segments)
local count = table.getn(segments)
local pool = GrowRoutePool(count)
local w = TaxiRouteMap:GetWidth()
local h = TaxiRouteMap:GetHeight()
for i = 1, pool do
local line = getglobal("TaxiRoute" .. i)
if i <= count then
local node, hop = segments[i][1], segments[i][2]
DrawRouteLine(line, "TaxiRouteMap",
TaxiGetSrcX(node, hop) * w, TaxiGetSrcY(node, hop) * h,
TaxiGetDestX(node, hop) * w, TaxiGetDestY(node, hop) * h, 32)
line:Show()
else
line:Hide()
end
end
end
-- The tooltip, and the route, for the node under the cursor (ref TaxiFrame.lua:82-126). A
-- reachable node prices the flight and draws every hop of it; the node we are standing on
-- says so and draws the whole neighbourhood instead.
--
-- `this:GetID()` in the fare read is the SAME button as the `button` parameter — the engine
-- binds the shared `this` global to the invoking frame before OnEnter runs (decision 0068's
-- event-dispatch convention), exactly like the real client, so the two reads agree.
function TaxiNodeOnButtonEnter(button)
local index = button:GetID()
GameTooltip:SetOwner(button, "ANCHOR_RIGHT")
GameTooltip:AddLine(TaxiNodeName(index), "", 1.0, 1.0, 1.0)
local state = TaxiNodeGetType(index)
if state == "REACHABLE" then
SetTooltipMoney(GameTooltip, TaxiNodeCost(this:GetID()))
TaxiNodeSetCurrent(index)
local hops = { }
for hop = 1, GetNumRoutes(index) do
hops[hop] = { index, hop }
end
PaintRoutes(hops)
elseif state == "CURRENT" then
GameTooltip:AddLine(TAXINODEYOUAREHERE, "", 0.5, 1.0, 0.5)
DrawOneHopLines()
end
GameTooltip:Show()
end
-- Every flight path within one hop of where we stand (ref TaxiFrame.lua:129-165) — what the
-- map shows the moment it opens, before anything is hovered. A node exactly one segment away
-- is one this flight master can send us to directly.
function DrawOneHopLines()
local hops = { }
for node = 1, NumTaxiNodes() do
if GetNumRoutes(node) == 1 then
table.insert(hops, { node, 1 })
end
end
PaintRoutes(hops)
-- A flight point with nothing one hop away is a dead end: say so, and take the window
-- back down rather than leave an empty map up.
if table.getn(hops) == 0 then
UIErrorsFrame:AddMessage(ERR_TAXINOPATHS, 1.0, 0.1, 0.1)
HideUIPanel(TaxiFrame)
end
end
-- ── Route lines ───────────────────────────────────────────────────────────────────────
--
-- A flight path is one straight line between two map points, drawn as a texture. The
-- engine can only place an AXIS-ALIGNED rectangle, so the rotation has to live in the
-- texture coordinates instead: SetTexCoord's 8-argument form places each of the four
-- source corners independently, which is exactly enough to spin (and shear) the art
-- inside its bounding box. The bounding box then has to grow to hold the rotated line.
--
-- The geometry — how a rotation angle turns into those four corners and that box — is
-- the specification, and it is the reference's (ref TaxiFrame.lua:168-226; the credit
-- their copy carries is to Daniel Stephens <iriel@vigilance-committee.org>). Every value
-- below is theirs, and the multiplications are grouped the way theirs are so the rounding
-- matches; how it is written — the naming, the shear terms hoisted out of both branches,
-- the anchoring helper — is ours. The reference derives each corner from the last
-- (`BRx = 1 - BLy`, `TRy = BRx`, …); we state each one directly instead, which is the
-- same number everywhere but one: the downward branch's top-left U is `-shearU` here
-- against their `1 - (1 + shearU)`. MEASURED, both implementations run side by side on
-- the real Lua 5.1 over 50k randomised segments plus the degenerate/axis-aligned/reversed
-- edges: every anchor identical to the last bit, and every texture coordinate identical
-- once narrowed to f32 — which the engine does to all eight on the way in
-- (`region::paint`'s `Variadic<f32>`). The lone survivor is the SIGN OF ZERO on an exactly
-- vertical downward line, where that corner is -0 here and +0 there: the same coordinate,
-- and the same sample.
--
-- Engine seam, all three proved by the phase-1 seam test: CreateTexture, SetTexCoord's
-- 8-arg form, and SetPoint against a canvas named by string.
-- The reference's two scale globals, value for value. Only the halved one is ever read —
-- by the bounding-box extents below, which are half-widths, hence the /2; the whole one
-- is a published global that nothing in the reference reads either. 32/30 is unexplained
-- there and carried unchanged here — a measurement of the shipped art, not ours to
-- re-derive.
TAXIROUTE_LINEFACTOR = 32/30
TAXIROUTE_LINEFACTOR_2 = TAXIROUTE_LINEFACTOR / 2
-- Seat `texture` as the box centred on (cx, cy) that reaches `halfW` / `halfH` out along
-- each axis, measured from `canvas`'s `relPoint` corner. Both ends always move together,
-- so clearing first is what keeps a stale anchor from outliving a move. (The reference
-- re-seats the two points without clearing on its degenerate path; a route texture only
-- ever holds these two anchors and SetPoint replaces one of the same name, so the box
-- that comes out is the same either way.)
local function AnchorCenteredBox(texture, canvas, relPoint, cx, cy, halfW, halfH)
texture:ClearAllPoints()
texture:SetPoint("BOTTOMLEFT", canvas, relPoint, cx - halfW, cy - halfH)
texture:SetPoint("TOPRIGHT", canvas, relPoint, cx + halfW, cy + halfH)
end
-- Draw the segment (sx,sy) -> (ex,ey), `width` thick, into `texture`, placed against
-- `canvas` in the coordinate space of its `relPoint` corner (BOTTOMLEFT by default).
function DrawRouteLine(texture, canvas, sx, sy, ex, ey, width, relPoint)
relPoint = relPoint or "BOTTOMLEFT"
local dx, dy = ex - sx, ey - sy
local cx, cy = (sx + ex) / 2, (sy + ey) / 2
-- A segment drawn back-to-front is the same segment: face it rightwards so only the
-- two upward/downward cases below have to exist.
if dx < 0 then
dx, dy = -dx, -dy
end
local len = sqrt((dx * dx) + (dy * dy))
if len == 0 then
-- Nothing to draw: collapse the quad onto its own midpoint and show no art.
texture:SetTexCoord(0, 0, 0, 0, 0, 0, 0, 0)
AnchorCenteredBox(texture, canvas, relPoint, cx, cy, 0, 0)
return
end
-- Sine and cosine of the segment's angle, and the two shear terms every corner is
-- built from — one walking the texture's horizontal axis, one its vertical axis.
local sinA, cosA = -dy / len, dx / len
local sinSq = sinA * sinA
local shear = sinA * cosA
local shearU = (width / len) * shear
local shearV = (len / width) * shear
-- Half the bounding box the rotated line needs, and the four source corners that lay
-- the art across it. Upward and downward are the same algebra mirrored: the four
-- quantities are the same, and which corner each lands on is what flips.
local halfW, halfH
local blU, blV, tlU, tlV, trU, trV, brU, brV
if dy >= 0 then
halfW = ((len * cosA) - (width * sinA)) * TAXIROUTE_LINEFACTOR_2
halfH = ((width * cosA) - (len * sinA)) * TAXIROUTE_LINEFACTOR_2
blU, blV = shearU, sinSq
tlU, tlV = sinSq, 1 - shearV
trU, trV = 1 - shearU, 1 - sinSq
brU, brV = 1 - sinSq, shearV
else
halfW = ((len * cosA) + (width * sinA)) * TAXIROUTE_LINEFACTOR_2
halfH = ((width * cosA) + (len * sinA)) * TAXIROUTE_LINEFACTOR_2
blU, blV = sinSq, -shearV
tlU, tlV = -shearU, 1 - sinSq
trU, trV = 1 - sinSq, 1 + shearV
brU, brV = 1 + shearU, sinSq
end
-- SetTexCoord's corner order is top-left, bottom-left, top-right, bottom-right.
texture:SetTexCoord(tlU, tlV, blU, blV, trU, trV, brU, brV)
AnchorCenteredBox(texture, canvas, relPoint, cx, cy, halfW, halfH)
end
-- Appearing and disappearing. The sounds are the MainMenu pair, not the CharacterInfo pair
-- Merchant/Trainer use — the specification genuinely names a different one for this window
-- (ref l.162-169). Showing also draws the neighbourhood straight away, so an untouched map
-- is not blank; hiding ends the session, which is what fires TAXIMAP_CLOSED back at us.
function BenillaTaxiFrame_OnShow()
PlaySound("igMainMenuOpen")
DrawOneHopLines()
end
function BenillaTaxiFrame_OnHide()
CloseTaxiMap()
PlaySound("igMainMenuClose")
end
function BenillaTaxiCloseButton_OnClick()
-- vanilla's client-side close sends no packet; the app clears TaxiState on this
-- intent (which fires TAXIMAP_CLOSED), and we hide immediately so there's no lag.
CloseTaxiMap()
HideUIPanel(TaxiFrame)
end
]]></Script>
<!--
The node-button pool's virtual template (ref TaxiButtonTemplate, l.4-32): a 16×16 button,
the state icon assigned per repaint via SetNormalTexture (deviation 1's static pool — see
the header note), the 32×32 CENTER highlight glow. The three handlers dispatch to the same
three verbs the specification's do (l.12-20) — including the click firing UNCONDITIONALLY
regardless of node state (a DISTANT/CURRENT click still queues TakeTaxiNode; the app-side
drain treats a routeless click as a no-op — there is no RE'd finding for what the real
client's native TakeTaxiNode does with an unreachable index, so this is the safe,
undocumented-but-reasonable inference, not an asserted fact).
-->
<Button name="BenillaTaxiButtonTemplate" hidden="true" virtual="true">
<Size>
<AbsDimension x="16" y="16"/>
</Size>
<Anchors>
<Anchor point="BOTTOMRIGHT"/>
</Anchors>
<Scripts>
<OnEnter>
TaxiNodeOnButtonEnter(this)
</OnEnter>
<OnLeave>
GameTooltip:Hide()
</OnLeave>
<OnClick>
TakeTaxiNode(this:GetID())
</OnClick>
</Scripts>
<HighlightTexture file="Interface\TaxiFrame\UI-Taxi-Icon-Highlight">
<Size x="32" y="32"/>
<Anchors>
<Anchor point="CENTER">
<Offset>
<AbsDimension x="0" y="0"/>
</Offset>
</Anchor>
</Anchors>
</HighlightTexture>
</Button>
<Frame name="TaxiFrame" hidden="true">
<!-- The real 384×512 window (ref l.34-36). No <Anchors> — ShowUIPanel/SetLeftFrame
SetPoints this to the left slot on open, exactly like Gossip/Merchant/Trainer. -->
<Size><AbsDimension x="384" y="512"/></Size>
<Layers>
<!-- NPC portrait 58×58 TOPLEFT (8,-9) (ref TaxiPortrait, l.46-57), BACKGROUND —
drawn under the ARTWORK quadrant slabs below so their frame art rings it. -->
<Layer level="BACKGROUND">
<Texture name="$parentPortrait">
<Size><AbsDimension x="58" y="58"/></Size>
<Anchors>
<Anchor point="TOPLEFT"><Offset><AbsDimension x="8" y="-9"/></Offset></Anchor>
</Anchors>
</Texture>
</Layer>
<!-- The four TaxiFrame quadrant slabs that ARE the window art (ref l.59-103): 256-wide
left halves, 128-wide right halves, each 256 tall, pinned to their corner. No
<TexCoords> — each samples its whole texture. -->
<Layer level="ARTWORK">
<Texture file="Interface\TaxiFrame\UI-TaxiFrame-TopLeft">
<Size><AbsDimension x="256" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT"/></Anchors>
</Texture>
<Texture file="Interface\TaxiFrame\UI-TaxiFrame-TopRight">
<Size><AbsDimension x="128" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="256" y="0"/></Offset></Anchor></Anchors>
</Texture>
<Texture file="Interface\TaxiFrame\UI-TaxiFrame-BotLeft">
<Size><AbsDimension x="256" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="0" y="-256"/></Offset></Anchor></Anchors>
</Texture>
<Texture file="Interface\TaxiFrame\UI-TaxiFrame-BotRight">
<Size><AbsDimension x="128" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="256" y="-256"/></Offset></Anchor></Anchors>
</Texture>
<!-- The flight master's name banner (ref TaxiMerchant, l.104-115): GameFontNormal,
CENTER, TOP -17, 192 wide — deviation 2 above (arg1, not UnitName("npc")). -->
<FontString name="TaxiNameText" inherits="GameFontNormal" justifyH="CENTER">
<Size><AbsDimension x="192" y="0"/></Size>
<Anchors>
<Anchor point="TOP"><Offset><AbsDimension x="0" y="-17"/></Offset></Anchor>
</Anchors>
</FontString>
</Layer>
<!-- The continent map art (ref TaxiMap, l.117-129): 316×352, TOP -13,-75. No file —
SetTaxiMap assigns the app's TAXIMAP<continentId> pick (byte-verified — decision 0496). -->
<Layer level="OVERLAY">
<Texture name="TaxiMap">
<Size><AbsDimension x="316" y="352"/></Size>
<Anchors>
<Anchor point="TOP"><Offset><AbsDimension x="-13" y="-75"/></Offset></Anchor>
</Anchors>
</Texture>
</Layer>
</Layers>
<Frames>
<!-- Close: the real UIPanelCloseButton position (TOPRIGHT -29,-8; ref l.133-141), on
the minimize-button art (this engine ships no UIPanelCloseButton — the Gossip/
Trainer/Merchant precedent). -->
<Button name="TaxiCloseButton">
<Size><AbsDimension x="32" y="32"/></Size>
<Anchors>
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-29" y="-8"/></Offset></Anchor>
</Anchors>
<NormalTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Up"/>
<PushedTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Down"/>
<HighlightTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Highlight" alphaMode="ADD"/>
<Scripts><OnClick>BenillaTaxiCloseButton_OnClick()</OnClick></Scripts>
</Button>
<!-- `<TaxiRouteFrame>` (ref l.142-153) — an intrinsic type this engine doesn't have —
becomes a plain <Frame> (see the header note): 316×352, TOP -13,-75, exactly
overlaid on TaxiMap. Houses the node-button pool + the dynamically-created
route-line textures, both parented here exactly like the reference (ref l.39,101). -->
<Frame name="TaxiRouteMap">
<Size><AbsDimension x="316" y="352"/></Size>
<Anchors>
<Anchor point="TOP"><Offset><AbsDimension x="-13" y="-75"/></Offset></Anchor>
</Anchors>
<Frames>
<Button name="TaxiButton1" inherits="BenillaTaxiButtonTemplate" id="1"/>
<Button name="TaxiButton2" inherits="BenillaTaxiButtonTemplate" id="2"/>
<Button name="TaxiButton3" inherits="BenillaTaxiButtonTemplate" id="3"/>
<Button name="TaxiButton4" inherits="BenillaTaxiButtonTemplate" id="4"/>
<Button name="TaxiButton5" inherits="BenillaTaxiButtonTemplate" id="5"/>
<Button name="TaxiButton6" inherits="BenillaTaxiButtonTemplate" id="6"/>
<Button name="TaxiButton7" inherits="BenillaTaxiButtonTemplate" id="7"/>
<Button name="TaxiButton8" inherits="BenillaTaxiButtonTemplate" id="8"/>
<Button name="TaxiButton9" inherits="BenillaTaxiButtonTemplate" id="9"/>
<Button name="TaxiButton10" inherits="BenillaTaxiButtonTemplate" id="10"/>
<Button name="TaxiButton11" inherits="BenillaTaxiButtonTemplate" id="11"/>
<Button name="TaxiButton12" inherits="BenillaTaxiButtonTemplate" id="12"/>
<Button name="TaxiButton13" inherits="BenillaTaxiButtonTemplate" id="13"/>
<Button name="TaxiButton14" inherits="BenillaTaxiButtonTemplate" id="14"/>
<Button name="TaxiButton15" inherits="BenillaTaxiButtonTemplate" id="15"/>
<Button name="TaxiButton16" inherits="BenillaTaxiButtonTemplate" id="16"/>
<Button name="TaxiButton17" inherits="BenillaTaxiButtonTemplate" id="17"/>
<Button name="TaxiButton18" inherits="BenillaTaxiButtonTemplate" id="18"/>
<Button name="TaxiButton19" inherits="BenillaTaxiButtonTemplate" id="19"/>
<Button name="TaxiButton20" inherits="BenillaTaxiButtonTemplate" id="20"/>
<Button name="TaxiButton21" inherits="BenillaTaxiButtonTemplate" id="21"/>
<Button name="TaxiButton22" inherits="BenillaTaxiButtonTemplate" id="22"/>
<Button name="TaxiButton23" inherits="BenillaTaxiButtonTemplate" id="23"/>
<Button name="TaxiButton24" inherits="BenillaTaxiButtonTemplate" id="24"/>
<Button name="TaxiButton25" inherits="BenillaTaxiButtonTemplate" id="25"/>
<Button name="TaxiButton26" inherits="BenillaTaxiButtonTemplate" id="26"/>
<Button name="TaxiButton27" inherits="BenillaTaxiButtonTemplate" id="27"/>
<Button name="TaxiButton28" inherits="BenillaTaxiButtonTemplate" id="28"/>
<Button name="TaxiButton29" inherits="BenillaTaxiButtonTemplate" id="29"/>
<Button name="TaxiButton30" inherits="BenillaTaxiButtonTemplate" id="30"/>
<Button name="TaxiButton31" inherits="BenillaTaxiButtonTemplate" id="31"/>
<Button name="TaxiButton32" inherits="BenillaTaxiButtonTemplate" id="32"/>
<Button name="TaxiButton33" inherits="BenillaTaxiButtonTemplate" id="33"/>
<Button name="TaxiButton34" inherits="BenillaTaxiButtonTemplate" id="34"/>
<Button name="TaxiButton35" inherits="BenillaTaxiButtonTemplate" id="35"/>
<Button name="TaxiButton36" inherits="BenillaTaxiButtonTemplate" id="36"/>
<Button name="TaxiButton37" inherits="BenillaTaxiButtonTemplate" id="37"/>
<Button name="TaxiButton38" inherits="BenillaTaxiButtonTemplate" id="38"/>
<Button name="TaxiButton39" inherits="BenillaTaxiButtonTemplate" id="39"/>
<Button name="TaxiButton40" inherits="BenillaTaxiButtonTemplate" id="40"/>
<Button name="TaxiButton41" inherits="BenillaTaxiButtonTemplate" id="41"/>
<Button name="TaxiButton42" inherits="BenillaTaxiButtonTemplate" id="42"/>
<Button name="TaxiButton43" inherits="BenillaTaxiButtonTemplate" id="43"/>
<Button name="TaxiButton44" inherits="BenillaTaxiButtonTemplate" id="44"/>
<Button name="TaxiButton45" inherits="BenillaTaxiButtonTemplate" id="45"/>
<Button name="TaxiButton46" inherits="BenillaTaxiButtonTemplate" id="46"/>
<Button name="TaxiButton47" inherits="BenillaTaxiButtonTemplate" id="47"/>
<Button name="TaxiButton48" inherits="BenillaTaxiButtonTemplate" id="48"/>
<Button name="TaxiButton49" inherits="BenillaTaxiButtonTemplate" id="49"/>
<Button name="TaxiButton50" inherits="BenillaTaxiButtonTemplate" id="50"/>
</Frames>
</Frame>
</Frames>
<Scripts>
<OnLoad>
TaxiFrame_OnLoad()
</OnLoad>
<OnEvent>
TaxiFrame_OnEvent(event)
</OnEvent>
<OnShow>
BenillaTaxiFrame_OnShow()
</OnShow>
<OnHide>
BenillaTaxiFrame_OnHide()
</OnHide>
</Scripts>
</Frame>
</Ui>