Move all WML and variable manipulator functions into new wml table

(The old functions in wesnoth and helper are still available,
but they are now deprecated.)
This commit is contained in:
Celtic Minstrel 2017-05-22 12:01:12 -04:00
parent b1e4b83014
commit c0f926e2ed
2 changed files with 309 additions and 235 deletions

View file

@ -15,3 +15,256 @@ function wesnoth.confirm(title, msg)
end
return wesnoth.show_message_box(title, msg, "yes_no", true)
end
wml = {}
wml.variable = {}
--[========[Config Manipulation Functions]========]
wml.tovconfig = wesnoth.tovconfig
wml.tostring = wesnoth.debug
local function ensure_config(cfg)
if type(cfg) == 'table' then
return true
end
if type(cfg) == 'userdata' then
if getmetatable(cfg) == 'wml object' then return true end
error("Expected a table or wml object but got " .. getmetatable(cfg), 3)
else
error("Expected a table or wml object but got " .. type(cfg), 3)
end
return false
end
--! Returns the first subtag of @a cfg with the given @a name.
--! If @a id is not nil, the "id" attribute of the subtag has to match too.
--! The function also returns the index of the subtag in the array.
function wml.get_child(cfg, name, id)
ensure_config(cfg)
for i,v in ipairs(cfg) do
if v[1] == name then
local w = v[2]
if not id or w.id == id then return w, i end
end
end
end
--! Returns the nth subtag of @a cfg with the given @a name.
--! (Indices start at 1, as always with Lua.)
--! The function also returns the index of the subtag in the array.
function wml.get_nth_child(cfg, name, n)
ensure_config(cfg)
for i,v in ipairs(cfg) do
if v[1] == name then
n = n - 1
if n == 0 then return v[2], i end
end
end
end
--! Returns the number of subtags of @a with the given @a name.
function wml.child_count(cfg, name)
ensure_config(cfg)
local n = 0
for i,v in ipairs(cfg) do
if v[1] == name then
n = n + 1
end
end
return n
end
--! Returns an iterator over all the subtags of @a cfg with the given @a name.
function wml.child_range(cfg, tag)
ensure_config(cfg)
local iter, state, i = ipairs(cfg)
local function f(s)
local c
repeat
i,c = iter(s,i)
if not c then return end
until c[1] == tag
return c[2]
end
return f, state
end
--! Returns an array from the subtags of @a cfg with the given @a name
function wml.child_array(cfg, tag)
ensure_config(cfg)
local result = {}
for val in helper.child_range(cfg, tag) do
table.insert(result, val)
end
return result
end
--[========[WML Tag Creation Table]========]
local create_tag_mt = {
__metatable = "WML tag builder",
__index = function(self, n)
return function(cfg) return { n, cfg } end
end
}
wml.tag = setmetatable({}, create_tag_mt)
--[========[Config / Vconfig Unified Handling]========]
function wml.literal(cfg)
if type(cfg) == "userdata" then
return cfg.__literal
else
return cfg or {}
end
end
function wml.parsed(cfg)
if type(cfg) == "userdata" then
return cfg.__parsed
else
return cfg or {}
end
end
function wml.shallow_literal(cfg)
if type(cfg) == "userdata" then
return cfg.__shallow_literal
else
return cfg or {}
end
end
function wml.shallow_parsed(cfg)
if type(cfg) == "userdata" then
return cfg.__shallow_parsed
else
return cfg or {}
end
end
--[========[Basic variable access]========]
wml.variable.get = wesnoth.get_variable
wml.variable.set = wesnoth.set_variable
wml.variable.get_all = wesnoth.get_all_vars
--[========[Variable Proxy Table]========]
local variable_mt = {
__metatable = "WML variable proxy"
}
local function get_variable_proxy(k)
local v = wesnoth.get_variable(k, true)
if type(v) == "table" then
v = setmetatable({ __varname = k }, variable_mt)
end
return v
end
local function set_variable_proxy(k, v)
if getmetatable(v) == "WML variable proxy" then
v = wesnoth.get_variable(v.__varname)
end
wesnoth.set_variable(k, v)
end
function variable_mt.__index(t, k)
local i = tonumber(k)
if i then
k = t.__varname .. '[' .. i .. ']'
else
k = t.__varname .. '.' .. k
end
return get_variable_proxy(k)
end
function variable_mt.__newindex(t, k, v)
local i = tonumber(k)
if i then
k = t.__varname .. '[' .. i .. ']'
else
k = t.__varname .. '.' .. k
end
set_variable_proxy(k, v)
end
local root_variable_mt = {
__metatable = "WML variables",
__index = function(t, k) return get_variable_proxy(k) end,
__newindex = function(t, k, v)
if type(v) == "function" then
-- User-friendliness when _G is overloaded early.
-- FIXME: It should be disabled outside the "preload" event.
rawset(t, k, v)
else
set_variable_proxy(k, v)
end
end
}
wml.variable.proxy = setmetatable({}, root_variable_mt)
--[========[Variable Array Access]========]
local function resolve_variable_context(ctx, err_hint)
if ctx == nil then
return {get = wesnoth.get_variable, set = wesnoth.set_variable}
elseif type(ctx) == 'number' and ctx > 0 and ctx <= #wesnoth.sides then
return resolve_variable_context(wesnoth.sides[ctx])
elseif type(ctx) == 'string' then
-- TODO: Treat it as a namespace for a global (persistent) variable
-- (Need Lua API for accessing them first, though.)
elseif getmetatable(ctx) == "unit" then
return {
get = function(path) return ctx.variables[path] end,
set = function(path, val) ctx.variables[path] = val end,
}
elseif getmetatable(ctx) == "side" then
return {
get = function(path) return wesnoth.get_side_variable(ctx.side, path) end,
set = function(path, val) wesnoth.set_side_variable(ctx.side, path, val) end,
}
elseif getmetatable(ctx) == "unit variables" or getmetatable(ctx) == "side variables" then
return {
get = function(path) return ctx[path] end,
set = function(path, val) ctx[path] = val end,
}
end
error(string.format("Invalid context for %s: expected nil, side, or unit", err_hint), 3)
end
--! Fetches all the WML container variables with name @a var.
--! @returns a table containing all the variables (starting at index 1).
function wml.variable.get_array(var, context)
context = resolve_variable_context(context, "get_variable_array")
local result = {}
for i = 1, context.get(var .. ".length") do
result[i] = context.get(string.format("%s[%d]", var, i - 1))
end
return result
end
--! Puts all the elements of table @a t inside a WML container with name @a var.
function wml.variable.set_array(var, t, context)
context = resolve_variable_context(context, "set_variable_array")
context.set(var)
for i, v in ipairs(t) do
context.set(string.format("%s[%d]", var, i - 1), v)
end
end
--! Creates proxies for all the WML container variables with name @a var.
--! This is similar to helper.get_variable_array, except that the elements
--! can be used for writing too.
--! @returns a table containing all the variable proxies (starting at index 1).
function wml.get_proxy_array(var)
local result = {}
for i = 1, wesnoth.get_variable(var .. ".length") do
result[i] = get_variable_proxy(string.format("%s[%d]", var, i - 1))
end
return result
end

