Support [set/clear_variable] inside [modify_unit/side]

This commit is contained in:
Celtic Minstrel 2019-11-11 22:29:26 -05:00
parent 6371f0497c
commit f6b6854c8d
5 changed files with 145 additions and 45 deletions

View file

@ -105,11 +105,12 @@ function wml_actions.store_gold(cfg)
if team then wml.variables[cfg.variable or "gold"] = team.gold end
end
function wml_actions.clear_variable(cfg)
function wml_actions.clear_variable(cfg, variables)
local names = cfg.name or
helper.wml_error "[clear_variable] missing required name= attribute."
if variables == nil then variables = wml.variables end
for w in utils.split(names) do
wml.variables[utils.trim(w)] = nil
variables[utils.trim(w)] = nil
end
end

View file

@ -91,16 +91,24 @@ function wesnoth.wml_actions.modify_side(cfg)
end
end
local ai, replace_ai = {}, false
for k, v in ipairs(cfg) do
local tag, content = v[1], v[2]
if tag == "ai" then
table.insert(ai, T.ai(content))
if content.ai_algorithm then
replace_ai = true
end
elseif tag == "set_variable" then
wesnoth.wml_actions.set_variable(v[2], side.variables)
elseif tag == "clear_variable" then
wesnoth.wml_actions.clear_variable(cfg, side.variables)
end
end
if cfg.switch_ai then
wesnoth.switch_ai(side.side, cfg.switch_ai)
end
local ai, replace_ai = {}, false
for next_ai in wml.child_range(cfg, "ai") do
table.insert(ai, T.ai(next_ai))
if next_ai.ai_algorithm then
replace_ai = true
end
end
if #ai > 0 then
if replace_ai then
wesnoth.switch_ai(side.side, ai)

View file

@ -55,6 +55,9 @@ local known_tags = make_set {
"trait",
"filter",
"status",
"set_variable",
-- todo: "set_variables",
"clear_variable",
}
local function is_simple(cfg)
@ -137,11 +140,14 @@ local function simple_modify_unit(cfg)
tagcontent = wml.parsed(tagcontent)
end
u:add_modification(tagname, tagcontent);
end
if tagname == "status" then
elseif tagname == "status" then
for i, v in pairs(tagcontent) do
u.status[i] = v
end
elseif tagname == "set_variable" then
wesnoth.wml_actions.set_variable(tagcontent, u.variables)
elseif tagname == "clear_variable" then
wesnoth.wml_actions.clear_variable(tagcontent, u.variables)
end
end
@ -233,6 +239,14 @@ function wml_actions.modify_unit(cfg)
else
helper.wml_error("[modify_unit] had invalid [effect]apply_to value")
end
elseif current_tag == "set_variable" then
local unit = wesnoth.create_unit(wml.variables[unit_path])
wesnoth.wml_actions.set_variable(current_table[2], unit.variables)
wml.variables[unit_path] = unit.__cfg
elseif current_tag == "clear_variable" then
local unit = wesnoth.create_unit(wml.variables[unit_path])
wesnoth.wml_actions.clear_variable(current_table[2], unit.variables)
wml.variables[unit_path] = unit.__cfg
else
if replace_mode then
wml.variables[string.format("%s.%s", unit_path, current_tag)] = {}

View file

