Files
benilla-octo/crates/benilla-app/assets/ui/DuelFrame.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

95 lines
5.2 KiB
XML

<Ui xmlns="http://www.blizzard.com/wow/ui/">
<!--
The duel UI (decision 0633): the two duel dialogs on our shared StaticPopup engine
(UiPanels.xml) plus the duel-event driver. The patch-chain FrameXML (mpqx extraction,
decision 0246) is the SPECIFICATION — StaticPopup.lua l.1103-1120 (DUEL_REQUESTED +
DUEL_OUTOFBOUNDS), UIParent.lua l.98-101 (the registrations) + l.378-393 (the event arms)
— and what it fixes is contract: the two dialog keys, the StaticPopupDialogs field names,
the sound kit and the two timeouts are all addressed by name, so they are reproduced
exactly while the entries around them are written here in our own shape. The strings are
the shipped enUS values from GlobalStrings.lua, quoted as data.
Duels have no window of their own — these two dialogs ARE the whole duel UI. Everything
else is engine-side: the "Duel starting: N" countdown and the outcome line are
CHAT_MSG_SYSTEM lines `crate::ui_duel` composes (byte-verified: WoW.exe's own duel TU emits
both through the chat path with type 0x0A, not through any frame), and the four events
below are fired from that module's state edges.
The DUEL_OUTOFBOUNDS dialog is text-only — no buttons, timeout 10 — and its text is filled
by the StaticPopup engine's per-tick countdown branch, which already names it in both of
its special-case lists (UiPanels.xml: the show-time " " seeding and the OnUpdate re-format).
The 10 s is the client's dialog timeout, and it coincides with the server's own forfeit
window (`Player::CheckDuelDistance`) rather than driving it: the popup expiring is cosmetic,
the forfeit is the server's.
-->
<Script><![CDATA[
-- ── Strings (the shipped enUS GlobalStrings.lua values, quoted as data) ─────────────────
local DUEL_REQUESTED = "%s has challenged you to a duel." -- l.957
local DUEL_OUTOFBOUNDS_TIMER = "Exiting duel area, you will forfeit in %d %s." -- l.956
local ACCEPT = "Accept"
local DECLINE = "Decline"
-- ── The challenge dialog (spec: StaticPopup.lua l.1103-1116) ────────────────────────────
-- Written scalars-first, handlers-last: what the dialog IS, then what answering it does.
--
-- No accept-guard flag (unlike PARTY_INVITE): the entry declines only on an explicit
-- OnCancel, so a timeout or an override hides it WITHOUT sending anything. That is not an
-- oversight to "fix" — the server's own 60 s is not a thing either; an unanswered challenge
-- simply sits until one side acts.
StaticPopupDialogs["DUEL_REQUESTED"] = {
text = DUEL_REQUESTED,
button1 = ACCEPT,
button2 = DECLINE,
sound = "igPlayerInvite",
timeout = 60,
hideOnEscape = 1,
-- The two buttons are the two replies. ESC arrives here as well — hideOnEscape routes
-- an escape through OnCancel — so backing out and declining are deliberately one path.
OnAccept = function()
AcceptDuel()
end,
OnCancel = function()
CancelDuel()
end,
}
-- ── The out-of-bounds warning (spec: StaticPopup.lua l.1117-1120) ───────────────────────
-- Two fields and nothing else: no buttons, because there is nothing to answer, and no
-- text of its own — the engine's countdown branch writes it every tick.
StaticPopupDialogs["DUEL_OUTOFBOUNDS"] = {
text = DUEL_OUTOFBOUNDS_TIMER,
timeout = 10,
}
-- ── The duel event arms (spec: UIParent.lua l.378-393) ──────────────────────────────────
function BenillaDuelDriver_OnEvent()
if event == "DUEL_REQUESTED" then
StaticPopup_Show("DUEL_REQUESTED", arg1)
elseif event == "DUEL_OUTOFBOUNDS" then
StaticPopup_Show("DUEL_OUTOFBOUNDS")
elseif event == "DUEL_INBOUNDS" then
StaticPopup_Hide("DUEL_OUTOFBOUNDS")
elseif event == "DUEL_FINISHED" then
-- Both, unconditionally: a duel that ends while the challenge popup is still up
-- (the other side cancelled first) must not leave it there.
StaticPopup_Hide("DUEL_REQUESTED")
StaticPopup_Hide("DUEL_OUTOFBOUNDS")
end
end
]]></Script>
<Frame name="BenillaDuelDriver" hidden="true">
<Size><AbsDimension x="1" y="1"/></Size>
<Anchors><Anchor point="TOPLEFT"/></Anchors>
<Scripts>
<OnLoad>
self:RegisterEvent("DUEL_REQUESTED")
self:RegisterEvent("DUEL_OUTOFBOUNDS")
self:RegisterEvent("DUEL_INBOUNDS")
self:RegisterEvent("DUEL_FINISHED")
</OnLoad>
<OnEvent>BenillaDuelDriver_OnEvent()</OnEvent>
</Scripts>
</Frame>
</Ui>