View file

@ -35,82 +35,6 @@ function helper.all_teams()
return f, { i = 1 }
end
local function ensure_config(cfg)
if type(cfg) == 'table' then
return true
end
if type(cfg) == 'userdata' then
if getmetatable(cfg) == 'wml object' then return true end
error("Expected a table or wml object but got " .. getmetatable(cfg), 3)
else
error("Expected a table or wml object but got " .. type(cfg), 3)
end
return false
end
--! Returns the first subtag of @a cfg with the given @a name.
--! If @a id is not nil, the "id" attribute of the subtag has to match too.
--! The function also returns the index of the subtag in the array.
function helper.get_child(cfg, name, id)
ensure_config(cfg)
for i,v in ipairs(cfg) do
if v[1] == name then
local w = v[2]
if not id or w.id == id then return w, i end
end
end
end
--! Returns the nth subtag of @a cfg with the given @a name.
--! (Indices start at 1, as always with Lua.)
--! The function also returns the index of the subtag in the array.
function helper.get_nth_child(cfg, name, n)
ensure_config(cfg)
for i,v in ipairs(cfg) do
if v[1] == name then
n = n - 1
if n == 0 then return v[2], i end
end
end
end
--! Returns the number of subtags of @a with the given @a name.
function helper.child_count(cfg, name)
ensure_config(cfg)
local n = 0
for i,v in ipairs(cfg) do
if v[1] == name then
n = n + 1
end
end
return n
end
--! Returns an iterator over all the subtags of @a cfg with the given @a name.
function helper.child_range(cfg, tag)
ensure_config(cfg)
local iter, state, i = ipairs(cfg)
local function f(s)
local c
repeat
i,c = iter(s,i)
if not c then return end
until c[1] == tag
return c[2]
end
return f, state
end
--! Returns an array from the subtags of @a cfg with the given @a name
function helper.child_array(cfg, tag)
ensure_config(cfg)
local result = {}
for val in helper.child_range(cfg, tag) do
table.insert(result, val)
end
return result
end
--! Modifies all the units satisfying the given @a filter.
--! @param vars key/value pairs that need changing.
--! @note Usable only during WML actions.
@ -175,67 +99,15 @@ function helper.move_unit_fake(filter, to_x, to_y)
wesnoth.set_variable("LUA_move_unit")
end
local variable_mt = {
__metatable = "WML variable proxy"
}
local function get_variable_proxy(k)
local v = wesnoth.get_variable(k, true)
if type(v) == "table" then
v = setmetatable({ __varname = k }, variable_mt)
end
return v
end
local function set_variable_proxy(k, v)
if getmetatable(v) == variable_mt then
v = wesnoth.get_variable(v.__varname)
end
wesnoth.set_variable(k, v)
end
function variable_mt.__index(t, k)
local i = tonumber(k)
if i then
k = t.__varname .. '[' .. i .. ']'
else
k = t.__varname .. '.' .. k
end
return get_variable_proxy(k)
end
function variable_mt.__newindex(t, k, v)
local i = tonumber(k)
if i then
k = t.__varname .. '[' .. i .. ']'
else
k = t.__varname .. '.' .. k
end
set_variable_proxy(k, v)
end
local root_variable_mt = {
-- Metatable that redirects access to wml.variable.proxy
local proxy_var_mt = {
__metatable = "WML variables",
__index = function(t, k) return get_variable_proxy(k) end,
__newindex = function(t, k, v)
if type(v) == "function" then
-- User-friendliness when _G is overloaded early.
-- FIXME: It should be disabled outside the "preload" event.
rawset(t, k, v)
else
set_variable_proxy(k, v)
end
end
__index = function(t, k) return wml.variable.proxy[k] end,
__newindex = function(t, k, v) wml.variable.proxy[k] = v end,
}
--! Sets the metatable of @a t so that it can be used to access WML variables.
--! @return @a t.
--! @code
--! helper.set_wml_var_metatable(_G)
--! my_persistent_variable = 42
--! @endcode
function helper.set_wml_var_metatable(t)
return setmetatable(t, root_variable_mt)
return setmetatable(t, proxy_var_mt)
end
local fire_action_mt = {
@ -255,80 +127,14 @@ function helper.set_wml_action_metatable(t)
return setmetatable(t, fire_action_mt)
end
local create_tag_mt = {
-- Metatable that redirects to wml.tag
local proxy_tag_mt = {
__metatable = "WML tag builder",
__index = function(t, n)
return function(cfg) return { n, cfg } end
end
__index = function(t, n) return wml.tag[n] end
}
--! Sets the metatable of @a t so that it can be used to create subtags with less brackets.
--! @return @a t.
--! @code
--! T = helper.set_wml_tag_metatable {}
--! W.event { name = "new turn", T.message { speaker = "narrator", message = "?" } }
--! @endcode
function helper.set_wml_tag_metatable(t)
return setmetatable(t, create_tag_mt)
end
local function resolve_variable_context(ctx, err_hint)
if ctx == nil then
return {get = wesnoth.get_variable, set = wesnoth.set_variable}
elseif type(ctx) == 'number' and ctx > 0 and ctx <= #wesnoth.sides then
return resolve_variable_context(wesnoth.sides[ctx])
elseif type(ctx) == 'string' then
-- TODO: Treat it as a namespace for a global (persistent) variable
-- (Need Lua API for accessing them first, though.)
elseif getmetatable(ctx) == "unit" then
return {
get = function(path) return ctx.variables[path] end,
set = function(path, val) ctx.variables[path] = val end,
}
elseif getmetatable(ctx) == "side" then
return {
get = function(path) return wesnoth.get_side_variable(ctx.side, path) end,
set = function(path, val) wesnoth.set_side_variable(ctx.side, path, val) end,
}
elseif getmetatable(ctx) == "unit variables" or getmetatable(ctx) == "side variables" then
return {
get = function(path) return ctx[path] end,
set = function(path, val) ctx[path] = val end,
}
end
error(string.format("Invalid context for %s: expected nil, side, or unit", err_hint), 3)
end
--! Fetches all the WML container variables with name @a var.
--! @returns a table containing all the variables (starting at index 1).
function helper.get_variable_array(var, context)
context = resolve_variable_context(context, "get_variable_array")
local result = {}
for i = 1, context.get(var .. ".length") do
result[i] = context.get(string.format("%s[%d]", var, i - 1))
end
return result
end
--! Puts all the elements of table @a t inside a WML container with name @a var.
function helper.set_variable_array(var, t, context)
context = resolve_variable_context(context, "set_variable_array")
context.set(var)
for i, v in ipairs(t) do
context.set(string.format("%s[%d]", var, i - 1), v)
end
end
--! Creates proxies for all the WML container variables with name @a var.
--! This is similar to helper.get_variable_array, except that the elements
--! can be used for writing too.
--! @returns a table containing all the variable proxies (starting at index 1).
function helper.get_variable_proxy_array(var)
local result = {}
for i = 1, wesnoth.get_variable(var .. ".length") do
result[i] = get_variable_proxy(string.format("%s[%d]", var, i - 1))
end
return result
return setmetatable(t, proxy_tag_mt)
end
--! Displays a WML message box with attributes from table @attr and options
@ -394,38 +200,6 @@ function helper.adjacent_tiles(x, y, with_borders)
end
end
function helper.literal(cfg)
if type(cfg) == "userdata" then
return cfg.__literal
else
return cfg or {}
end
end
function helper.parsed(cfg)
if type(cfg) == "userdata" then
return cfg.__parsed
else
return cfg or {}
end
end
function helper.shallow_literal(cfg)
if type(cfg) == "userdata" then
return cfg.__shallow_literal
else
return cfg or {}
end
end
function helper.shallow_parsed(cfg)
if type(cfg) == "userdata" then
return cfg.__shallow_parsed
else
return cfg or {}
end
end
function helper.rand (possible_values, random_func)
random_func = random_func or wesnoth.random
assert(type(possible_values) == "table" or type(possible_values) == "string", string.format("helper.rand expects a string or table as parameter, got %s instead", type(possible_values)))
@ -541,4 +315,51 @@ function helper.shuffle( t, random_func)
end
end
-- Compatibility and deprecations
helper.get_child = helper.deprecate(
"helper.get_child is deprecated; use wml.get_child instead", wml.get_child)
helper.get_nth_child = helper.deprecate(
"helper.get_nth_child is deprecated; use wml.get_nth_child instead", wml.get_nth_child)
helper.child_count = helper.deprecate(
"helper.child_count is deprecated; use wml.child_count instead", wml.child_count)
helper.child_range = helper.deprecate(
"helper.child_range is deprecated; use wml.child_range instead", wml.child_range)
helper.child_array = helper.deprecate(
"helper.child_array is deprecated; use wml.child_array instead", wml.child_array)
helper.get_variable_array = helper.deprecate(
"helper.get_variable_array is deprecated; use wml.variable.get_array instead",
wml.variable.get_array)
helper.set_variable_array = helper.deprecate(
"helper.set_variable_array is deprecated; use wml.variable.set_array instead",
wml.variable.set_array)
helper.get_variable_proxy_array = helper.deprecate(
"helper.get_variable_proxy_array is deprecated; use wml.variable.get_proxy_array instead",
wml.variable.get_proxy_array)
helper.literal = helper.deprecate(
"helper.literal is deprecated; use wml.literal instead", wml.literal)
helper.parsed = helper.deprecate(
"helper.parsed is deprecated; use wml.parsed instead", wml.parsed)
helper.shallow_literal = helper.deprecate(
"helper.shallow_literal is deprecated; use wml.shallow_literal instead", wml.shallow_literal)
helper.shallow_parsed = helper.deprecate(
"helper.shallow_parsed is deprecated; use wml.shallow_parsed instead", wml.shallow_parsed)
helper.set_wml_var_metatable = helper.deprecate(
"helper.set_wml_var_metatable is deprecated; use wml.variable.proxy instead " ..
"which has the metatable already set", helper.set_wml_var_metatable)
helper.set_wml_tag_metatable = helper.deprecate(
"helper.set_wml_tag_metatable is deprecated; use wml.tag instead " ..
"which has the metatable already set", helper.set_wml_tag_metatable)
wesnoth.get_variable = helper.deprecate(
"wesnoth.get_variable is deprecated; use wml.variable.get instead", wesnoth.get_variable)
wesnoth.set_variable = helper.deprecate(
"wesnoth.set_variable is deprecated; use wml.variable.set instead", wesnoth.set_variable)
wesnoth.get_all_vars = helper.deprecate(
"wesnoth.get_all_vars is deprecated; use wml.variable.get_all instead", wesnoth.get_all_vars)
wesnoth.tovconfig = helper.deprecate(
"wesnoth.tovconfig is deprecated; use wml.tovconfig instead", wesnoth.tovconfig)
wesnoth.debug = helper.deprecate(
"wesnoth.debug is deprecated; use wml.tostring instead", wesnoth.debug)
return helper