262 lines
7.6 KiB
Lua
262 lines
7.6 KiB
Lua
local helper = wesnoth.require "helper"
|
|
local utils = wesnoth.require "wml-utils"
|
|
local wml_actions = wesnoth.wml_actions
|
|
|
|
function wml_actions.command(cfg)
|
|
utils.handle_event_commands(cfg, "plain")
|
|
end
|
|
|
|
-- we can't create functions with names that are Lua keywords (eg if, while)
|
|
-- instead, we store the following anonymous functions directly into
|
|
-- the table, using the [] operator, rather than by using the point syntax
|
|
|
|
wml_actions["if"] = function(cfg)
|
|
if not (wml.get_child(cfg, 'then') or wml.get_child(cfg, 'elseif') or wml.get_child(cfg, 'else')) then
|
|
helper.wml_error("[if] didn't find any [then], [elseif], or [else] children.")
|
|
end
|
|
|
|
if wesnoth.eval_conditional(cfg) then -- evaluate [if] tag
|
|
for then_child in wml.child_range(cfg, "then") do
|
|
local action = utils.handle_event_commands(then_child, "conditional")
|
|
if action ~= "none" then break end
|
|
end
|
|
return -- stop after executing [then] tags
|
|
end
|
|
|
|
for elseif_child in wml.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 wml.child_range(elseif_child, "then") do
|
|
local action = utils.handle_event_commands(then_tag, "conditional")
|
|
if action ~= "none" then break end
|
|
end
|
|
return -- stop on first matched condition
|
|
end
|
|
end
|
|
|
|
-- no matched condition, try the [else] tags
|
|
for else_child in wml.child_range(cfg, "else") do
|
|
local action = utils.handle_event_commands(else_child, "conditional")
|
|
if action ~= "none" then break end
|
|
end
|
|
end
|
|
|
|
wml_actions["while"] = function( cfg )
|
|
if wml.child_count(cfg, "do") == 0 then
|
|
helper.wml_error "[while] does not contain any [do] tags"
|
|
end
|
|
|
|
-- execute [do] up to 65536 times
|
|
for i = 1, 65536 do
|
|
if wesnoth.eval_conditional( cfg ) then
|
|
for do_child in wml.child_range( cfg, "do" ) do
|
|
local action = utils.handle_event_commands(do_child, "loop")
|
|
if action == "break" then
|
|
utils.set_exiting("none")
|
|
return
|
|
elseif action == "continue" then
|
|
utils.set_exiting("none")
|
|
break
|
|
elseif action ~= "none" then
|
|
return
|
|
end
|
|
end
|
|
else return end
|
|
end
|
|
end
|
|
|
|
wml_actions["break"] = function(cfg)
|
|
utils.set_exiting("break")
|
|
end
|
|
|
|
wml_actions["return"] = function(cfg)
|
|
utils.set_exiting("return")
|
|
end
|
|
|
|
function wml_actions.continue(cfg)
|
|
utils.set_exiting("continue")
|
|
end
|
|
|
|
wesnoth.wml_actions["for"] = function(cfg)
|
|
if wml.child_count(cfg, "do") == 0 then
|
|
helper.wml_error "[for] does not contain any [do] tags"
|
|
end
|
|
|
|
local loop_lim = {}
|
|
local first
|
|
if cfg.array ~= nil then
|
|
if cfg.reverse then
|
|
first = wml.variables[cfg.array .. ".length"] - 1
|
|
loop_lim.last = 0
|
|
loop_lim.step = -1
|
|
else
|
|
first = 0
|
|
loop_lim.last = '$($' .. cfg.array .. ".length - 1)"
|
|
loop_lim.step = 1
|
|
end
|
|
else
|
|
-- Get a literal config to fetch end and step;
|
|
-- this done is to delay expansion of variables
|
|
local cfg_lit = wml.literal(cfg)
|
|
first = cfg.start or 0
|
|
loop_lim.last = cfg_lit["end"] or first
|
|
loop_lim.step = cfg_lit.step or 1
|
|
end
|
|
loop_lim = wml.tovconfig(loop_lim)
|
|
if loop_lim.step == 0 then -- Sanity check
|
|
helper.wml_error("[for] has a step of 0!")
|
|
end
|
|
if (first < loop_lim.last and loop_lim.step <= 0)
|
|
or (first > loop_lim.last and loop_lim.step >= 0) then
|
|
-- Sanity check: If they specify something like start,end,step=1,4,-1
|
|
-- then we do nothing
|
|
return
|
|
end
|
|
local i_var = cfg.variable or "i"
|
|
local save_i = utils.start_var_scope(i_var)
|
|
wml.variables[i_var] = first
|
|
local function loop_condition()
|
|
local sentinel = loop_lim.last
|
|
if loop_lim.step then
|
|
sentinel = sentinel + loop_lim.step
|
|
if loop_lim.step > 0 then
|
|
return wml.variables[i_var] < sentinel
|
|
else
|
|
return wml.variables[i_var] > sentinel
|
|
end
|
|
elseif loop_lim.last < first then
|
|
sentinel = sentinel - 1
|
|
return wml.variables[i_var] > sentinel
|
|
else
|
|
sentinel = sentinel + 1
|
|
return wml.variables[i_var] < sentinel
|
|
end
|
|
end
|
|
while loop_condition() do
|
|
for do_child in wml.child_range( cfg, "do" ) do
|
|
local action = utils.handle_event_commands(do_child, "loop")
|
|
if action == "break" then
|
|
utils.set_exiting("none")
|
|
goto exit
|
|
elseif action == "continue" then
|
|
utils.set_exiting("none")
|
|
break
|
|
elseif action ~= "none" then
|
|
goto exit
|
|
end
|
|
end
|
|
wml.variables[i_var] = wml.variables[i_var] + loop_lim.step
|
|
end
|
|
::exit::
|
|
utils.end_var_scope(i_var, save_i)
|
|
end
|
|
|
|
wml_actions["repeat"] = function(cfg)
|
|
if wml.child_count(cfg, "do") == 0 then
|
|
helper.wml_error "[repeat] does not contain any [do] tags"
|
|
end
|
|
|
|
local times = cfg.times or 1
|
|
for i = 1, times do
|
|
for do_child in wml.child_range( cfg, "do" ) do
|
|
local action = utils.handle_event_commands(do_child, "loop")
|
|
if action == "break" then
|
|
utils.set_exiting("none")
|
|
return
|
|
elseif action == "continue" then
|
|
utils.set_exiting("none")
|
|
break
|
|
elseif action ~= "none" then
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function wml_actions.foreach(cfg)
|
|
if wml.child_count(cfg, "do") == 0 then
|
|
helper.wml_error "[foreach] does not contain any [do] tags"
|
|
end
|
|
|
|
local array_name = cfg.array or helper.wml_error "[foreach] missing required array= attribute"
|
|
local array = wml.array_access.get(array_name)
|
|
if #array == 0 then return end -- empty and scalars unwanted
|
|
local item_name = cfg.variable or "this_item"
|
|
local this_item = utils.start_var_scope(item_name) -- if this_item is already set
|
|
local i_name = cfg.index_var or "i"
|
|
local i = utils.start_var_scope(i_name) -- if i is already set
|
|
local array_length = wml.variables[array_name .. ".length"]
|
|
|
|
for index, value in ipairs(array) do
|
|
-- Some protection against external modification
|
|
-- It's not perfect, though - it'd be nice if *any* change could be detected
|
|
if array_length ~= wml.variables[array_name .. ".length"] then
|
|
helper.wml_error("WML array length changed during [foreach] iteration")
|
|
end
|
|
wml.variables[item_name] = value
|
|
-- set index variable
|
|
wml.variables[i_name] = index-1 -- here -1, because of WML array
|
|
-- perform actions
|
|
for do_child in wml.child_range(cfg, "do") do
|
|
local action = utils.handle_event_commands(do_child, "loop")
|
|
if action == "break" then
|
|
utils.set_exiting("none")
|
|
goto exit
|
|
elseif action == "continue" then
|
|
utils.set_exiting("none")
|
|
break
|
|
elseif action ~= "none" then
|
|
goto exit
|
|
end
|
|
end
|
|
-- set back the content, in case the author made some modifications
|
|
if not cfg.readonly then
|
|
array[index] = wml.variables[item_name]
|
|
end
|
|
end
|
|
::exit::
|
|
|
|
-- house cleaning
|
|
utils.end_var_scope(item_name, this_item)
|
|
utils.end_var_scope(i_name, i)
|
|
|
|
--[[
|
|
This forces the readonly key to be taken literally.
|
|
|
|
If readonly=yes, then this line guarantees that the array
|
|
is unchanged after the [foreach] loop ends.
|
|
|
|
If readonly=no, then this line updates the array with any
|
|
changes the user has applied through the $this_item
|
|
variable (or whatever variable was given in item_var).
|
|
|
|
Note that altering the array via indexing (with the index_var)
|
|
is not supported; any such changes will be reverted by this line.
|
|
]]
|
|
wml.array_access.set(array_name, array)
|
|
end
|
|
|
|
function wml_actions.switch(cfg)
|
|
local var_value = wml.variables[cfg.variable]
|
|
local found = false
|
|
|
|
-- Execute all the [case]s where the value matches.
|
|
for v in wml.child_range(cfg, "case") do
|
|
for w in utils.split(v.value) do
|
|
if w == tostring(var_value) then
|
|
local action = utils.handle_event_commands(v, "switch")
|
|
found = true
|
|
if action ~= "none" then goto exit end
|
|
break
|
|
end
|
|
end
|
|
end
|
|
::exit::
|
|
|
|
-- Otherwise execute [else] statements.
|
|
if not found then
|
|
for v in wml.child_range(cfg, "else") do
|
|
local action = utils.handle_event_commands(v, "switch")
|
|
if action ~= "none" then break end
|
|
end
|
|
end
|
|
end
|