@ -1,54 +1,55 @@
local helper = wesnoth.require "helper"
function wesnoth.wml_actions.set_variable(cfg)
local name = cfg.name or helper.wml_error "trying to set a variable with an empty name"
function wesnoth.wml_actions.set_variable(cfg, variables)
local name = cfg.name or wml.error "trying to set a variable with an empty name"
if variables == nil then variables = wml.variables end
if cfg.value ~= nil then -- check for nil because user may try to set a variable as false
wml.variables[name] = cfg.value
variables[name] = cfg.value
end
if cfg.literal ~= nil then
wml.variables[name] = wml.shallow_literal(cfg).literal
variables[name] = wml.shallow_literal(cfg).literal
end
if cfg.to_variable then
wml.variables[name] = wml.variables[cfg.to_variable]
variables[name] = variables[cfg.to_variable]
end
if cfg.suffix then
wml.variables[name] = (wml.variables[name] or '') .. (cfg.suffix or '')
variables[name] = (variables[name] or '') .. (cfg.suffix or '')
end
if cfg.prefix then
wml.variables[name] = (cfg.prefix or '') .. (wml.variables[name] or '')
variables[name] = (cfg.prefix or '') .. (variables[name] or '')
end
if cfg.add then
wml.variables[name] = (tonumber(wml.variables[name]) or 0) + (tonumber(cfg.add) or 0)
variables[name] = (tonumber(variables[name]) or 0) + (tonumber(cfg.add) or 0)
end
if cfg.sub then
wml.variables[name] = (tonumber(wml.variables[name]) or 0) - (tonumber(cfg.sub) or 0)
variables[name] = (tonumber(variables[name]) or 0) - (tonumber(cfg.sub) or 0)
end
if cfg.multiply then
wml.variables[name] = (tonumber(wml.variables[name]) or 0) * (tonumber(cfg.multiply) or 0)
variables[name] = (tonumber(variables[name]) or 0) * (tonumber(cfg.multiply) or 0)
end
if cfg.divide then
local divide = tonumber(cfg.divide) or 0
if divide == 0 then helper.wml_error("division by zero on variable " .. name) end
wml.variables[name] = (tonumber(wml.variables[name]) or 0) / divide
if divide == 0 then wml.error("division by zero on variable " .. name) end
variables[name] = (tonumber(variables[name]) or 0) / divide
end
if cfg.modulo then
local modulo = tonumber(cfg.modulo) or 0
if modulo == 0 then helper.wml_error("division by zero on variable " .. name) end
wml.variables[name] = (tonumber(wml.variables[name]) or 0) % modulo
if modulo == 0 then wml.error("division by zero on variable " .. name) end
variables[name] = (tonumber(variables[name]) or 0) % modulo
end
if cfg.abs then
wml.variables[name] = math.abs(tonumber(wml.variables[name]) or 0)
variables[name] = math.abs(tonumber(variables[name]) or 0)
end
if cfg.root then
@ -64,40 +65,40 @@ function wesnoth.wml_actions.set_variable(cfg)
root_fcn = function(n) return n ^ (1 / root) end
end
local radicand = tonumber(wml.variables[name]) or 0
local radicand = tonumber(variables[name]) or 0
if radicand < 0 and root % 2 == 0 then
if root == 2 then
helper.wml_error("square root of negative number on variable " .. name)
wml.error("square root of negative number on variable " .. name)
else
helper.wml_error(string.format("%dth root of negative number on variable %s", root, name))
wml.error(string.format("%dth root of negative number on variable %s", root, name))
end
end
wml.variables[name] = root_fcn(radicand)
variables[name] = root_fcn(radicand)
end
if cfg.power then
wml.variables[name] = (tonumber(wml.variables[name]) or 0) ^ (tonumber(cfg.power) or 0)
variables[name] = (tonumber(variables[name]) or 0) ^ (tonumber(cfg.power) or 0)
end
if cfg.round then
local var = tonumber(wml.variables[name] or 0)
local var = tonumber(variables[name] or 0)
local round_val = cfg.round
if round_val == "ceil" then
wml.variables[name] = math.ceil(var)
variables[name] = math.ceil(var)
elseif round_val == "floor" then
wml.variables[name] = math.floor(var)
variables[name] = math.floor(var)
elseif round_val == "trunc" then
-- Storing to a variable first because modf returns two values,
-- and I'm not sure if set_variable will complain about the extra parameter
local new_val = math.modf(var)
wml.variables[name] = new_val
variables[name] = new_val
else
local decimals = math.modf(tonumber(round_val) or 0)
local value = var * (10 ^ decimals)
value = helper.round(value)
value = value * (10 ^ -decimals)
wml.variables[name] = value
variables[name] = value
end
end
@ -106,36 +107,36 @@ function wesnoth.wml_actions.set_variable(cfg)
-- but on the value assigned to the respective key
if cfg.ipart then
local ivalue = math.modf(tonumber(cfg.ipart) or 0)
wml.variables[name] = ivalue
variables[name] = ivalue
end
if cfg.fpart then
local ivalue, fvalue = math.modf(tonumber(cfg.fpart) or 0)
wml.variables[name] = fvalue
variables[name] = fvalue
end
if cfg.string_length ~= nil then
wml.variables[name] = string.len(tostring(cfg.string_length))
variables[name] = string.len(tostring(cfg.string_length))
end
if cfg.time then
if cfg.time == "stamp" then
wml.variables[name] = wesnoth.get_time_stamp()
variables[name] = wesnoth.get_time_stamp()
end
end
if cfg.rand then
wml.variables[name] = helper.rand(tostring(cfg.rand))
variables[name] = helper.rand(tostring(cfg.rand))
end
if cfg.formula then
local fcn = wesnoth.compile_formula(cfg.formula)
wml.variables[name] = fcn(wml.variables[name])
variables[name] = fcn(variables[name])
end
local join_child = wml.get_child(cfg, "join")
if join_child then
local array_name = join_child.variable or helper.wml_error "missing variable= attribute in [join]"
local array_name = join_child.variable or wml.error "missing variable= attribute in [join]"
local separator = join_child.separator
local key_name = join_child.key or "value"
local remove_empty = join_child.remove_empty
@ -151,6 +152,6 @@ function wesnoth.wml_actions.set_variable(cfg)
end
end
wml.variables[name] = string_to_join
variables[name] = string_to_join
end
end

View file

@ -40,6 +40,7 @@
// Registry key
static const char * Team = "side";
static const char teamVar[] = "side variables";
/**
* Gets some data on a side (__index metamethod).
@ -102,6 +103,13 @@ static int impl_side_get(lua_State *L)
}
return 1;
}
if(strcmp(m, "variables") == 0) {
lua_createtable(L, 1, 0);
lua_pushvalue(L, 1);
lua_rawseti(L, -2, 1);
luaL_setmetatable(L, teamVar);
return 1;
}
// These are blocked together because they are all part of the team_data struct.
// Some of these values involve iterating over the units map to calculate them.
@ -212,10 +220,67 @@ static int impl_side_equal(lua_State *L)
return 1;
}
/**
* Gets the variable of a side (__index metamethod).
* - Arg 1: table containing the userdata containing the side id.
* - Arg 2: string containing the name of the status.
* - Ret 1: boolean.
*/
static int impl_side_variables_get(lua_State *L)
{
if(!lua_istable(L, 1)) {
return luaW_type_error(L, 1, "side variables");
}
lua_rawgeti(L, 1, 1);
const team& side = luaW_checkteam(L, -1);
char const *m = luaL_checkstring(L, 2);
return_cfgref_attrib("__cfg", side.variables());
variable_access_const v(m, side.variables());
return luaW_pushvariable(L, v) ? 1 : 0;
}
/**
* Sets the variable of a side (__newindex metamethod).
* - Arg 1: table containing the userdata containing the side id.
* - Arg 2: string containing the name of the status.
* - Arg 3: scalar.
*/
static int impl_side_variables_set(lua_State *L)
{
if(!lua_istable(L, 1)) {
return luaW_type_error(L, 1, "side variables");
}
lua_rawgeti(L, 1, 1);
team& side = luaW_checkteam(L, -1);
char const *m = luaL_checkstring(L, 2);
if(strcmp(m, "__cfg") == 0) {
side.variables() = luaW_checkconfig(L, 3);
return 0;
}
config& vars = side.variables();
if(lua_isnoneornil(L, 3)) {
try {
variable_access_throw(m, vars).clear(false);
} catch(const invalid_variablename_exception&) {
}
return 0;
}
variable_access_create v(m, vars);
luaW_checkvariable(L, v, 3);
return 0;
}
namespace lua_team {
std::string register_metatable(lua_State * L)
{
std::ostringstream cmd_out;
cmd_out << "Adding getside metatable...\n";
luaL_newmetatable(L, Team);
static luaL_Reg const callbacks[] {
@ -232,7 +297,18 @@ namespace lua_team {
luaW_getglobal(L, "wesnoth", "match_side");
lua_setfield(L, -2, "matches");
return "Adding getside metatable...\n";
// Create the side variables metatable.
cmd_out << "Adding side variables metatable...\n";
luaL_newmetatable(L, teamVar);
lua_pushcfunction(L, impl_side_variables_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, impl_side_variables_set);
lua_setfield(L, -2, "__newindex");
lua_pushstring(L, "side variables");
lua_setfield(L, -2, "__metatable");
return cmd_out.str();
}
}