Use to-be-closed variables to scope WML variables in tag definitions (#5536)
This commit is contained in:
parent
a2d676e394
commit
a0ee38a49a
8 changed files with 77 additions and 35 deletions
|
@ -111,7 +111,7 @@ wesnoth.wml_actions["for"] = function(cfg)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local i_var = cfg.variable or "i"
|
local i_var = cfg.variable or "i"
|
||||||
local save_i = utils.start_var_scope(i_var)
|
local save_i <close> = utils.scoped_var(i_var)
|
||||||
wml.variables[i_var] = first
|
wml.variables[i_var] = first
|
||||||
local function loop_condition()
|
local function loop_condition()
|
||||||
local sentinel = loop_lim.last
|
local sentinel = loop_lim.last
|
||||||
|
@ -146,7 +146,6 @@ wesnoth.wml_actions["for"] = function(cfg)
|
||||||
wml.variables[i_var] = wml.variables[i_var] + loop_lim.step
|
wml.variables[i_var] = wml.variables[i_var] + loop_lim.step
|
||||||
end
|
end
|
||||||
::exit::
|
::exit::
|
||||||
utils.end_var_scope(i_var, save_i)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
wml_actions["repeat"] = function(cfg)
|
wml_actions["repeat"] = function(cfg)
|
||||||
|
@ -180,9 +179,9 @@ function wml_actions.foreach(cfg)
|
||||||
local array = wml.array_variables[array_name]
|
local array = wml.array_variables[array_name]
|
||||||
if #array == 0 then return end -- empty and scalars unwanted
|
if #array == 0 then return end -- empty and scalars unwanted
|
||||||
local item_name = cfg.variable or "this_item"
|
local item_name = cfg.variable or "this_item"
|
||||||
local this_item = utils.start_var_scope(item_name) -- if this_item is already set
|
local this_item <close> = utils.scoped_var(item_name) -- if this_item is already set
|
||||||
local i_name = cfg.index_var or "i"
|
local i_name = cfg.index_var or "i"
|
||||||
local i = utils.start_var_scope(i_name) -- if i is already set
|
local i <close> = utils.scoped_var(i_name) -- if i is already set
|
||||||
local array_length = wml.variables[array_name .. ".length"]
|
local array_length = wml.variables[array_name .. ".length"]
|
||||||
|
|
||||||
for index, value in ipairs(array) do
|
for index, value in ipairs(array) do
|
||||||
|
@ -214,10 +213,6 @@ function wml_actions.foreach(cfg)
|
||||||
end
|
end
|
||||||
::exit::
|
::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.
|
This forces the readonly key to be taken literally.
|
||||||
|
|
||||||
|
|
|
@ -166,14 +166,14 @@ function utils.parenthetical_split(str)
|
||||||
end
|
end
|
||||||
|
|
||||||
--note: when using these, make sure that nothing can throw over the call to end_var_scope
|
--note: when using these, make sure that nothing can throw over the call to end_var_scope
|
||||||
function utils.start_var_scope(name)
|
local function start_var_scope(name)
|
||||||
local var = wml.array_variables[name] --containers and arrays
|
local var = wml.array_variables[name] --containers and arrays
|
||||||
if #var == 0 then var = wml.variables[name] end --scalars (and nil/empty)
|
if #var == 0 then var = wml.variables[name] end --scalars (and nil/empty)
|
||||||
wml.variables[name] = nil
|
wml.variables[name] = nil
|
||||||
return var
|
return var
|
||||||
end
|
end
|
||||||
|
|
||||||
function utils.end_var_scope(name, var)
|
local function end_var_scope(name, var)
|
||||||
wml.variables[name] = nil
|
wml.variables[name] = nil
|
||||||
if type(var) == "table" then
|
if type(var) == "table" then
|
||||||
wml.array_variables[name] = var
|
wml.array_variables[name] = var
|
||||||
|
@ -182,8 +182,43 @@ function utils.end_var_scope(name, var)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function utils.scoped_var(name)
|
||||||
|
local orig = start_var_scope(name)
|
||||||
|
return setmetatable({
|
||||||
|
set = function(self, new)
|
||||||
|
if type(new) == "table" then
|
||||||
|
wml.array_variables[name] = new
|
||||||
|
else
|
||||||
|
wml.variables[name] = new
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
get = function(self)
|
||||||
|
local val = wml.array_variables[name]
|
||||||
|
if #val == 0 then val = wml.variables[name] end
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
}, {
|
||||||
|
__metatable = "scoped WML variable",
|
||||||
|
__close = function(self)
|
||||||
|
end_var_scope(name, orig)
|
||||||
|
end,
|
||||||
|
__index = function(self, key)
|
||||||
|
if key == '__original' then
|
||||||
|
return orig
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
__newindex = function(self, key, val)
|
||||||
|
if key == '__original' then
|
||||||
|
error("scoped variable '__original' value is read-only", 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
utils.trim = wesnoth.deprecate_api('wml_utils.trim', 'stringx.trim', 1, nil, stringx.trim)
|
utils.trim = wesnoth.deprecate_api('wml_utils.trim', 'stringx.trim', 1, nil, stringx.trim)
|
||||||
utils.parenthetical_split = wesnoth.deprecate_api('wml_utils.parenthetical_split', 'stringx.quoted_split or stringx.split', 1, nil, utils.parenthetical_split)
|
utils.parenthetical_split = wesnoth.deprecate_api('wml_utils.parenthetical_split', 'stringx.quoted_split or stringx.split', 1, nil, utils.parenthetical_split)
|
||||||
utils.split = wesnoth.deprecate_api('wml_utils.split', 'stringx.split', 1, nil, utils.split)
|
utils.split = wesnoth.deprecate_api('wml_utils.split', 'stringx.split', 1, nil, utils.split)
|
||||||
|
utils.start_var_scope = wesnoth.deprecate_api('wml_utils.start_var_scope', 'wml_utils.scoped_var', 1, nil, start_var_scope, 'Assign the scoped_var to a to-be-closed local variable.')
|
||||||
|
utils.end_var_scope = wesnoth.deprecate_api('wml_utils.end_var_scope', 'wml_utils.scoped_var', 1, nil, end_var_scope, 'Assign the scoped_var to a to-be-closed local variable.')
|
||||||
|
|
||||||
return utils
|
return utils
|
||||||
|
|
|
@ -6,7 +6,7 @@ function wesnoth.wml_actions.find_path(cfg)
|
||||||
local unit = wesnoth.units.find_on_map(filter_unit)[1] or wml.error("[find_path]'s filter didn't match any unit")
|
local unit = wesnoth.units.find_on_map(filter_unit)[1] or wml.error("[find_path]'s filter didn't match any unit")
|
||||||
local filter_location = wml.get_child(cfg, "destination") or wml.error( "[find_path] missing required [destination] tag" )
|
local filter_location = wml.get_child(cfg, "destination") or wml.error( "[find_path] missing required [destination] tag" )
|
||||||
-- support for $this_unit
|
-- support for $this_unit
|
||||||
local this_unit = utils.start_var_scope("this_unit")
|
local this_unit <close> = utils.scoped_var("this_unit")
|
||||||
|
|
||||||
wml.variables["this_unit"] = nil -- clearing this_unit
|
wml.variables["this_unit"] = nil -- clearing this_unit
|
||||||
wml.variables["this_unit"] = unit.__cfg -- cfg field needed
|
wml.variables["this_unit"] = unit.__cfg -- cfg field needed
|
||||||
|
@ -116,17 +116,13 @@ function wesnoth.wml_actions.find_path(cfg)
|
||||||
|
|
||||||
if cost >= 42424241 then -- it's the high value returned for unwalkable or busy terrains
|
if cost >= 42424241 then -- it's the high value returned for unwalkable or busy terrains
|
||||||
wml.variables[tostring(variable)] = { hexes = 0 } -- set only length, nil all other values
|
wml.variables[tostring(variable)] = { hexes = 0 } -- set only length, nil all other values
|
||||||
-- support for $this_unit
|
return
|
||||||
wml.variables["this_unit"] = nil -- clearing this_unit
|
end
|
||||||
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
|
if not allow_multiple_turns and turns > 1 then -- location cannot be reached in one turn
|
||||||
wml.variables[tostring(variable)] = { hexes = 0 }
|
wml.variables[tostring(variable)] = { hexes = 0 }
|
||||||
-- support for $this_unit
|
return
|
||||||
wml.variables["this_unit"] = nil -- clearing this_unit
|
end -- skip the cycles below
|
||||||
utils.end_var_scope("this_unit", this_unit)
|
|
||||||
return end -- skip the cycles below
|
|
||||||
|
|
||||||
wml.variables[tostring( variable )] =
|
wml.variables[tostring( variable )] =
|
||||||
{
|
{
|
||||||
|
@ -165,8 +161,4 @@ function wesnoth.wml_actions.find_path(cfg)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- support for $this_unit
|
|
||||||
wml.variables["this_unit"] = nil -- clearing this_unit
|
|
||||||
utils.end_var_scope("this_unit", this_unit)
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,7 +19,7 @@ function wml_actions.harm_unit(cfg)
|
||||||
else return false end
|
else return false end
|
||||||
end
|
end
|
||||||
|
|
||||||
local this_unit = utils.start_var_scope("this_unit")
|
local this_unit <close> = utils.scoped_var("this_unit")
|
||||||
|
|
||||||
for index, unit_to_harm in ipairs(wesnoth.units.find_on_map(filter)) do
|
for index, unit_to_harm in ipairs(wesnoth.units.find_on_map(filter)) do
|
||||||
if unit_to_harm.valid then
|
if unit_to_harm.valid then
|
||||||
|
@ -202,7 +202,4 @@ function wml_actions.harm_unit(cfg)
|
||||||
|
|
||||||
wml_actions.redraw {}
|
wml_actions.redraw {}
|
||||||
end
|
end
|
||||||
|
|
||||||
wml.variables["this_unit"] = nil -- clearing this_unit
|
|
||||||
utils.end_var_scope("this_unit", this_unit)
|
|
||||||
end
|
end
|
|
@ -184,12 +184,11 @@ local function simple_modify_unit(cfg)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local this_unit = utils.start_var_scope("this_unit")
|
local this_unit <close> = utils.scoped_var("this_unit")
|
||||||
for i, u in ipairs(wesnoth.units.find(filter)) do
|
for i, u in ipairs(wesnoth.units.find(filter)) do
|
||||||
wml.variables["this_unit"] = u.__cfg
|
wml.variables["this_unit"] = u.__cfg
|
||||||
handle_unit(u)
|
handle_unit(u)
|
||||||
end
|
end
|
||||||
utils.end_var_scope("this_unit", this_unit)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function wml_actions.modify_unit(cfg)
|
function wml_actions.modify_unit(cfg)
|
||||||
|
@ -289,11 +288,10 @@ function wml_actions.modify_unit(cfg)
|
||||||
wml_actions.store_unit { {"filter", filter}, variable = unit_variable }
|
wml_actions.store_unit { {"filter", filter}, variable = unit_variable }
|
||||||
local max_index = wml.variables[unit_variable .. ".length"] - 1
|
local max_index = wml.variables[unit_variable .. ".length"] - 1
|
||||||
|
|
||||||
local this_unit = utils.start_var_scope("this_unit")
|
local this_unit <close> = utils.scoped_var("this_unit")
|
||||||
for current_unit = 0, max_index do
|
for current_unit = 0, max_index do
|
||||||
handle_unit(current_unit)
|
handle_unit(current_unit)
|
||||||
end
|
end
|
||||||
utils.end_var_scope("this_unit", this_unit)
|
|
||||||
|
|
||||||
wml.variables[unit_variable] = nil
|
wml.variables[unit_variable] = nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,7 +9,7 @@ wesnoth.wml_actions.random_placement = function(cfg)
|
||||||
local num_items = cfg.num_items or wml.error("[random_placement] missing required 'num_items' attribute")
|
local num_items = cfg.num_items or wml.error("[random_placement] missing required 'num_items' attribute")
|
||||||
local variable = cfg.variable or wml.error("[random_placement] missing required 'variable' attribute")
|
local variable = cfg.variable or wml.error("[random_placement] missing required 'variable' attribute")
|
||||||
local allow_less = cfg.allow_less == true
|
local allow_less = cfg.allow_less == true
|
||||||
local variable_previous = utils.start_var_scope(variable)
|
local variable_previous <close> = utils.scoped_var(variable)
|
||||||
local math_abs = math.abs
|
local math_abs = math.abs
|
||||||
local locs = wesnoth.get_locations(filter)
|
local locs = wesnoth.get_locations(filter)
|
||||||
if type(num_items) == "string" then
|
if type(num_items) == "string" then
|
||||||
|
@ -87,11 +87,8 @@ wesnoth.wml_actions.random_placement = function(cfg)
|
||||||
utils.set_exiting("none")
|
utils.set_exiting("none")
|
||||||
break
|
break
|
||||||
elseif action ~= "none" then
|
elseif action ~= "none" then
|
||||||
utils.end_var_scope(variable, variable_previous)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
utils.end_var_scope(variable, variable_previous)
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
27
data/test/scenarios/test_scoped_vars.cfg
Normal file
27
data/test/scenarios/test_scoped_vars.cfg
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{GENERIC_UNIT_TEST "test_scoped_vars" (
|
||||||
|
[event]
|
||||||
|
name = prestart
|
||||||
|
{VARIABLE test_var 1}
|
||||||
|
#[inspect][/inspect]
|
||||||
|
{ASSERT {VARIABLE_CONDITIONAL test_var equals 1}}
|
||||||
|
[lua]
|
||||||
|
code = <<
|
||||||
|
local wml_utils = wesnoth.require "wml-utils"
|
||||||
|
local var <close> = wml_utils.scoped_var("test_var")
|
||||||
|
-- This runs the contents of [args] as WML actions
|
||||||
|
wml_utils.handle_event_commands(...)
|
||||||
|
>>
|
||||||
|
[args]
|
||||||
|
#[inspect][/inspect]
|
||||||
|
{ASSERT {VARIABLE_CONDITIONAL test_var equals ""}}
|
||||||
|
{VARIABLE test_var 5}
|
||||||
|
#[inspect][/inspect]
|
||||||
|
{ASSERT {VARIABLE_CONDITIONAL test_var equals 5}}
|
||||||
|
[/args]
|
||||||
|
[/lua]
|
||||||
|
|
||||||
|
#[inspect][/inspect]
|
||||||
|
{ASSERT {VARIABLE_CONDITIONAL test_var equals 1}}
|
||||||
|
{SUCCEED}
|
||||||
|
[/event]
|
||||||
|
)}
|
|
@ -115,6 +115,7 @@
|
||||||
0 test_wml_actions
|
0 test_wml_actions
|
||||||
0 test_wml_conditionals
|
0 test_wml_conditionals
|
||||||
0 lua_wml_tagnames
|
0 lua_wml_tagnames
|
||||||
|
0 test_scoped_vars
|
||||||
0 as_text
|
0 as_text
|
||||||
#
|
#
|
||||||
# Pathfinding
|
# Pathfinding
|
||||||
|
|
Loading…
Add table
Reference in a new issue