JASS choice dialogs
The Warcraft game module owns DialogCreate, DialogDestroy, DialogClear,
DialogSetMessage, DialogAddButton, DialogAddQuitButton, DialogDisplay,
TriggerRegisterDialogEvent, TriggerRegisterDialogButtonEvent,
GetClickedButton, and GetClickedDialog.
Ownership and input
level.dialogs and level.dialog_buttons are bounded pools (64 dialogs, 256
buttons). DialogClear and DialogDestroy release slots for reuse, and each
reuse advances a generation stored in the ID's high bits
((generation << 16) | (slot + 1)). A late click, a stale registration, or a
saved ID for a released occupant never matches the slot's new occupant.
Exhaustion is refused and logged to stderr. The server
retains the message, labels, hotkeys, visibility, and ownership; the universal
client receives an ordinary server-authored svc_window and returns
jassdialog <dialog-id> <button-id>. The game validates both IDs, parentage,
client identity, and player-specific visibility, then consumes that visibility
before emitting an event. Duplicated network commands are ignored.
UI_WINDOW_CLOSE is a generic server-to-client svc_window operation carrying
only the window ID. It is not a Warcraft-specific UI protocol. It lets
DialogDisplay(false), DialogClear, and DialogDestroy release modal focus
without synthesizing an answer. The client's close_window_command handles a
selected button's local closure without changing simulation state.
When a result dialog has paused the single-player game, the selected dialog
event is drained synchronously so its JASS button action can run without
waiting for a simulation frame.
The generic interactive window uses UI_WINDOW_MODAL | UI_WINDOW_UNIQUE |
UI_WINDOW_NO_PAUSE | UI_WINDOW_NO_ESCAPE; it captures gameplay input without
inventing a simulation pause. Text is resolved through G_LevelString and the
existing FDF UI layer.
Events and save/load
The event registration records a dialog ID and optional button ID. A click
queues the matching EVENT_DIALOG_CLICK and/or EVENT_DIALOG_BUTTON_CLICK
registration through the normal GAMEEVENT pipeline. JASSCONTEXT stores
both IDs and the clicking player's playerState, including across coroutine
suspension. The response survives game saves via the level event serializer,
JASS snapshots, and stable dialog/button handle indexes. The game save version
is 67, and the JASS snapshot version is 7; earlier layouts are rejected.
A choice is one-shot per DialogDisplay call. Re-display is allowed and does not
recreate the JASS handles. A new dialog shown to the same player replaces the
previous active choice and clears its authoritative visible bit.
Deliberate remaining compatibility work
DialogAddQuitButtoncreates an ordinary selectable button carrying the quit and score-screen metadata. The retail-specific quit/end-game transition and score-screen policy are not implemented here.- Button hotkeys are stored, but the generic
svc_windowkeyboard shortcut routing is not implemented. Mouse input is supported. - UI emits at most 12 buttons in a window; omitted choices are reported to stderr. Unusually large custom-map dialogs need scrolling/pagination and retail scaling research.
- ScriptDialog and ScriptDialogButton use generated bindings to stock FDF. The retail FDF has no variable-height choice rows, so row anchors and dialog height are generated from the runtime button count. Final visual alignment, actual clickable rendering, and exact retail dialog layout require in-game verification at 640×480 and widescreen resolutions.
- Save restores dialog identities and visibility.
ClientBeginon the post-load reconnect republishes the active choice once; manual save/reconnect testing is still required. - Nested simultaneous dialogs, priority and per-button hotkey collision rules need further retail verification.
The NightElfX06 fork requires both the global dialog event and specific-button event pathways. It must not use a map-specific event or client UI hook.
Regression tests
wc3_dialog.* covers creation, distinct handles, clear/destroy lifecycle,
JASS native calls, a choice event, correct GetClickedButton /
GetClickedDialog / GetTriggerPlayer context, registration selection, and
duplicate click rejection. It also verifies player-number identity when a
client slot differs from its Warcraft player number, verifies clear hides the
matching client's window, and checks that repeated serialization reclaims
temporary FDF frames. Further cases cover slot reuse across 100 clear/destroy
rounds, stale-ID rejection after reuse, malformed or foreign jassdialog
commands, clicked-dialog context across a save taken while the action sleeps,
and a single republish after load. The disabled-label
serialization path and save-version rejection have focused regressions.
make test passed with 46,715 assertions after the audit fixes. Retail visual
alignment and campaign integration still require user-side game verification.
Defeat difficulty reduction
CustomDefeatReduceDifficultyBJ is the authoritative stock JASS action in a
script-authored defeat choice dialog: Hard -> Normal, Normal -> Easy, no change
to default, unpause, and restart. The generic ScriptDialog implementation
continues to dispatch the authored action (including when the simulation is
paused) rather than copying it into the UI. For maps without a working authored
dialog, the native GameResultDialog fallback clones the stock Restart button
style to expose the localized GAMEOVER_REDUCE_DIFFICULTY action. Its command
checks single-player defeat and uses the existing deferred restart transition;
the action is hidden on Easy and Insane. See Difficulty.