Skip to content

Instantly share code, notes, and snippets.

@CelticMinstrel
Last active September 16, 2015 08:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save CelticMinstrel/008f4f26805537c40ac8 to your computer and use it in GitHub Desktop.
Save CelticMinstrel/008f4f26805537c40ac8 to your computer and use it in GitHub Desktop.
A Lua implementation of Wesnoth's [message] WML tag
local helper = wesnoth.require "lua/helper.lua"
local location_set = wesnoth.require "lua/location_set.lua"
local wml_actions = wesnoth.wml_actions
local function get_image(cfg, speaker)
local image = cfg.image or ""
if image == "none" then
image = ""
end
if image == "" and speaker ~= nil then
image = speaker.__cfg.profile
end
return image
end
local function get_caption(cfg, speaker)
local caption = cfg.caption
if not caption and speaker ~= nil then
caption = speaker.name or speaker.type_name
end
return caption
end
local function message_user_choice(msg_cfg)
return function()
local dlg_result, option_chosen, ti_content = wesnoth.show_message_dialog(msg_cfg)
if dlg_result == -1 then -- Cancelled (not quite sure of the right value)
wesnoth.context_skip_messages = true
end
local result_cfg = {}
if #options > 0 then
result_cfg.value = option_chosen
end
if ti_element ~= nil then
result_cfg.text = ti_content
end
return result_cfg
end
end
local function build_message_dialog(cfg, speaker, options, ti_element)
local image = get_image(cfg, speaker)
local caption = get_caption(cfg, speaker)
local left_side = true
-- If this doesn't work, might need tostring()
if string.find(image, "~RIGHT()") then
left_side = false
image = string.gsub(image, "~RIGHT()", "")
end
-- Parse input text, if not available all fields are empty
if ti_element ~= nil then
local ti_label = ti_element.label or ""
local ti_content = ti_element.text or ""
local input_max_size = tonumber(ti_element.max_length) or 256
if input_max_size > 1024 or input_max_size < 1 then
-- lg::wml_error << "Invalid maximum size for input " << input_max_size
input_max_size = 256
end
end
local msg_cfg = {
left_side = left_side,
title = caption,
message = cfg.message,
portrait = image,
}
if ti_element ~= nil then
table.insert(msg_cfg, {"text_input", {
label = ti_label,
text = ti_content,
max_length = input_max_size,
}})
end
for i,opt in ipairs(options) do
table.insert(msg_cfg, {"option", {value = opt}})
end
return msg_cfg
end
local function message_get_speaker(cfg)
local speaker
if cfg.speaker == "unit" then
speaker = wesnoth.get_unit(wesnoth.current.event_context.x1, wesnoth.current.event_context.x2)
elseif cfg.speaker == "second_unit" then
speaker = wesnoth.get_unit(wesnoth.current.event_context.x1, wesnoth.current.event_context.x2)
elseif cfg.speaker ~= "narrator" then
speaker = wesnoth.get_units(cfg)[1]
end
if speaker ~= nil then
-- LOG_NG << "Set speaker to '" << speaker->name() << "'"
wesnoth.select_hex(speaker.x, speaker.y)
if cfg["scroll"] then
-- LOG_DP << "Scrolling to speaker..."
local offset_from_center = math.max(0, speaker.y - 1)
wesnoth.scroll_to_tile(speaker.x, offset_from_center)
end
wesnoth.select_hex(speaker.x, speaker.y)
elseif cfg.speaker == "narrator" then
-- LOG_NG << "No speaker"
wesnoth.select_hex(-1000, -1000)
end
return speaker
end
function wml_actions.message(cfg)
-- Check if there is any input to be made, if not the message may be skipped
local has_ti = helper.get_child(cfg, "text_input") ~= nil
local has_input = has_ti or helper.get_child("text_input") ~= nil
if not has_input and (wesnoth.is_skipping_replay or wesnoth.context_skip_messages) then return end
local side_for_raw = cfg.side_for
if side_for then
local side_for_show = has_input
for side in string.gmatch(side_for_raw, '[^,]+') do
side = tonumber(side)
-- Make sanity check that side number is good
-- then check if this side is human controlled
if side > 0 and side < #wesnoth.sides and wesnoth.sides[side].controller == "human" then
side_for_show = true
break
end
end
if not side_for_show then
-- DBG_NG << "Player isn't controlling side which should get message"
return
end
end
local speaker = message_get_speaker(cfg)
-- screen.draw(false)
-- LOG_DP << "Done scrolling to speaker..."
if speaker == nil and cfg.speaker ~= "narrator" then
-- No matching unit found, so the dialog can't come up.
-- Continue onto the next message.
-- WRN_NG << "Cannot show message"
return
end
local options, option_events = {}, {}
for opt_cfg in helper.child_range(cfg, "option") do
local msg_str = opt_cfg.message
local condition = helper.get_child(cfg, "show_if")
if condition == nil or wesnoth.eval_conditional(condition) then
table.insert(options, msg_str)
table.insert(option_events, {})
for cmd in helper.child_range(opt_cfg, "command") do
table.insert(option_events[#option_events])
end
end
end
has_input = #options > 0 or has_ti
if not has_input and wesnoth.is_skipping_replay then
-- No input to get and the user is not interested either.
return
end
if cfg.sound then wesnoth.play_sound(cfg.sound) end
local ti_element
for ti_cfg in helper.child_range(cfg, "text_input") do
if ti_element ~= nil then
-- lg::wml_error << "Too many text_unput tags, only one accepted"
break
end
ti_element = ti_cfg
end
local option_chosen, ti_result
-- DBG_DP << "Showing dialog..."
local msg_cfg = build_message_dialog(cfg, speaker, options, ti_element)
if not has_input then
--[[ Always show the dialog if it has no input, whether we are
replaying or not ]]
message_user_choice(msg_cfg)
else
local choice = wesnoth.synchronize_choice(message_user_choice(msg_cfg))
option_chosen = tonumber(choice.value)
ti_result = choice.text
end
-- Implement the consequences of the choice
if has_ti then
local var_name = ti_element.variable or "input"
wesnoth.set_variable(var_name, ti_result)
end
if #options > 0 then
if option_chosen > #options then
--[[ replace::process_error("invalid choice (" .. option_chosen ..
") was specified, choice 1 to " .. #options ..
" was expected") ]]
return
end
for i,cmd in ipairs(option_events[option_chosen]) do
local action = handle_event_commands(cmd)
if action == "continue" then
helper.wml_error("[continue] encountered outside a loop scope")
elseif action == "break" then
return
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment