Merge pull request #492 from CelticMinstrel/lua-message-object
Port [message] and [object] ActionWML to Lua
This commit is contained in:
commit
e841252f19
15 changed files with 702 additions and 635 deletions
|
@ -30,6 +30,11 @@ Version 1.13.1+dev:
|
|||
* Added support for current.event_context.unit_x/y fields (bug #23507)
|
||||
* Added wesnoth.set_dialog_focus function
|
||||
* Added wesnoth.set_dialog_visible function
|
||||
* Added wesnoth.show_message_dialog function
|
||||
* Added wesnoth.show_popup_dialog function
|
||||
* Added wesnoth.deselect_hex function
|
||||
* Added wesnoth.is_skipping_messages and wesnoth.skip_messages functions
|
||||
* New parameter write_to_mods in wesnoth.add_modification
|
||||
* Added wesnoth.random function
|
||||
* helper.shuffle is now synced
|
||||
* Music and sound effects:
|
||||
|
@ -85,6 +90,7 @@ Version 1.13.1+dev:
|
|||
number of movement points the attack consumes
|
||||
* Ability to patch movetypes to account for custom terrains or damage types
|
||||
* Removed y offset by -1 from [message]'s scroll-to-unit logic.
|
||||
* Add [found_item] ConditionalWML to check if an [object]id= ActionWML has been taken
|
||||
* Editor:
|
||||
* Added Category field and color sliders to the Edit Label panel.
|
||||
* Miscellaneous and bug fixes:
|
||||
|
|
|
@ -13,78 +13,21 @@ end
|
|||
|
||||
wesnoth.require "lua/wml/objectives.lua"
|
||||
wesnoth.require "lua/wml/items.lua"
|
||||
wesnoth.require "lua/wml/message.lua"
|
||||
wesnoth.require "lua/wml/object.lua"
|
||||
|
||||
local helper = wesnoth.require "lua/helper.lua"
|
||||
local location_set = wesnoth.require "lua/location_set.lua"
|
||||
local utils = wesnoth.require "lua/wml-utils.lua"
|
||||
local wml_actions = wesnoth.wml_actions
|
||||
|
||||
local function trim(s)
|
||||
-- use (f(a)) to get first argument
|
||||
return (tostring(s):gsub("^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
|
||||
local function split(s)
|
||||
return tostring(s):gmatch("[^%s,][^,]*")
|
||||
end
|
||||
|
||||
local function vwriter_init(cfg, default_variable)
|
||||
local variable = cfg.variable or default_variable
|
||||
local is_explicit_index = string.sub(variable, string.len(variable)) == "]"
|
||||
local mode = cfg.mode or "always_clear"
|
||||
local index = 0
|
||||
if is_explicit_index then
|
||||
-- explicit indexes behave always like "replace"
|
||||
elseif mode == "append" then
|
||||
index = wesnoth.get_variable(variable .. ".length")
|
||||
elseif mode ~= "replace" then
|
||||
wesnoth.set_variable(variable)
|
||||
end
|
||||
return {
|
||||
variable = variable,
|
||||
is_explicit_index = is_explicit_index,
|
||||
index = index,
|
||||
}
|
||||
end
|
||||
|
||||
local function vwriter_write(self, container)
|
||||
if self.is_explicit_index then
|
||||
wesnoth.set_variable(self.variable, container)
|
||||
else
|
||||
wesnoth.set_variable(string.format("%s[%u]", self.variable, self.index), container)
|
||||
end
|
||||
self.index = self.index + 1
|
||||
end
|
||||
|
||||
local function optional_side_filter(cfg, key_name, filter_name)
|
||||
local key_name = key_name or "side"
|
||||
local sides = cfg[key_name]
|
||||
local filter_name = filter_name or "filter_side"
|
||||
local filter_side = helper.get_child(cfg, filter_name)
|
||||
if filter_side then
|
||||
sides = wesnoth.get_sides(filter_side)
|
||||
elseif sides then
|
||||
local dummy_cfg = {side=sides}
|
||||
sides = wesnoth.get_sides(dummy_cfg)
|
||||
else
|
||||
return true
|
||||
end
|
||||
for index,side in ipairs(sides) do
|
||||
if side.controller == "human" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local engine_message = wml_actions.message
|
||||
|
||||
function wml_actions.sync_variable(cfg)
|
||||
local names = cfg.name or helper.wml_error "[sync_variable] missing required name= attribute."
|
||||
local result = wesnoth.synchronize_choice(
|
||||
function()
|
||||
local res = {}
|
||||
for name_raw in split(names) do
|
||||
local name = trim(name_raw)
|
||||
for name_raw in utils.split(names) do
|
||||
local name = utils.trim(name_raw)
|
||||
local variable_type = string.sub(name, string.len(name)) == "]" and "indexed" or ( wesnoth.get_variable(name .. ".length") > 0 and "array" or "attribute")
|
||||
local variable_info = { name = name, type = variable_type }
|
||||
table.insert(res, { "variable", variable_info })
|
||||
|
@ -116,13 +59,6 @@ function wml_actions.sync_variable(cfg)
|
|||
end
|
||||
end
|
||||
|
||||
function wml_actions.message(cfg)
|
||||
local show_if = helper.get_child(cfg, "show_if")
|
||||
if not show_if or wesnoth.eval_conditional(show_if) then
|
||||
engine_message(cfg)
|
||||
end
|
||||
end
|
||||
|
||||
function wml_actions.chat(cfg)
|
||||
local side_list = wesnoth.get_sides(cfg)
|
||||
local speaker = tostring(cfg.speaker or "WML")
|
||||
|
@ -157,8 +93,8 @@ end
|
|||
function wml_actions.clear_variable(cfg)
|
||||
local names = cfg.name or
|
||||
helper.wml_error "[clear_variable] missing required name= attribute."
|
||||
for w in split(names) do
|
||||
wesnoth.set_variable(trim(w))
|
||||
for w in utils.split(names) do
|
||||
wesnoth.set_variable(utils.trim(w))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -175,11 +111,11 @@ end
|
|||
function wml_actions.store_unit_type(cfg)
|
||||
local types = cfg.type or
|
||||
helper.wml_error "[store_unit_type] missing required type= attribute."
|
||||
local writer = vwriter_init(cfg, "unit_type")
|
||||
for w in split(types) do
|
||||
local writer = utils.vwriter.init(cfg, "unit_type")
|
||||
for w in utils.split(types) do
|
||||
local unit_type = wesnoth.unit_types[w] or
|
||||
helper.wml_error(string.format("Attempt to store nonexistent unit type '%s'.", w))
|
||||
vwriter_write(writer, unit_type.__cfg)
|
||||
utils.vwriter.write(writer, unit_type.__cfg)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -205,7 +141,7 @@ function wml_actions.allow_recruit(cfg)
|
|||
local unit_types = cfg.type or helper.wml_error("[allow_recruit] missing required type= attribute")
|
||||
for index, team in ipairs(wesnoth.get_sides(cfg)) do
|
||||
local v = team.recruit
|
||||
for type in split(unit_types) do
|
||||
for type in utils.split(unit_types) do
|
||||
table.insert(v, type)
|
||||
wesnoth.add_known_unit(type)
|
||||
end
|
||||
|
@ -217,7 +153,7 @@ function wml_actions.allow_extra_recruit(cfg)
|
|||
local recruits = cfg.extra_recruit or helper.wml_error("[allow_extra_recruit] missing required extra_recruit= attribute")
|
||||
for index, unit in ipairs(wesnoth.get_units(cfg)) do
|
||||
local v = unit.extra_recruit
|
||||
for recruit in split(recruits) do
|
||||
for recruit in utils.split(recruits) do
|
||||
table.insert(v, recruit)
|
||||
wesnoth.add_known_unit(recruit)
|
||||
end
|
||||
|
@ -230,7 +166,7 @@ function wml_actions.disallow_recruit(cfg)
|
|||
for index, team in ipairs(wesnoth.get_sides(cfg)) do
|
||||
if unit_types then
|
||||
local v = team.recruit
|
||||
for w in split(unit_types) do
|
||||
for w in utils.split(unit_types) do
|
||||
for i, r in ipairs(v) do
|
||||
if r == w then
|
||||
table.remove(v, i)
|
||||
|
@ -249,7 +185,7 @@ function wml_actions.disallow_extra_recruit(cfg)
|
|||
local recruits = cfg.extra_recruit or helper.wml_error("[disallow_extra_recruit] missing required extra_recruit= attribute")
|
||||
for index, unit in ipairs(wesnoth.get_units(cfg)) do
|
||||
local v = unit.extra_recruit
|
||||
for w in split(recruits) do
|
||||
for w in utils.split(recruits) do
|
||||
for i, r in ipairs(v) do
|
||||
if r == w then
|
||||
table.remove(v, i)
|
||||
|
@ -265,7 +201,7 @@ function wml_actions.set_recruit(cfg)
|
|||
local recruit = cfg.recruit or helper.wml_error("[set_recruit] missing required recruit= attribute")
|
||||
for index, team in ipairs(wesnoth.get_sides(cfg)) do
|
||||
local v = {}
|
||||
for w in split(recruit) do
|
||||
for w in utils.split(recruit) do
|
||||
table.insert(v, w)
|
||||
end
|
||||
team.recruit = v
|
||||
|
@ -276,7 +212,7 @@ function wml_actions.set_extra_recruit(cfg)
|
|||
local recruits = cfg.extra_recruit or helper.wml_error("[set_extra_recruit] missing required extra_recruit= attribute")
|
||||
local v = {}
|
||||
|
||||
for w in split(recruits) do
|
||||
for w in utils.split(recruits) do
|
||||
table.insert(v, w)
|
||||
end
|
||||
|
||||
|
@ -300,7 +236,7 @@ function wml_actions.unit_worth(cfg)
|
|||
local hp = u.hitpoints / u.max_hitpoints
|
||||
local xp = u.experience / u.max_experience
|
||||
local best_adv = ut.cost
|
||||
for w in split(ut.__cfg.advances_to) do
|
||||
for w in utils.split(ut.__cfg.advances_to) do
|
||||
local uta = wesnoth.unit_types[w]
|
||||
if uta and uta.cost > best_adv then best_adv = uta.cost end
|
||||
end
|
||||
|
@ -339,53 +275,7 @@ function wml_actions.music(cfg)
|
|||
wesnoth.set_music(cfg)
|
||||
end
|
||||
|
||||
local function handle_event_commands(cfg)
|
||||
-- The WML might be modifying the currently executed WML by mixing
|
||||
-- [insert_tag] with [set_variables] and [clear_variable], so we
|
||||
-- have to be careful not to get confused by tags vanishing during
|
||||
-- the execution, hence the manual handling of [insert_tag].
|
||||
local cmds = helper.shallow_literal(cfg)
|
||||
for i = 1,#cmds do
|
||||
local v = cmds[i]
|
||||
local cmd = v[1]
|
||||
local arg = v[2]
|
||||
local insert_from
|
||||
if cmd == "insert_tag" then
|
||||
cmd = arg.name
|
||||
local from = arg.variable or
|
||||
helper.wml_error("[insert_tag] found with no variable= field")
|
||||
|
||||
arg = wesnoth.get_variable(from)
|
||||
if type(arg) ~= "table" then
|
||||
-- Corner case: A missing variable is replaced
|
||||
-- by an empty container rather than being ignored.
|
||||
arg = {}
|
||||
elseif string.sub(from, -1) ~= ']' then
|
||||
insert_from = from
|
||||
end
|
||||
arg = wesnoth.tovconfig(arg)
|
||||
end
|
||||
if not string.find(cmd, "^filter") then
|
||||
cmd = wml_actions[cmd] or
|
||||
helper.wml_error(string.format("[%s] not supported", cmd))
|
||||
if insert_from then
|
||||
local j = 0
|
||||
repeat
|
||||
cmd(arg)
|
||||
j = j + 1
|
||||
if j >= wesnoth.get_variable(insert_from .. ".length") then break end
|
||||
arg = wesnoth.tovconfig(wesnoth.get_variable(string.format("%s[%d]", insert_from, j)))
|
||||
until false
|
||||
else
|
||||
cmd(arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Apply music alterations once all the commands have been processed.
|
||||
wesnoth.set_music()
|
||||
end
|
||||
|
||||
wml_actions.command = handle_event_commands
|
||||
wml_actions.command = utils.handle_event_commands
|
||||
|
||||
-- since if and while are Lua keywords, we can't create functions with such names
|
||||
-- instead, we store the following anonymous functions directly into
|
||||
|
@ -398,7 +288,7 @@ wml_actions["if"] = function(cfg)
|
|||
|
||||
if wesnoth.eval_conditional(cfg) then -- evaluate [if] tag
|
||||
for then_child in helper.child_range(cfg, "then") do
|
||||
handle_event_commands(then_child)
|
||||
utils.handle_event_commands(then_child)
|
||||
end
|
||||
return -- stop after executing [then] tags
|
||||
end
|
||||
|
@ -406,7 +296,7 @@ wml_actions["if"] = function(cfg)
|
|||
for elseif_child in helper.child_range(cfg, "elseif") do
|
||||
if wesnoth.eval_conditional(elseif_child) then -- we'll evaluate the [elseif] tags one by one
|
||||
for then_tag in helper.child_range(elseif_child, "then") do
|
||||
handle_event_commands(then_tag)
|
||||
utils.handle_event_commands(then_tag)
|
||||
end
|
||||
return -- stop on first matched condition
|
||||
end
|
||||
|
@ -414,7 +304,7 @@ wml_actions["if"] = function(cfg)
|
|||
|
||||
-- no matched condition, try the [else] tags
|
||||
for else_child in helper.child_range(cfg, "else") do
|
||||
handle_event_commands(else_child)
|
||||
utils.handle_event_commands(else_child)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -423,7 +313,7 @@ wml_actions["while"] = function( cfg )
|
|||
for i = 1, 65536 do
|
||||
if wesnoth.eval_conditional( cfg ) then
|
||||
for do_child in helper.child_range( cfg, "do" ) do
|
||||
handle_event_commands( do_child )
|
||||
utils.handle_event_commands( do_child )
|
||||
end
|
||||
else return end
|
||||
end
|
||||
|
@ -435,9 +325,9 @@ function wml_actions.switch(cfg)
|
|||
|
||||
-- Execute all the [case]s where the value matches.
|
||||
for v in helper.child_range(cfg, "case") do
|
||||
for w in split(v.value) do
|
||||
for w in utils.split(v.value) do
|
||||
if w == tostring(var_value) then
|
||||
handle_event_commands(v)
|
||||
utils.handle_event_commands(v)
|
||||
found = true
|
||||
break
|
||||
end
|
||||
|
@ -447,7 +337,7 @@ function wml_actions.switch(cfg)
|
|||
-- Otherwise execute [else] statements.
|
||||
if not found then
|
||||
for v in helper.child_range(cfg, "else") do
|
||||
handle_event_commands(v)
|
||||
utils.handle_event_commands(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -455,14 +345,14 @@ end
|
|||
function wml_actions.scroll_to(cfg)
|
||||
local loc = wesnoth.get_locations( cfg )[1]
|
||||
if not loc then return end
|
||||
if not optional_side_filter(cfg) then return end
|
||||
if not utils.optional_side_filter(cfg) then return end
|
||||
wesnoth.scroll_to_tile(loc[1], loc[2], cfg.check_fogged, cfg.immediate)
|
||||
end
|
||||
|
||||
function wml_actions.scroll_to_unit(cfg)
|
||||
local u = wesnoth.get_units(cfg)[1]
|
||||
if not u then return end
|
||||
if not optional_side_filter(cfg, "for_side", "for_side") then return end
|
||||
if not utils.optional_side_filter(cfg, "for_side", "for_side") then return end
|
||||
wesnoth.scroll_to_tile(u.x, u.y, cfg.check_fogged, cfg.immediate)
|
||||
end
|
||||
|
||||
|
@ -484,7 +374,7 @@ function wml_actions.unit_overlay(cfg)
|
|||
local img = cfg.image or helper.wml_error( "[unit_overlay] missing required image= attribute" )
|
||||
for i,u in ipairs(wesnoth.get_units(cfg)) do
|
||||
local ucfg = u.__cfg
|
||||
for w in split(ucfg.overlays) do
|
||||
for w in utils.split(ucfg.overlays) do
|
||||
if w == img then ucfg = nil end
|
||||
end
|
||||
if ucfg then
|
||||
|
@ -497,43 +387,10 @@ end
|
|||
function wml_actions.remove_unit_overlay(cfg)
|
||||
local img = cfg.image or helper.wml_error( "[remove_unit_overlay] missing required image= attribute" )
|
||||
|
||||
-- Splits the string argument on commas, excepting those commas that occur
|
||||
-- within paired parentheses. The result is returned as a (non-empty) table.
|
||||
-- (The table might have a single entry that is an empty string, though.)
|
||||
-- Spaces around splitting commas are stripped (as in the C++ version).
|
||||
-- Empty strings are not removed (unlike the C++ version).
|
||||
local function parenthetical_split(str)
|
||||
local t = {""}
|
||||
-- To simplify some logic, end the string with paired parentheses.
|
||||
local formatted = (str or "") .. ",()"
|
||||
|
||||
-- Isolate paired parentheses.
|
||||
for prefix,paren in string.gmatch(formatted, "(.-)(%b())") do
|
||||
-- Separate on commas
|
||||
for comma,text in string.gmatch(prefix, "(,?)([^,]*)") do
|
||||
if comma == "" then
|
||||
-- We are continuing the last string found.
|
||||
t[#t] = t[#t] .. text
|
||||
else
|
||||
-- We are starting the next string.
|
||||
-- (Now that we know the last string is complete,
|
||||
-- strip leading and trailing spaces from it.)
|
||||
t[#t] = string.match(t[#t], "^%s*(.-)%s*$")
|
||||
table.insert(t, text)
|
||||
end
|
||||
end
|
||||
-- Add the parenthetical part to the last string found.
|
||||
t[#t] = t[#t] .. paren
|
||||
end
|
||||
-- Remove the empty parentheses we had added to the end.
|
||||
table.remove(t)
|
||||
return t
|
||||
end
|
||||
|
||||
-- Loop through all matching units.
|
||||
for i,u in ipairs(wesnoth.get_units(cfg)) do
|
||||
local ucfg = u.__cfg
|
||||
local t = parenthetical_split(ucfg.overlays)
|
||||
local t = utils.parenthetical_split(ucfg.overlays)
|
||||
-- Remove the specified image from the overlays.
|
||||
for i = #t,1,-1 do
|
||||
if t[i] == img then table.remove(t, i) end
|
||||
|
@ -558,10 +415,10 @@ function wml_actions.store_unit(cfg)
|
|||
local units = wesnoth.get_units(filter)
|
||||
local recall_units = wesnoth.get_recall_units(filter)
|
||||
|
||||
local writer = vwriter_init(cfg, "unit")
|
||||
local writer = utils.vwriter.init(cfg, "unit")
|
||||
|
||||
for i,u in ipairs(units) do
|
||||
vwriter_write(writer, u.__cfg)
|
||||
utils.vwriter.write(writer, u.__cfg)
|
||||
if kill_units then wesnoth.put_unit(u.x, u.y) end
|
||||
end
|
||||
|
||||
|
@ -570,7 +427,7 @@ function wml_actions.store_unit(cfg)
|
|||
local ucfg = u.__cfg
|
||||
ucfg.x = "recall"
|
||||
ucfg.y = "recall"
|
||||
vwriter_write(writer, ucfg)
|
||||
utils.vwriter.write(writer, ucfg)
|
||||
if kill_units then wesnoth.extract_unit(u) end
|
||||
end
|
||||
end
|
||||
|
@ -585,7 +442,7 @@ function wml_actions.store_locations(cfg)
|
|||
-- the variable can be mentioned in a [find_in] subtag, so it
|
||||
-- cannot be cleared before the locations are recovered
|
||||
local locs = wesnoth.get_locations(cfg)
|
||||
local writer = vwriter_init(cfg, "location")
|
||||
local writer = utils.vwriter.init(cfg, "location")
|
||||
for i, loc in ipairs(locs) do
|
||||
local x, y = loc[1], loc[2]
|
||||
local t = wesnoth.get_terrain(x, y)
|
||||
|
@ -593,7 +450,7 @@ function wml_actions.store_locations(cfg)
|
|||
if wesnoth.get_terrain_info(t).village then
|
||||
res.owner_side = wesnoth.get_village_owner(x, y) or 0
|
||||
end
|
||||
vwriter_write(writer, res)
|
||||
utils.vwriter.write(writer, res)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -657,23 +514,6 @@ function wml_actions.unhide_unit(cfg)
|
|||
wml_actions.redraw {}
|
||||
end
|
||||
|
||||
--note: when using these, make sure that nothing can throw over the call to end_var_scope
|
||||
local function start_var_scope(name)
|
||||
local var = helper.get_variable_array(name) --containers and arrays
|
||||
if #var == 0 then var = wesnoth.get_variable(name) end --scalars (and nil/empty)
|
||||
wesnoth.set_variable(name)
|
||||
return var
|
||||
end
|
||||
|
||||
local function end_var_scope(name, var)
|
||||
wesnoth.set_variable(name)
|
||||
if type(var) == "table" then
|
||||
helper.set_variable_array(name, var)
|
||||
else
|
||||
wesnoth.set_variable(name, var)
|
||||
end
|
||||
end
|
||||
|
||||
function wml_actions.modify_unit(cfg)
|
||||
local unit_variable = "LUA_modify_unit"
|
||||
|
||||
|
@ -736,11 +576,11 @@ function wml_actions.modify_unit(cfg)
|
|||
wml_actions.store_unit { {"filter", filter}, variable = unit_variable }
|
||||
local max_index = wesnoth.get_variable(unit_variable .. ".length") - 1
|
||||
|
||||
local this_unit = start_var_scope("this_unit")
|
||||
local this_unit = utils.start_var_scope("this_unit")
|
||||
for current_unit = 0, max_index do
|
||||
handle_unit(current_unit)
|
||||
end
|
||||
end_var_scope("this_unit", this_unit)
|
||||
utils.end_var_scope("this_unit", this_unit)
|
||||
|
||||
wesnoth.set_variable(unit_variable)
|
||||
end
|
||||
|
@ -891,7 +731,7 @@ function wml_actions.harm_unit(cfg)
|
|||
else return false end
|
||||
end
|
||||
|
||||
local this_unit = start_var_scope("this_unit")
|
||||
local this_unit = utils.start_var_scope("this_unit")
|
||||
|
||||
for index, unit_to_harm in ipairs(wesnoth.get_units(filter)) do
|
||||
if unit_to_harm.valid then
|
||||
|
@ -922,7 +762,7 @@ function wml_actions.harm_unit(cfg)
|
|||
wesnoth.scroll_to_tile(unit_to_harm.x, unit_to_harm.y, true)
|
||||
end
|
||||
|
||||
-- the two functions below are taken straight from the C++ engine, util.cpp and actions.cpp, with a few unuseful parts removed
|
||||
-- the two functions below are taken straight from the C++ engine, utils.cpp and actions.cpp, with a few unuseful parts removed
|
||||
-- may be moved in helper.lua in 1.11
|
||||
local function round_damage( base_damage, bonus, divisor )
|
||||
local rounding
|
||||
|
@ -1071,7 +911,7 @@ function wml_actions.harm_unit(cfg)
|
|||
end
|
||||
|
||||
wesnoth.set_variable ( "this_unit" ) -- clearing this_unit
|
||||
end_var_scope("this_unit", this_unit)
|
||||
utils.end_var_scope("this_unit", this_unit)
|
||||
end
|
||||
|
||||
function wml_actions.heal_unit(cfg)
|
||||
|
@ -1107,7 +947,7 @@ function wml_actions.transform_unit(cfg)
|
|||
end
|
||||
|
||||
function wml_actions.store_side(cfg)
|
||||
local writer = vwriter_init(cfg, "side")
|
||||
local writer = utils.vwriter.init(cfg, "side")
|
||||
for t, side_number in helper.get_sides(cfg) do
|
||||
local container = {
|
||||
controller = t.controller,
|
||||
|
@ -1128,7 +968,7 @@ function wml_actions.store_side(cfg)
|
|||
flag_icon = t.flag_icon,
|
||||
side = side_number
|
||||
}
|
||||
vwriter_write(writer, container)
|
||||
utils.vwriter.write(writer, container)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1189,7 +1029,7 @@ function wml_actions.find_path(cfg)
|
|||
local unit = wesnoth.get_units(filter_unit)[1] or helper.wml_error("[find_path]'s filter didn't match any unit")
|
||||
local filter_location = (helper.get_child(cfg, "destination")) or helper.wml_error( "[find_path] missing required [destination] tag" )
|
||||
-- support for $this_unit
|
||||
local this_unit = start_var_scope("this_unit")
|
||||
local this_unit = utils.start_var_scope("this_unit")
|
||||
|
||||
wesnoth.set_variable ( "this_unit" ) -- clearing this_unit
|
||||
wesnoth.set_variable("this_unit", unit.__cfg) -- cfg field needed
|
||||
|
@ -1249,14 +1089,14 @@ function wml_actions.find_path(cfg)
|
|||
wesnoth.set_variable ( string.format("%s", variable), { hexes = 0 } ) -- set only length, nil all other values
|
||||
-- support for $this_unit
|
||||
wesnoth.set_variable ( "this_unit" ) -- clearing this_unit
|
||||
end_var_scope("this_unit", this_unit)
|
||||
utils.end_var_scope("this_unit", this_unit)
|
||||
return end
|
||||
|
||||
if not allow_multiple_turns and turns > 1 then -- location cannot be reached in one turn
|
||||
wesnoth.set_variable ( string.format("%s", variable), { hexes = 0 } )
|
||||
-- support for $this_unit
|
||||
wesnoth.set_variable ( "this_unit" ) -- clearing this_unit
|
||||
end_var_scope("this_unit", this_unit)
|
||||
utils.end_var_scope("this_unit", this_unit)
|
||||
return end -- skip the cycles below
|
||||
|
||||
wesnoth.set_variable ( string.format( "%s", variable ), { hexes = current_distance, from_x = unit.x, from_y = unit.y, to_x = current_location[1], to_y = current_location[2], movement_cost = cost, required_turns = turns } )
|
||||
|
@ -1277,11 +1117,11 @@ function wml_actions.find_path(cfg)
|
|||
|
||||
-- support for $this_unit
|
||||
wesnoth.set_variable ( "this_unit" ) -- clearing this_unit
|
||||
end_var_scope("this_unit", this_unit)
|
||||
utils.end_var_scope("this_unit", this_unit)
|
||||
end
|
||||
|
||||
function wml_actions.store_starting_location(cfg)
|
||||
local writer = vwriter_init(cfg, "location")
|
||||
local writer = utils.vwriter.init(cfg, "location")
|
||||
for possibly_wrong_index, side in ipairs(wesnoth.get_sides(cfg)) do
|
||||
local loc = wesnoth.get_starting_location(side.side)
|
||||
if loc then
|
||||
|
@ -1290,16 +1130,16 @@ function wml_actions.store_starting_location(cfg)
|
|||
if wesnoth.get_terrain_info(terrain).village then
|
||||
result.owner_side = wesnoth.get_village_owner(loc[1], loc[2]) or 0
|
||||
end
|
||||
vwriter_write(writer, result)
|
||||
utils.vwriter.write(writer, result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function wml_actions.store_villages( cfg )
|
||||
local villages = wesnoth.get_villages( cfg )
|
||||
local writer = vwriter_init(cfg, "location")
|
||||
local writer = utils.vwriter.init(cfg, "location")
|
||||
for index, village in ipairs( villages ) do
|
||||
vwriter_write(writer, {
|
||||
utils.vwriter.write(writer, {
|
||||
x = village[1],
|
||||
y = village[2],
|
||||
terrain = wesnoth.get_terrain( village[1], village[2] ),
|
||||
|
@ -1417,7 +1257,7 @@ end
|
|||
function wml_actions.remove_event(cfg)
|
||||
local id = cfg.id or helper.wml_error("[remove_event] missing required id= key")
|
||||
|
||||
for w in split(id) do
|
||||
for w in utils.split(id) do
|
||||
wesnoth.remove_event_handler(w)
|
||||
end
|
||||
end
|
||||
|
@ -1468,8 +1308,8 @@ function wml_actions.role(cfg)
|
|||
local filter = helper.shallow_literal(cfg)
|
||||
|
||||
local types = {}
|
||||
for value in split(cfg.type) do
|
||||
table.insert(types, trim(value))
|
||||
for value in utils.split(cfg.type) do
|
||||
table.insert(types, utils.trim(value))
|
||||
end
|
||||
|
||||
filter.role, filter.type = nil, nil
|
||||
|
|
170
data/lua/wml-utils.lua
Normal file
170
data/lua/wml-utils.lua
Normal file
|
@ -0,0 +1,170 @@
|
|||
|
||||
local helper = wesnoth.require "lua/helper.lua"
|
||||
local utils = {vwriter = {}}
|
||||
|
||||
function utils.trim(s)
|
||||
-- use (f(a)) to get first argument
|
||||
return (tostring(s):gsub("^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
|
||||
function utils.split(s)
|
||||
return tostring(s):gmatch("[^%s,][^,]*")
|
||||
end
|
||||
|
||||
function utils.check_key(val, key, tag, convert_spaces)
|
||||
if not val then return nil end
|
||||
if convert_spaces then
|
||||
val = tostring(val):gsub(' ', '_')
|
||||
end
|
||||
if not val:match('^[a-zA-Z0-9_]+$') then
|
||||
helper.wml_error("Invalid " .. key .. "= in [" .. tag .. "]")
|
||||
end
|
||||
return val
|
||||
end
|
||||
|
||||
function utils.vwriter.init(cfg, default_variable)
|
||||
local variable = cfg.variable or default_variable
|
||||
local is_explicit_index = string.sub(variable, string.len(variable)) == "]"
|
||||
local mode = cfg.mode or "always_clear"
|
||||
local index = 0
|
||||
if is_explicit_index then
|
||||
-- explicit indexes behave always like "replace"
|
||||
elseif mode == "append" then
|
||||
index = wesnoth.get_variable(variable .. ".length")
|
||||
elseif mode ~= "replace" then
|
||||
wesnoth.set_variable(variable)
|
||||
end
|
||||
return {
|
||||
variable = variable,
|
||||
is_explicit_index = is_explicit_index,
|
||||
index = index,
|
||||
}
|
||||
end
|
||||
|
||||
function utils.vwriter.write(self, container)
|
||||
if self.is_explicit_index then
|
||||
wesnoth.set_variable(self.variable, container)
|
||||
else
|
||||
wesnoth.set_variable(string.format("%s[%u]", self.variable, self.index), container)
|
||||
end
|
||||
self.index = self.index + 1
|
||||
end
|
||||
|
||||
function utils.optional_side_filter(cfg, key_name, filter_name)
|
||||
local key_name = key_name or "side"
|
||||
local sides = cfg[key_name]
|
||||
local filter_name = filter_name or "filter_side"
|
||||
local filter_side = helper.get_child(cfg, filter_name)
|
||||
if filter_side then
|
||||
sides = wesnoth.get_sides(filter_side)
|
||||
elseif sides then
|
||||
local dummy_cfg = {side=sides}
|
||||
sides = wesnoth.get_sides(dummy_cfg)
|
||||
else
|
||||
return true
|
||||
end
|
||||
for index,side in ipairs(sides) do
|
||||
if side.controller == "human" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function utils.handle_event_commands(cfg)
|
||||
-- The WML might be modifying the currently executed WML by mixing
|
||||
-- [insert_tag] with [set_variables] and [clear_variable], so we
|
||||
-- have to be careful not to get confused by tags vanishing during
|
||||
-- the execution, hence the manual handling of [insert_tag].
|
||||
local cmds = helper.shallow_literal(cfg)
|
||||
for i = 1,#cmds do
|
||||
local v = cmds[i]
|
||||
local cmd = v[1]
|
||||
local arg = v[2]
|
||||
local insert_from
|
||||
if cmd == "insert_tag" then
|
||||
cmd = arg.name
|
||||
local from = arg.variable or
|
||||
helper.wml_error("[insert_tag] found with no variable= field")
|
||||
|
||||
arg = wesnoth.get_variable(from)
|
||||
if type(arg) ~= "table" then
|
||||
-- Corner case: A missing variable is replaced
|
||||
-- by an empty container rather than being ignored.
|
||||
arg = {}
|
||||
elseif string.sub(from, -1) ~= ']' then
|
||||
insert_from = from
|
||||
end
|
||||
arg = wesnoth.tovconfig(arg)
|
||||
end
|
||||
if not string.find(cmd, "^filter") then
|
||||
cmd = wesnoth.wml_actions[cmd] or
|
||||
helper.wml_error(string.format("[%s] not supported", cmd))
|
||||
if insert_from then
|
||||
local j = 0
|
||||
repeat
|
||||
cmd(arg)
|
||||
j = j + 1
|
||||
if j >= wesnoth.get_variable(insert_from .. ".length") then break end
|
||||
arg = wesnoth.tovconfig(wesnoth.get_variable(string.format("%s[%d]", insert_from, j)))
|
||||
until false
|
||||
else
|
||||
cmd(arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Apply music alterations once all the commands have been processed.
|
||||
wesnoth.set_music()
|
||||
end
|
||||
|
||||
-- Splits the string argument on commas, excepting those commas that occur
|
||||
-- within paired parentheses. The result is returned as a (non-empty) table.
|
||||
-- (The table might have a single entry that is an empty string, though.)
|
||||
-- Spaces around splitting commas are stripped (as in the C++ version).
|
||||
-- Empty strings are not removed (unlike the C++ version).
|
||||
function utils.parenthetical_split(str)
|
||||
local t = {""}
|
||||
-- To simplify some logic, end the string with paired parentheses.
|
||||
local formatted = (str or "") .. ",()"
|
||||
|
||||
-- Isolate paired parentheses.
|
||||
for prefix,paren in string.gmatch(formatted, "(.-)(%b())") do
|
||||
-- Separate on commas
|
||||
for comma,text in string.gmatch(prefix, "(,?)([^,]*)") do
|
||||
if comma == "" then
|
||||
-- We are continuing the last string found.
|
||||
t[#t] = t[#t] .. text
|
||||
else
|
||||
-- We are starting the next string.
|
||||
-- (Now that we know the last string is complete,
|
||||
-- strip leading and trailing spaces from it.)
|
||||
t[#t] = string.match(t[#t], "^%s*(.-)%s*$")
|
||||
table.insert(t, text)
|
||||
end
|
||||
end
|
||||
-- Add the parenthetical part to the last string found.
|
||||
t[#t] = t[#t] .. paren
|
||||
end
|
||||
-- Remove the empty parentheses we had added to the end.
|
||||
table.remove(t)
|
||||
return t
|
||||
end
|
||||
|
||||
--note: when using these, make sure that nothing can throw over the call to end_var_scope
|
||||
function utils.start_var_scope(name)
|
||||
local var = helper.get_variable_array(name) --containers and arrays
|
||||
if #var == 0 then var = wesnoth.get_variable(name) end --scalars (and nil/empty)
|
||||
wesnoth.set_variable(name)
|
||||
return var
|
||||
end
|
||||
|
||||
function utils.end_var_scope(name, var)
|
||||
wesnoth.set_variable(name)
|
||||
if type(var) == "table" then
|
||||
helper.set_variable_array(name, var)
|
||||
else
|
||||
wesnoth.set_variable(name, var)
|
||||
end
|
||||
end
|
||||
|
||||
return utils
|
221
data/lua/wml/message.lua
Normal file
221
data/lua/wml/message.lua
Normal file
|
@ -0,0 +1,221 @@
|
|||
|
||||
local helper = wesnoth.require "lua/helper.lua"
|
||||
local utils = wesnoth.require "lua/wml-utils.lua"
|
||||
local location_set = wesnoth.require "lua/location_set.lua"
|
||||
|
||||
local function log(msg, level)
|
||||
wesnoth.wml_actions.wml_message({
|
||||
message = msg,
|
||||
logger = level,
|
||||
})
|
||||
end
|
||||
|
||||
local function get_image(cfg, speaker)
|
||||
local image = cfg.image
|
||||
|
||||
if speaker and image == nil then
|
||||
image = speaker.__cfg.profile
|
||||
end
|
||||
|
||||
if image == "none" or image == nil then
|
||||
return ""
|
||||
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 get_speaker(cfg)
|
||||
local speaker
|
||||
local context = wesnoth.current.event_context
|
||||
|
||||
if cfg.speaker == "narrator" then
|
||||
speaker = "narrator"
|
||||
elseif cfg.speaker == "unit" then
|
||||
speaker = wesnoth.get_unit(context.x1, context.y1)
|
||||
elseif cfg.speaker == "second_unit" then
|
||||
speaker = wesnoth.get_unit(context.x2, context.y2)
|
||||
else
|
||||
speaker = wesnoth.get_units(cfg)[1]
|
||||
end
|
||||
|
||||
return speaker
|
||||
end
|
||||
|
||||
local function message_user_choice(cfg, speaker, options, text_input)
|
||||
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 image:find("~RIGHT()") then
|
||||
left_side = false
|
||||
-- The percent signs escape the parentheses for a literal match
|
||||
image = image:gsub("~RIGHT%(%)", "")
|
||||
end
|
||||
|
||||
local msg_cfg = {
|
||||
left_side = left_side,
|
||||
title = caption,
|
||||
message = cfg.message,
|
||||
portrait = image,
|
||||
}
|
||||
|
||||
-- Parse input text, if not available all fields are empty
|
||||
if text_input then
|
||||
local input_max_size = tonumber(text_input.max_length) or 256
|
||||
if input_max_size > 1024 or input_max_size < 1 then
|
||||
log("Invalid maximum size for input " .. input_max_size, "warning")
|
||||
input_max_size = 256
|
||||
end
|
||||
|
||||
-- This roundabout method is because text_input starts out
|
||||
-- as an immutable userdata value
|
||||
text_input = {
|
||||
label = text_input.label or "",
|
||||
text = text_input.text or "",
|
||||
max_length = input_max_size,
|
||||
}
|
||||
end
|
||||
|
||||
return function()
|
||||
local option_chosen, ti_content = wesnoth.show_message_dialog(msg_cfg, options, text_input)
|
||||
|
||||
if option_chosen == -2 then -- Pressed Escape (only if no input)
|
||||
wesnoth.skip_messages()
|
||||
end
|
||||
|
||||
local result_cfg = {}
|
||||
|
||||
if #options > 0 then
|
||||
result_cfg.value = option_chosen
|
||||
end
|
||||
|
||||
if text_input ~= nil then
|
||||
result_cfg.text = ti_content
|
||||
end
|
||||
|
||||
return result_cfg
|
||||
end
|
||||
end
|
||||
|
||||
function wesnoth.wml_actions.message(cfg)
|
||||
local show_if = helper.get_child(cfg, "show_if") or {}
|
||||
if not wesnoth.eval_conditional(show_if) then
|
||||
log("[message] skipped because [show_if] did not pass", "debug")
|
||||
return
|
||||
end
|
||||
|
||||
-- Only the first text_input tag is considered
|
||||
local text_input
|
||||
for cfg in helper.child_range(cfg, "text_input") do
|
||||
if text_input ~= nil then
|
||||
log("Too many [text_input] tags, only one accepted", "warning")
|
||||
break
|
||||
end
|
||||
text_input = cfg
|
||||
end
|
||||
|
||||
local options, option_events = {}, {}
|
||||
for option in helper.child_range(cfg, "option") do
|
||||
local condition = helper.get_child(cfg, "show_if") or {}
|
||||
|
||||
if wesnoth.eval_conditional(condition) then
|
||||
table.insert(options, option.message)
|
||||
table.insert(option_events, {})
|
||||
|
||||
for cmd in helper.child_range(option, "command") do
|
||||
table.insert(option_events[#option_events], cmd)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if there is any input to be made, if not the message may be skipped
|
||||
local has_input = text_input ~= nil or #options > 0
|
||||
|
||||
if not has_input and wesnoth.is_skipping_messages() then
|
||||
-- No input to get and the user is not interested either
|
||||
log("Skipping [message] because user not interested", "debug")
|
||||
return
|
||||
end
|
||||
|
||||
local sides_for = cfg.side_for
|
||||
if sides_for and not has_input then
|
||||
local show_for_side = false
|
||||
|
||||
-- Sanity checks on side number and controller
|
||||
for side in utils.split(sides_for) do
|
||||
side = tonumber(side)
|
||||
if side > 0 and side < #wesnoth.sides and wesnoth.sides[side].controller == "human" then
|
||||
show_for_side = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not show_for_side then
|
||||
-- Player isn't controlling side which should see the message
|
||||
log("Player isn't controlling side that should see [message]", "debug")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local speaker = get_speaker(cfg)
|
||||
if not speaker then
|
||||
-- No matching unit found, continue onto the next message
|
||||
log("No speaker found for [message]", "debug")
|
||||
return
|
||||
elseif speaker == "narrator" then
|
||||
-- Narrator, so deselect units
|
||||
wesnoth.deselect_hex()
|
||||
-- The speaker is expected to be either nil or a unit later
|
||||
speaker = nil
|
||||
else
|
||||
-- Check ~= false, because the default if omitted should be true
|
||||
if cfg.scroll ~= false then
|
||||
wesnoth.scroll_to_tile(speaker.x, speaker.y)
|
||||
end
|
||||
|
||||
wesnoth.select_hex(speaker.x, speaker.y, false)
|
||||
end
|
||||
|
||||
if cfg.sound then wesnoth.play_sound(cfg.sound) end
|
||||
|
||||
local msg_dlg = message_user_choice(cfg, speaker, options, text_input)
|
||||
|
||||
local option_chosen
|
||||
if not has_input then
|
||||
-- Always show the dialog if it has no input, whether we are replaying or not
|
||||
msg_dlg()
|
||||
else
|
||||
local choice = wesnoth.synchronize_choice(msg_dlg)
|
||||
|
||||
option_chosen = tonumber(choice.value)
|
||||
|
||||
if text_input ~= nil then
|
||||
-- Implement the consequences of the choice
|
||||
wesnoth.set_variable(text_input.variable or "input", choice.text)
|
||||
end
|
||||
end
|
||||
|
||||
if #options > 0 then
|
||||
if option_chosen > #options then
|
||||
log("invalid choice (" .. option_chosen .. ") was specified, choice 1 to " ..
|
||||
#options .. " was expected", "debug")
|
||||
return
|
||||
end
|
||||
|
||||
for i, cmd in ipairs(option_events[option_chosen]) do
|
||||
utils.handle_event_commands(cmd)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
85
data/lua/wml/object.lua
Normal file
85
data/lua/wml/object.lua
Normal file
|
@ -0,0 +1,85 @@
|
|||
|
||||
local helper = wesnoth.require "lua/helper.lua"
|
||||
local utils = wesnoth.require "lua/wml-utils.lua"
|
||||
local T = helper.set_wml_tag_metatable {}
|
||||
local wml_actions = wesnoth.wml_actions
|
||||
|
||||
local used_items = {}
|
||||
|
||||
function wml_actions.object(cfg)
|
||||
local context = wesnoth.current.event_context
|
||||
|
||||
-- If this item has already been used
|
||||
local obj_id = utils.check_key(cfg.id, "id", "object", true)
|
||||
if obj_id and used_items[obj_id] then return end
|
||||
|
||||
local unit
|
||||
local filter = helper.get_child(cfg, "filter")
|
||||
if filter then
|
||||
unit = wesnoth.get_units(filter)[1]
|
||||
end
|
||||
if not unit then
|
||||
unit = wesnoth.get_unit(contxt.x, context.y)
|
||||
end
|
||||
|
||||
local command_type, text
|
||||
if unit then
|
||||
text = tostring(cfg.description or "")
|
||||
command_type = "then"
|
||||
|
||||
local dvs = cfg.delayed_variable_substitution
|
||||
local add = cfg.no_write ~= true
|
||||
if dvs then
|
||||
wesnoth.add_modification(unit, "object", helper.literal(cfg), add)
|
||||
else
|
||||
wesnoth.add_modification(unit, "object", helper.parsed(cfg), add)
|
||||
end
|
||||
|
||||
wesnoth.select_hex(unit.x, unit.y)
|
||||
|
||||
-- Mark this item as used up
|
||||
if obj_id then used_items[obj_id] = true end
|
||||
else
|
||||
text = tostring(cfg.cannot_use_message or "")
|
||||
command_type = "else"
|
||||
end
|
||||
|
||||
-- Default to silent if object has no description
|
||||
local silent = cfg.silent
|
||||
if silent == nil then silent = (text:len() == 0) end
|
||||
|
||||
if not silent then
|
||||
wml_actions.redraw{}
|
||||
local name = tostring(cfg.name or "")
|
||||
wesnoth.show_popup_dialog(name, text, cfg.image)
|
||||
end
|
||||
|
||||
for cmd in helper.child_range(cfg, command_type) do
|
||||
utils.handle_event_commands(cmd)
|
||||
end
|
||||
end
|
||||
|
||||
local old_on_load = wesnoth.game_events.on_load
|
||||
function wesnoth.game_events.on_load(cfg)
|
||||
for i = 1,#cfg do
|
||||
if cfg[i][1] == "used_items" then
|
||||
-- Not quite sure if this will work
|
||||
-- Might need to loop through and copy each ID separately
|
||||
used_items = cfg[i][2]
|
||||
table.remove(cfg, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
old_on_load(cfg)
|
||||
end
|
||||
|
||||
local old_on_save = wesnoth.game_events.on_save
|
||||
function wesnoth.game_events.on_save()
|
||||
local cfg = old_on_save()
|
||||
table.insert(cfg, T.used_items(used_items) )
|
||||
return cfg
|
||||
end
|
||||
|
||||
function wesnoth.wml_conditionals.found_item(cfg)
|
||||
return used_items[utils.check_key(cfg.id, "id", "found_item", true)]
|
||||
end
|
|
@ -96,79 +96,6 @@ namespace game_events
|
|||
// (So keep it at the rop of this file?)
|
||||
wml_action::map wml_action::registry_;
|
||||
|
||||
|
||||
namespace { // advance declarations
|
||||
std::string get_caption(const vconfig& cfg, unit_map::iterator speaker);
|
||||
std::string get_image(const vconfig& cfg, unit_map::iterator speaker);
|
||||
}
|
||||
|
||||
namespace { // Types
|
||||
struct message_user_choice : mp_sync::user_choice
|
||||
{
|
||||
vconfig cfg;
|
||||
unit_map::iterator speaker;
|
||||
vconfig text_input_element;
|
||||
bool has_text_input;
|
||||
const std::vector<std::string> &options;
|
||||
|
||||
message_user_choice(const vconfig &c, const unit_map::iterator &s,
|
||||
const vconfig &t, bool ht, const std::vector<std::string> &o)
|
||||
: cfg(c), speaker(s), text_input_element(t)
|
||||
, has_text_input(ht), options(o)
|
||||
{}
|
||||
|
||||
virtual config query_user(int /*side*/) const
|
||||
{
|
||||
std::string image = get_image(cfg, speaker);
|
||||
std::string caption = get_caption(cfg, speaker);
|
||||
|
||||
size_t right_offset = image.find("~RIGHT()");
|
||||
bool left_side = right_offset == std::string::npos;
|
||||
if (!left_side) {
|
||||
image.erase(right_offset);
|
||||
}
|
||||
|
||||
// Parse input text, if not available all fields are empty
|
||||
std::string text_input_label = text_input_element["label"];
|
||||
std::string text_input_content = text_input_element["text"];
|
||||
unsigned input_max_size = text_input_element["max_length"].to_int(256);
|
||||
if (input_max_size > 1024 || input_max_size < 1) {
|
||||
lg::wml_error << "invalid maximum size for input "
|
||||
<< input_max_size << '\n';
|
||||
input_max_size = 256;
|
||||
}
|
||||
|
||||
int option_chosen = -1;
|
||||
int dlg_result = gui2::show_wml_message(left_side,
|
||||
resources::screen->video(), caption, cfg["message"],
|
||||
image, false, has_text_input, text_input_label,
|
||||
&text_input_content, input_max_size, options,
|
||||
&option_chosen);
|
||||
|
||||
/* Since gui2::show_wml_message needs to do undrawing the
|
||||
chatlines can get garbled and look dirty on screen. Force a
|
||||
redraw to fix it. */
|
||||
/** @todo This hack can be removed once gui2 is finished. */
|
||||
resources::screen->invalidate_all();
|
||||
resources::screen->draw(true,true);
|
||||
|
||||
if (dlg_result == gui2::twindow::CANCEL) {
|
||||
resources::game_events->pump().context_skip_messages(true);
|
||||
}
|
||||
|
||||
config cfg;
|
||||
if (!options.empty()) cfg["value"] = option_chosen;
|
||||
if (has_text_input) cfg["text"] = text_input_content;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
virtual config random_choice(int /*side*/) const
|
||||
{
|
||||
return config();
|
||||
}
|
||||
};
|
||||
} // end anonymous namespace (types)
|
||||
|
||||
namespace { // Support functions
|
||||
|
||||
/**
|
||||
|
@ -277,103 +204,6 @@ namespace { // Support functions
|
|||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to handle the caption part of [message].
|
||||
*
|
||||
* @param cfg cfg of message.
|
||||
* @param speaker The speaker of the message.
|
||||
*
|
||||
* @returns The caption to show.
|
||||
*/
|
||||
std::string get_caption(const vconfig& cfg, unit_map::iterator speaker)
|
||||
{
|
||||
std::string caption = cfg["caption"];
|
||||
if (caption.empty() && speaker != resources::units->end()) {
|
||||
caption = speaker->name();
|
||||
if(caption.empty()) {
|
||||
caption = speaker->type_name();
|
||||
}
|
||||
}
|
||||
return caption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to handle the image part of [message].
|
||||
*
|
||||
* @param cfg cfg of message.
|
||||
* @param speaker The speaker of the message.
|
||||
*
|
||||
* @returns The image to show.
|
||||
*/
|
||||
std::string get_image(const vconfig& cfg, unit_map::iterator speaker)
|
||||
{
|
||||
std::string image = cfg["image"];
|
||||
|
||||
if (image == "none") {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (image.empty() && speaker != resources::units->end())
|
||||
{
|
||||
image = speaker->big_profile();
|
||||
#ifndef LOW_MEM
|
||||
if(image == speaker->absolute_image()) {
|
||||
image += speaker->image_mods();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to handle the speaker part of [message].
|
||||
*
|
||||
* @param event_info event_info of message.
|
||||
* @param cfg cfg of message.
|
||||
*
|
||||
* @returns The unit who's the speaker or units->end().
|
||||
*/
|
||||
unit_map::iterator handle_speaker(const queued_event& event_info,
|
||||
const vconfig& cfg, bool scroll)
|
||||
{
|
||||
unit_map *units = resources::units;
|
||||
game_display &screen = *resources::screen;
|
||||
|
||||
unit_map::iterator speaker = units->end();
|
||||
const std::string speaker_str = cfg["speaker"];
|
||||
|
||||
if(speaker_str == "unit") {
|
||||
speaker = units->find(event_info.loc1);
|
||||
} else if(speaker_str == "second_unit") {
|
||||
speaker = units->find(event_info.loc2);
|
||||
} else if(speaker_str != "narrator") {
|
||||
const unit_filter ufilt(cfg, resources::filter_con);
|
||||
for(speaker = units->begin(); speaker != units->end(); ++speaker){
|
||||
if ( ufilt(*speaker) )
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(speaker != units->end()) {
|
||||
LOG_NG << "set speaker to '" << speaker->name() << "'\n";
|
||||
const map_location &spl = speaker->get_location();
|
||||
screen.highlight_hex(spl);
|
||||
if(scroll) {
|
||||
LOG_DP << "scrolling to speaker..\n";
|
||||
screen.scroll_to_tile(spl);
|
||||
}
|
||||
screen.highlight_hex(spl);
|
||||
} else if(speaker_str == "narrator") {
|
||||
LOG_NG << "no speaker\n";
|
||||
screen.highlight_hex(map_location::null_location());
|
||||
} else {
|
||||
return speaker;
|
||||
}
|
||||
|
||||
screen.draw(false);
|
||||
LOG_DP << "done scrolling to speaker...\n";
|
||||
return speaker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the lifting and resetting of fog via WML.
|
||||
* Keeping affect_normal_fog as false causes only the fog override to be affected.
|
||||
|
@ -418,11 +248,6 @@ namespace { // Support functions
|
|||
resources::screen->recalculate_minimap();
|
||||
resources::screen->invalidate_all();
|
||||
}
|
||||
|
||||
void handle_event_commands(const queued_event& event_info, const vconfig &cfg) {
|
||||
assert(resources::lua_kernel);
|
||||
resources::lua_kernel->run_wml_action("command", cfg, event_info);
|
||||
}
|
||||
} // end anonymous namespace (support functions)
|
||||
|
||||
void handle_deprecated_message(const config& cfg)
|
||||
|
@ -562,141 +387,6 @@ WML_HANDLER_FUNCTION(lift_fog, /*event_info*/, cfg)
|
|||
toggle_fog(true, cfg, !cfg["multiturn"].to_bool(false));
|
||||
}
|
||||
|
||||
/// Display a message dialog
|
||||
WML_HANDLER_FUNCTION(message, event_info, cfg)
|
||||
{
|
||||
// Check if there is any input to be made, if not the message may be skipped
|
||||
const vconfig::child_list menu_items = cfg.get_children("option");
|
||||
|
||||
const vconfig::child_list text_input_elements = cfg.get_children("text_input");
|
||||
const bool has_text_input = (text_input_elements.size() == 1);
|
||||
|
||||
bool has_input= (has_text_input || !menu_items.empty() );
|
||||
|
||||
// skip messages during quick replay
|
||||
play_controller *controller = resources::controller;
|
||||
if(!has_input && (
|
||||
controller->is_skipping_replay() ||
|
||||
resources::game_events->pump().context_skip_messages()
|
||||
))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this message is for this side
|
||||
// handeling of side_for for messages with input is done below in the get_user_choice call
|
||||
std::string side_for_raw = cfg["side_for"];
|
||||
if (!side_for_raw.empty() && !has_input)
|
||||
{
|
||||
bool side_for_show = false;
|
||||
|
||||
std::vector<std::string> side_for =
|
||||
utils::split(side_for_raw, ',', utils::STRIP_SPACES | utils::REMOVE_EMPTY);
|
||||
std::vector<std::string>::iterator itSide;
|
||||
size_t side;
|
||||
|
||||
// Check if any of side numbers are human controlled
|
||||
for (itSide = side_for.begin(); itSide != side_for.end(); ++itSide)
|
||||
{
|
||||
side = lexical_cast_default<size_t>(*itSide);
|
||||
// Make sanity check that side number is good
|
||||
// then check if this side is human controlled.
|
||||
if (side > 0 && side <= resources::teams->size() &&
|
||||
(*resources::teams)[side-1].is_local_human())
|
||||
{
|
||||
side_for_show = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!side_for_show)
|
||||
{
|
||||
DBG_NG << "player isn't controlling side which should get message\n";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unit_map::iterator speaker = handle_speaker(event_info, cfg, cfg["scroll"].to_bool(true));
|
||||
if (speaker == resources::units->end() && cfg["speaker"] != "narrator") {
|
||||
// No matching unit found, so the dialog can't come up.
|
||||
// Continue onto the next message.
|
||||
WRN_NG << "cannot show message" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> options;
|
||||
std::vector<vconfig::child_list> option_events;
|
||||
|
||||
for(vconfig::child_list::const_iterator mi = menu_items.begin();
|
||||
mi != menu_items.end(); ++mi) {
|
||||
std::string msg_str = (*mi)["message"];
|
||||
if (!mi->has_child("show_if")
|
||||
|| conditional_passed(mi->child("show_if")))
|
||||
{
|
||||
options.push_back(msg_str);
|
||||
option_events.push_back((*mi).get_children("command"));
|
||||
}
|
||||
}
|
||||
|
||||
has_input = !options.empty() || has_text_input;
|
||||
if (!has_input && resources::controller->is_skipping_replay()) {
|
||||
// No input to get and the user is not interested either.
|
||||
return;
|
||||
}
|
||||
|
||||
if (cfg.has_attribute("sound")) {
|
||||
sound::play_sound(cfg["sound"]);
|
||||
}
|
||||
|
||||
if(text_input_elements.size()>1) {
|
||||
lg::wml_error << "too many text_input tags, only one accepted\n";
|
||||
}
|
||||
|
||||
const vconfig text_input_element = has_text_input ?
|
||||
text_input_elements.front() : vconfig::empty_vconfig();
|
||||
|
||||
int option_chosen = 0;
|
||||
std::string text_input_result;
|
||||
|
||||
DBG_DP << "showing dialog...\n";
|
||||
|
||||
message_user_choice msg(cfg, speaker, text_input_element, has_text_input,
|
||||
options);
|
||||
if (!has_input)
|
||||
{
|
||||
/* Always show the dialog if it has no input, whether we are
|
||||
replaying or not. */
|
||||
msg.query_user(resources::controller->current_side());
|
||||
}
|
||||
else
|
||||
{
|
||||
config choice = mp_sync::get_user_choice("input", msg, cfg["side_for"].to_int(0));
|
||||
option_chosen = choice["value"];
|
||||
text_input_result = choice["text"].str();
|
||||
}
|
||||
|
||||
// Implement the consequences of the choice
|
||||
if(options.empty() == false) {
|
||||
if(size_t(option_chosen) >= menu_items.size()) {
|
||||
std::stringstream errbuf;
|
||||
errbuf << "invalid choice (" << option_chosen
|
||||
<< ") was specified, choice 0 to " << (menu_items.size() - 1)
|
||||
<< " was expected.\n";
|
||||
replay::process_error(errbuf.str());
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const vconfig &cmd, option_events[option_chosen]) {
|
||||
handle_event_commands(event_info, cmd);
|
||||
}
|
||||
}
|
||||
if(has_text_input) {
|
||||
std::string variable_name=text_input_element["variable"];
|
||||
if(variable_name.empty())
|
||||
variable_name="input";
|
||||
resources::gamedata->set_variable(variable_name, text_input_result);
|
||||
}
|
||||
}
|
||||
|
||||
WML_HANDLER_FUNCTION(modify_turns, /*event_info*/, cfg)
|
||||
{
|
||||
config::attribute_value value = cfg["value"];
|
||||
|
@ -797,81 +487,6 @@ WML_HANDLER_FUNCTION(move_units_fake, /*event_info*/, cfg)
|
|||
LOG_NG << "Units moved\n";
|
||||
}
|
||||
|
||||
WML_HANDLER_FUNCTION(object, event_info, cfg)
|
||||
{
|
||||
const vconfig & filter = cfg.child("filter");
|
||||
boost::optional<unit_filter> ufilt;
|
||||
if (!filter.null())
|
||||
ufilt = unit_filter(filter, resources::filter_con);
|
||||
|
||||
std::string id = cfg["id"];
|
||||
|
||||
// If this item has already been used
|
||||
assert(resources::game_events);
|
||||
if ( resources::game_events->item_used(id) )
|
||||
return;
|
||||
|
||||
std::string image = cfg["image"];
|
||||
std::string caption = cfg["name"];
|
||||
std::string text;
|
||||
|
||||
map_location loc;
|
||||
if(ufilt) {
|
||||
unit_const_ptr u_ptr = ufilt->first_match_on_map();
|
||||
if (u_ptr) {
|
||||
loc = u_ptr->get_location();
|
||||
}
|
||||
}
|
||||
|
||||
if(loc.valid() == false) {
|
||||
loc = event_info.loc1;
|
||||
}
|
||||
|
||||
const unit_map::iterator u = resources::units->find(loc);
|
||||
|
||||
std::string command_type = "then";
|
||||
|
||||
if ( u != resources::units->end() && (!ufilt || ufilt->matches(*u)) )
|
||||
{
|
||||
text = cfg["description"].str();
|
||||
|
||||
const bool no_add = cfg["no_write"].to_bool(false);
|
||||
|
||||
if(cfg["delayed_variable_substitution"].to_bool(false))
|
||||
u->add_modification("object", cfg.get_config(), no_add);
|
||||
else
|
||||
u->add_modification("object", cfg.get_parsed_config(), no_add);
|
||||
|
||||
resources::screen->select_hex(event_info.loc1);
|
||||
resources::screen->invalidate_unit();
|
||||
|
||||
// Mark this item as used up.
|
||||
resources::game_events->item_used(id, true);
|
||||
} else {
|
||||
text = cfg["cannot_use_message"].str();
|
||||
command_type = "else";
|
||||
}
|
||||
|
||||
// Default to silent if object has no description
|
||||
const bool silent = cfg.has_attribute("silent") ? cfg["silent"].to_bool() : !cfg.has_attribute("description");
|
||||
|
||||
if (!silent)
|
||||
{
|
||||
// Redraw the unit, with its new stats
|
||||
resources::screen->draw();
|
||||
|
||||
try {
|
||||
gui2::show_transient_message(resources::screen->video(), caption, text, image, true);
|
||||
} catch(utf8::invalid_utf8_exception&) {
|
||||
// we already had a warning so do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const vconfig &cmd, cfg.get_children(command_type)) {
|
||||
handle_event_commands(event_info, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/// If we should recall units that match a certain description.
|
||||
WML_HANDLER_FUNCTION(recall, /*event_info*/, cfg)
|
||||
{
|
||||
|
|
|
@ -64,28 +64,6 @@ void manager::add_event_handler(const config & handler, bool is_menu_item)
|
|||
event_handlers_->add_event_handler(handler, *this, is_menu_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an item has been used.
|
||||
* (An empty id will never be considered used.)
|
||||
*/
|
||||
bool manager::item_used(const std::string & id)
|
||||
{
|
||||
return !id.empty() && used_items_.count(id) > 0;
|
||||
}
|
||||
|
||||
/** Records if an item has been used. */
|
||||
void manager::item_used(const std::string & id, bool used)
|
||||
{
|
||||
// Empty IDs are not tracked.
|
||||
if ( id.empty() )
|
||||
return;
|
||||
|
||||
if ( used )
|
||||
used_items_.insert(id);
|
||||
else
|
||||
used_items_.erase(id);
|
||||
}
|
||||
|
||||
/** Removes an event handler. */
|
||||
void manager::remove_event_handler(const std::string & id)
|
||||
{
|
||||
|
@ -98,7 +76,6 @@ void manager::remove_event_handler(const std::string & id)
|
|||
manager::manager(const config& cfg, const boost::shared_ptr<t_context> & res)
|
||||
: event_handlers_(new t_event_handlers())
|
||||
, unit_wml_ids_()
|
||||
, used_items_()
|
||||
, pump_(new game_events::t_pump(*this, res))
|
||||
, resources_(res)
|
||||
, wml_menu_items_()
|
||||
|
@ -122,14 +99,6 @@ manager::manager(const config& cfg, const boost::shared_ptr<t_context> & res)
|
|||
resources_->lua_kernel->set_wml_action(action_cur->first, action_cur->second);
|
||||
}
|
||||
|
||||
const std::string used = cfg["used_items"];
|
||||
if(!used.empty()) {
|
||||
const std::vector<std::string>& v = utils::split(used);
|
||||
for(std::vector<std::string>::const_iterator i = v.begin(); i != v.end(); ++i) {
|
||||
item_used(*i, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the event handlers for menu items.
|
||||
wml_menu_items_.init_handlers(me_);
|
||||
}
|
||||
|
@ -238,7 +207,6 @@ void manager::write_events(config& cfg)
|
|||
cfg.add_child("event", eh->get_config());
|
||||
}
|
||||
|
||||
cfg["used_items"] = utils::join(used_items_);
|
||||
cfg["unit_wml_ids"] = utils::join(unit_wml_ids_);
|
||||
wml_menu_items_.to_config(cfg);
|
||||
}
|
||||
|
|
|
@ -112,7 +112,6 @@ namespace game_events {
|
|||
|
||||
boost::scoped_ptr<t_event_handlers> event_handlers_;
|
||||
std::set<std::string> unit_wml_ids_;
|
||||
std::set<std::string> used_items_;
|
||||
|
||||
boost::scoped_ptr<game_events::t_pump> pump_;
|
||||
boost::shared_ptr<t_context> resources_;
|
||||
|
@ -127,10 +126,6 @@ namespace game_events {
|
|||
|
||||
/// Create an event handler.
|
||||
void add_event_handler(const config & handler, bool is_menu_item=false);
|
||||
/// Checks if an item has been used.
|
||||
bool item_used(const std::string & id);
|
||||
/// Records if an item has been used.
|
||||
void item_used(const std::string & id, bool used);
|
||||
/// Removes an event handler.
|
||||
void remove_event_handler(const std::string & id);
|
||||
|
||||
|
|
|
@ -864,6 +864,16 @@ static void convert_old_saves_1_13_1(config& cfg)
|
|||
}
|
||||
}
|
||||
}
|
||||
BOOST_FOREACH(config& snapshot, cfg.child_range("snapshot")) {
|
||||
if (snapshot.has_attribute("used_items")) {
|
||||
config used_items;
|
||||
BOOST_FOREACH(const std::string& item, utils::split(snapshot["used_items"])) {
|
||||
used_items[item] = true;
|
||||
}
|
||||
snapshot.remove_attribute("used_items");
|
||||
snapshot.add_child("used_items", used_items);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void convert_old_saves(config& cfg)
|
||||
|
|
|
@ -124,6 +124,10 @@
|
|||
#include "lua/lauxlib.h" // for luaL_checkinteger, etc
|
||||
#include "lua/lua.h" // for lua_setfield, etc
|
||||
|
||||
#include "resources.hpp"
|
||||
#include "game_events/manager.hpp"
|
||||
#include "game_events/pump.hpp"
|
||||
|
||||
class CVideo;
|
||||
|
||||
#ifdef DEBUG_LUA
|
||||
|
@ -2850,6 +2854,48 @@ int game_lua_kernel::intf_select_hex(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deselects any highlighted hex on the map.
|
||||
* No arguments or return values
|
||||
*/
|
||||
int game_lua_kernel::intf_deselect_hex(lua_State*)
|
||||
{
|
||||
const map_location loc;
|
||||
play_controller_.get_mouse_handler_base().select_hex(
|
||||
loc, false, false, false);
|
||||
if (game_display_) {
|
||||
game_display_->highlight_hex(loc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a replay is in progress but the player has chosen to skip it
|
||||
*/
|
||||
int game_lua_kernel::intf_is_skipping_messages(lua_State *L)
|
||||
{
|
||||
bool skipping = play_controller_.is_skipping_replay();
|
||||
if (!skipping && resources::game_events) {
|
||||
skipping = resources::game_events->pump().context_skip_messages();
|
||||
}
|
||||
lua_pushboolean(L, skipping);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to skip messages
|
||||
* Arg 1 (optional) - boolean
|
||||
*/
|
||||
int game_lua_kernel::intf_skip_messages(lua_State *L)
|
||||
{
|
||||
bool skip = true;
|
||||
if (!lua_isnone(L, 1)) {
|
||||
skip = lua_toboolean(L, 1);
|
||||
}
|
||||
resources::game_events->pump().context_skip_messages(skip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct lua_synchronize : mp_sync::user_choice
|
||||
{
|
||||
|
@ -3283,6 +3329,7 @@ static int intf_get_traits(lua_State* L)
|
|||
* - Arg 1: unit.
|
||||
* - Arg 2: string.
|
||||
* - Arg 3: WML table.
|
||||
* - Arg 4: (optional) Whether to add to [modifications] - default true
|
||||
*/
|
||||
static int intf_add_modification(lua_State *L)
|
||||
{
|
||||
|
@ -3296,9 +3343,13 @@ static int intf_add_modification(lua_State *L)
|
|||
if (sm != "advancement" && sm != "object" && sm != "trait") {
|
||||
return luaL_argerror(L, 2, "unknown modification type");
|
||||
}
|
||||
bool write_to_mods = true;
|
||||
if (!lua_isnone(L, 4)) {
|
||||
write_to_mods = lua_toboolean(L, 4);
|
||||
}
|
||||
|
||||
config cfg = luaW_checkconfig(L, 3);
|
||||
u->add_modification(sm, cfg);
|
||||
u->add_modification(sm, cfg, !write_to_mods);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4154,6 +4205,9 @@ game_lua_kernel::game_lua_kernel(CVideo * video, game_state & gs, play_controlle
|
|||
{ "scroll", &dispatch<&game_lua_kernel::intf_scroll > },
|
||||
{ "scroll_to_tile", &dispatch<&game_lua_kernel::intf_scroll_to_tile > },
|
||||
{ "select_hex", &dispatch<&game_lua_kernel::intf_select_hex > },
|
||||
{ "deselect_hex", &dispatch<&game_lua_kernel::intf_deselect_hex > },
|
||||
{ "skip_messages", &dispatch<&game_lua_kernel::intf_skip_messages > },
|
||||
{ "is_skipping_messages", &dispatch<&game_lua_kernel::intf_is_skipping_messages > },
|
||||
{ "set_end_campaign_credits", &dispatch<&game_lua_kernel::intf_set_end_campaign_credits > },
|
||||
{ "set_end_campaign_text", &dispatch<&game_lua_kernel::intf_set_end_campaign_text > },
|
||||
{ "set_menu_item", &dispatch<&game_lua_kernel::intf_set_menu_item > },
|
||||
|
|
|
@ -129,6 +129,9 @@ class game_lua_kernel : public lua_kernel_base
|
|||
int intf_simulate_combat(lua_State *L);
|
||||
int intf_scroll_to_tile(lua_State *L);
|
||||
int intf_select_hex(lua_State *L);
|
||||
int intf_deselect_hex(lua_State *L);
|
||||
int intf_is_skipping_messages(lua_State *L);
|
||||
int intf_skip_messages(lua_State *L);
|
||||
int intf_synchronize_choice(lua_State *L);
|
||||
int intf_get_locations(lua_State *L);
|
||||
int intf_get_villages(lua_State *L);
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "gui/auxiliary/window_builder.hpp" // for twindow_builder, etc
|
||||
#include "gui/dialogs/gamestate_inspector.hpp"
|
||||
#include "gui/dialogs/lua_interpreter.hpp"
|
||||
#include "gui/dialogs/wml_message.hpp"
|
||||
#include "gui/dialogs/transient_message.hpp"
|
||||
#include "gui/widgets/clickable.hpp" // for tclickable_
|
||||
#include "gui/widgets/control.hpp" // for tcontrol
|
||||
#include "gui/widgets/multi_page.hpp" // for tmulti_page
|
||||
|
@ -41,6 +43,7 @@
|
|||
#include "scripting/lua_api.hpp" // for luaW_toboolean, etc
|
||||
#include "scripting/lua_common.hpp"
|
||||
#include "scripting/lua_types.hpp" // for getunitKey, dlgclbkKey, etc
|
||||
#include "scripting/push_check.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
#include "tstring.hpp"
|
||||
#include "video.hpp"
|
||||
|
@ -239,6 +242,71 @@ int show_dialog(lua_State *L, CVideo & video)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a message window
|
||||
* - Arg 1: Table describing the window
|
||||
* - Arg 2: List of options (nil or empty table - no options)
|
||||
* - Arg 3: Text input specifications (nil or empty table - no text input)
|
||||
* - Ret 1: option chosen (if no options: 0 if there's text input, -2 if escape pressed, else -1)
|
||||
* - Ret 2: string entered (empty if none, nil if no text input)
|
||||
*/
|
||||
int show_message_dialog(lua_State *L, CVideo & video)
|
||||
{
|
||||
config txt_cfg;
|
||||
const bool has_input = !lua_isnoneornil(L, 3) && luaW_toconfig(L, 3, txt_cfg) && !txt_cfg.empty();
|
||||
const std::string& input_caption = txt_cfg["label"];
|
||||
std::string input_text = txt_cfg["text"].str();
|
||||
unsigned int input_max_len = txt_cfg["max_length"].to_int(256);
|
||||
|
||||
std::vector<std::string> options;
|
||||
int chosen_option = -1;
|
||||
if (!lua_isnoneornil(L, 2)) {
|
||||
options = lua_check<std::vector<std::string> >(L, 2);
|
||||
}
|
||||
|
||||
const config& def_cfg = luaW_checkconfig(L, 1);
|
||||
const std::string& title = def_cfg["title"];
|
||||
const std::string& message = def_cfg["message"];
|
||||
const std::string& portrait = def_cfg["portrait"];
|
||||
const bool left_side = def_cfg["left_side"].to_bool(true);
|
||||
const bool mirror = def_cfg["mirror"].to_bool(false);
|
||||
|
||||
int dlg_result = gui2::show_wml_message(
|
||||
left_side, video, title, message, portrait, mirror,
|
||||
has_input, input_caption, &input_text, input_max_len,
|
||||
options, &chosen_option
|
||||
);
|
||||
|
||||
if (!has_input and options.empty()) {
|
||||
lua_pushinteger(L, dlg_result);
|
||||
} else {
|
||||
lua_pushinteger(L, chosen_option + 1);
|
||||
}
|
||||
|
||||
if (has_input) {
|
||||
lua_pushlstring(L, input_text.c_str(), input_text.length());
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a popup message
|
||||
* - Arg 1: Title (allows Pango markup)
|
||||
* - Arg 2: Message (allows Pango markup)
|
||||
* - Arg 3: Image (optional)
|
||||
*/
|
||||
int show_popup_dialog(lua_State *L, CVideo & video) {
|
||||
std::string title = luaL_checkstring(L, 1);
|
||||
std::string msg = luaL_checkstring(L, 2);
|
||||
std::string image = lua_isnoneornil(L, 3) ? "" : luaL_checkstring(L, 3);
|
||||
|
||||
gui2::show_transient_message(video, title, msg, image, true, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a widget on the current dialog.
|
||||
* - Arg 1: scalar.
|
||||
|
|
|
@ -32,6 +32,8 @@ int intf_set_dialog_active(lua_State *L);
|
|||
int intf_set_dialog_visible(lua_State *L);
|
||||
int intf_add_dialog_tree_node(lua_State *L);
|
||||
int show_dialog(lua_State *L, CVideo & video);
|
||||
int show_message_dialog(lua_State *L, CVideo & video);
|
||||
int show_popup_dialog(lua_State *L, CVideo & video);
|
||||
int show_lua_console(lua_State*L, CVideo & video, lua_kernel_base * lk);
|
||||
int show_gamestate_inspector(CVideo & video, const vconfig & cfg);
|
||||
int intf_remove_dialog_item(lua_State *L);
|
||||
|
|
|
@ -119,6 +119,28 @@ int lua_kernel_base::intf_show_dialog(lua_State *L)
|
|||
return lua_gui2::show_dialog(L, *video_);
|
||||
}
|
||||
|
||||
int lua_kernel_base::intf_show_message_dialog(lua_State *L)
|
||||
{
|
||||
if (!video_) {
|
||||
ERR_LUA << "Cannot show dialog, no video object is available to this lua kernel.";
|
||||
lua_error(L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return lua_gui2::show_message_dialog(L, *video_);
|
||||
}
|
||||
|
||||
int lua_kernel_base::intf_show_popup_dialog(lua_State *L)
|
||||
{
|
||||
if (!video_) {
|
||||
ERR_LUA << "Cannot show dialog, no video object is available to this lua kernel.";
|
||||
lua_error(L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return lua_gui2::show_popup_dialog(L, *video_);
|
||||
}
|
||||
|
||||
// The show lua console callback is similarly a method of lua kernel
|
||||
int lua_kernel_base::intf_show_lua_console(lua_State *L)
|
||||
{
|
||||
|
@ -257,6 +279,8 @@ lua_kernel_base::lua_kernel_base(CVideo * video)
|
|||
{ "dofile", &dispatch<&lua_kernel_base::intf_dofile> },
|
||||
{ "require", &dispatch<&lua_kernel_base::intf_require> },
|
||||
{ "show_dialog", &dispatch<&lua_kernel_base::intf_show_dialog> },
|
||||
{ "show_message_dialog", &dispatch<&lua_kernel_base::intf_show_message_dialog> },
|
||||
{ "show_popup_dialog", &dispatch<&lua_kernel_base::intf_show_popup_dialog> },
|
||||
{ "show_lua_console", &dispatch<&lua_kernel_base::intf_show_lua_console> },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
|
|
@ -103,6 +103,12 @@ protected:
|
|||
|
||||
// Show a dialog to the currently connected video object (if available)
|
||||
int intf_show_dialog(lua_State * L);
|
||||
|
||||
// Show a message dialog, possibly with options
|
||||
int intf_show_message_dialog(lua_State * L);
|
||||
|
||||
// Show a transient popup message
|
||||
int intf_show_popup_dialog(lua_State * L);
|
||||
|
||||
// Show the interactive lua console (for debugging purposes)
|
||||
int intf_show_lua_console(lua_State * L);
|
||||
|
|
Loading…
Add table
Reference in a new issue