- Joined
- Mar 25, 2021
- Messages
- 18
Short snippet to display and handle dialogs more intuitively.
Features
Installation
Changelog:
(2021-04-12) Version 1.1.1 introduced OO approach and adjustments made for dialog buttons showing another dialog, example is in spoiler.
(2021-04-14) Small code adjustments and added comments using @Eikonium's formatting from his Set library.
Features
- Implement button-press handlers using Lua callbacks.
- Create dialogs that can be shown (or hidden) to all or a specific player.
- No dialog is actually created until you call the show()-method so you can define dialogs at any time you like and show them on demand.
Lua:
--[[
* DialogUtils 1.1.1 by Forsakn
*
* DialogUtils provides an easy way to predefine and show/hide native blizzard dialogs at will.
*
* API Functions:
**************************************************************
* The API only offers object-oriented notation. Colons are used after objects to access class-wide methods. Dots refer to attributes of the object itself, even when those are functions.
* ---------------
* | DialogUtils |
* ---------------
* - This class at the time only has one purpose, to create new instances of the DialogData class.
*
* DialogUtils.create(title)
* - Returns a new DialogData instance
* - Example:
* local dialog = DialogUtils.create("DialogUtils test")
* --------------
* | DialogData |
* --------------
* - This class holds on to dialog related data and provides functions for building/showing and hiding a native Blizzard dialog using that data.
* - No static methods here, all methods must be called on an instance of DialogData, created via the DialogUtils.create method.
*
* <DialogData>:addButton(text, callback, hotkeyId?)
* - Adds a button to <DialogData>, this does not actually create a blizzard DialogButton but only adds data to the object for later use.
* - The callback function has to provide one parameter, "player", this is the player who clicked the button.
* - Returning true in the callback function will make the dialog stay open on click, useful for invalid choices.
* - Example:
* <DialogData>:addButton("button", function(player) print(GetPlayerName(player) end)
*
* <DialogData>:show(player?)
* - Builds and shows a blizzard Dialog adding all buttons added to <DialogData>.
* - Internally the dialogs are indexed by player ID or -1 for a dialog thats shown to all players. Calling this function will destroy previous dialog at this index.
* - Example:
* <DialogData>:show() -- Shows the dialog to all players.
* <DialogData>:show(Player(0)) -- Show the dialog only to red player.
*
* <DialogData>:hide(player?)
* - Hides the dialog created from calling <DialogData>:show from provided player.
* - Example:
* <DialogData>:hide() -- Hides the dialog from all players.
* <DialogData>:hide(Player(0)) -- Hides the dialog from red player.
]]
do
local dialogs = {}
local dialogIdOccupier = {}
local triggers = {}
----------------
-- DialogData --
----------------
---@class dialogdata
local DialogData = setmetatable({}, {})
getmetatable(DialogData).__index = DialogData
-- Define members (mostly to help with editor intellisense, will be overwritten on creation of DialogData)
DialogData.title = ""
DialogData.buttons = {}
---@param text string
---@param callback? function
---@param hotkeyId? integer
function DialogData:addButton(text, callback, hotkeyId)
table.insert(self.buttons, {text = text, callback = callback, hotkeyId = hotkeyId or 0})
end
---@param player? player
function DialogData:show(player)
local dialogId = GetPlayerId(player) or -1
player = player or GetLocalPlayer()
-- Destroy old dialog and create new
if dialogs[dialogId] then
DialogDestroy(dialogs[dialogId])
end
dialogs[dialogId] = DialogCreate()
local dialog = dialogs[dialogId]
-- Destroy old trigger
if triggers[dialogId] then
DestroyTrigger(triggers[dialogId])
end
-- Create new trigger and add buttons to dialog
local t = CreateTrigger()
TriggerRegisterDialogEvent(t, dialog)
for i = 1, #self.buttons do
local button = self.buttons[i]
local dialogButton = DialogAddButton(dialog, button.text, button.hotkeyId)
TriggerAddCondition(t, Condition(function ()
if GetClickedButton() == dialogButton and button.callback then
local clickingPlayer = GetTriggerPlayer()
local keepAlive = button.callback(clickingPlayer) == true
DialogDisplay(clickingPlayer, dialog, keepAlive)
end
end))
end
triggers[dialogId] = t
-- Display dialog to player
DialogSetMessage(dialog, self.title)
DialogDisplay(player, dialog, true)
-- Set self as occupier of this dialog (Used for hiding)
dialogIdOccupier[dialogId] = self
end
---@param player? player
function DialogData:hide(player)
player = player or GetLocalPlayer()
for id, data in ipairs(dialogIdOccupier) do
if data == self then
local dialog = dialogs[id]
if dialog then
if GetLocalPlayer() == player then
DialogDisplay(player, dialog, false)
end
end
end
end
end
-----------------
-- DialogUtils --
-----------------
DialogUtils = {}
---@param title string
---@return dialogdata
function DialogUtils.create(title)
local dialogData = setmetatable({}, getmetatable(DialogData))
dialogData.title = title
dialogData.buttons = {}
return dialogData
end
end
This is the result of the nested example.
Lua:
-- Example display dialog to all players
TimerStart(CreateTimer(), 2, false , function ()
DestroyTimer(GetExpiredTimer())
local dialog = DialogUtils.create("DialogUtils test")
dialog:addButton("Yes", function (player)
print(string.format("\x25s clicked yes", GetPlayerName(player)))
end)
dialog:addButton("Other", function (player)
print(string.format("\x25s clicked other", GetPlayerName(player)))
end)
dialog:addButton("Keep alive", function (player)
print(string.format("\x25s clicked keep alive", GetPlayerName(player)))
return true -- Returning true keeps the dialog alive instead of closing it.
end)
dialog:addButton("Close")
dialog:show()
-- This won't work in singleplayer since dialogs pause the game
TimerStart(CreateTimer(), 5, false, function ()
DestroyTimer(GetExpiredTimer())
dialog:hide() -- Can hide dialog from specific player here if you wish.
end)
end)
-- Example display dialog to specific player
TimerStart(CreateTimer(), 2, false , function ()
DestroyTimer(GetExpiredTimer())
local player = Player(0)
local dialog = DialogUtils.create("DialogUtils test")
dialog:addButton("Yes", function (player)
print(string.format("\x25s clicked yes", GetPlayerName(player)))
end)
dialog:addButton("Other", function (player)
print(string.format("\x25s clicked other", GetPlayerName(player)))
end)
dialog:addButton("Keep alive", function (player)
print(string.format("\x25s clicked keep alive", GetPlayerName(player)))
return true -- Returning true keeps the dialog alive instead of closing it.
end)
dialog:addButton("Close")
dialog:show(player)
-- This won't work in singleplayer since dialogs pause the game
TimerStart(CreateTimer(), 5, false, function ()
DestroyTimer(GetExpiredTimer())
dialog:hide(player) -- Can skip supplying player here, but it's best to supply one since we have it available.
end)
end)
-- Example nested dialogs
TimerStart(CreateTimer(), 2, false , function ()
DestroyTimer(GetExpiredTimer())
local dialog = DialogUtils.create("DialogUtils test")
local dialog2 = DialogUtils.create("DialogUtils test 2")
dialog:addButton("Go to dialog 2", function (player)
dialog2:show(player)
end)
dialog:addButton("Close")
dialog2:addButton("Ok")
dialog2:addButton("Go back", function (player)
dialog:show(player)
end)
dialog:show()
end)
Installation
- Copy the code in the "DialogUtils.lua"-spoiler and paste it into a custom script in your trigger editor.
- Finished! To get started I do however recommend you to copy an example from the examples-spoiler and place it in a custom script below the one you created in step 1. Edit and test it!
Changelog:
(2021-04-12) Version 1.1.1 introduced OO approach and adjustments made for dialog buttons showing another dialog, example is in spoiler.
(2021-04-14) Small code adjustments and added comments using @Eikonium's formatting from his Set library.
Last edited: