Violet

Dialogs

On this page

core.dialog gives scripts read + click access to the game's active UI dialogs — the CDialog windows in the client's dialog list. It is the imperative companion to the on_dialog event: on_dialog tells you a window appeared (once), while core.dialog lets you enumerate what's open right now and act on it whenever you like.

NPC dialogs and blue boxes are best handled through on_dialog directly (see Core events); this page is about every other UI window.

Identifying a dialog

Dialogs are identified by their uol — the WZ resource node the window was built from, e.g. UI/UIWindow.img/KeyTypeSelect. It is version-agnostic (stable across game patches) and present for any dialog the client loads from a UOL. Raw pointers and vtable addresses are never exposed.

Match on uol rather than on screen position or button ids, which vary between dialogs. A dialog built without a UOL (see Box-style dialogs) has an empty uol — identify those by their buttons() / text() instead.

Window object

core.dialog.list(), core.dialog.get(), and the on_dialog event's dialog.window field all hand you a window handle. The handle exposes no raw pointer, and every method re-checks that the dialog is still open before touching it — a handle to a closed dialog simply reports invalid instead of dangling.

window:click(id: number) -> boolean

Presses the control with this id (invokes the dialog's OnButtonClicked). Returns false if the dialog has since closed. Pass a buttons() id, or a known control id for the dialog (see Box-style dialogs).


window:buttons() -> table | nil

Returns an array of the window's child controls as {id, text}, or nil if the dialog has closed. id is what you pass to click; text is the control's label (often empty — most MapleStory buttons are images with no text).


window:uol() -> string

The dialog's WZ resource node (e.g. UI/UIWindow.img/KeyTypeSelect), or "" if the dialog wasn't built from a UOL.


window:text() -> string

The joined text of the window's controls (labels, static text), or "".


window:valid() -> boolean

true while the dialog is still open. Every other method validates on its own, so this is only needed when you want to check without acting.

Functions

core.dialog.list() -> table<window>

Returns a window handle for every dialog currently open in the game's dialog list; empty when none are open. NPC dialogs and blue boxes may show up here too, but those are better handled through on_dialog.

lua
for _, w in ipairs(core.dialog.list()) do
    core.log("open dialog  uol=" .. w:uol())
end

core.dialog.get(uol: string) -> window | nil

Returns the first open dialog whose uol equals the given string, or nil if none match — a convenient way to act on a specific dialog from on_tick without walking the whole list.

lua
function on_tick()
    local w = core.dialog.get("UI/UIWindow.img/KeyTypeSelect")
    if w then
        w:click(w:buttons()[1].id)
    end
end

Box-style dialogs

A few dialogs — notably the class-intro story shown during character creation — don't expose their choices as child controls: the game renders the boxes itself and maps a click to OnButtonClicked(base + index), so buttons() has nothing to enumerate. Some of these (the story dialog included) are also built without a UOL, so their uol is empty too — find them via core.dialog.list(), identify them by their text(), then drive them by the known convention:

lua
for _, w in ipairs(core.dialog.list()) do
    if w:text():find("some story text") then
        w:click(1000)  -- box N is control id 1000 + N
    end
end

The base is not universal — the story dialog uses 1000; others use 2001, 1, and so on — so there is no generic "click box N". Pass the id the specific dialog expects.

Use from the game thread

Call core.dialog.* from on_tick / on_dialog (the game thread). The dialog list is live game state; reading it from a background context (for example an on_packet_recv handler running on the network thread) races with the game.

Tip

Prefer matching on uol and reading buttons() over hard-coding ids where you can — a dialog's control ids are stable within a version, but uol is the identity that carries across patches.