Last active
September 16, 2015 08:37
-
-
Save CelticMinstrel/008f4f26805537c40ac8 to your computer and use it in GitHub Desktop.
A Lua implementation of Wesnoth's [message] WML tag
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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