diff --git a/data/lua/wml-tags.lua b/data/lua/wml-tags.lua index 09d49e1d68c..2f420f340e7 100644 --- a/data/lua/wml-tags.lua +++ b/data/lua/wml-tags.lua @@ -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 diff --git a/data/lua/wml/modify_side.lua b/data/lua/wml/modify_side.lua index 6cfdf7891cc..19c38c998ab 100644 --- a/data/lua/wml/modify_side.lua +++ b/data/lua/wml/modify_side.lua @@ -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) diff --git a/data/lua/wml/modify_unit.lua b/data/lua/wml/modify_unit.lua index 5b83434fc7c..6ea3ef6c653 100644 --- a/data/lua/wml/modify_unit.lua +++ b/data/lua/wml/modify_unit.lua @@ -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)] = {} diff --git a/data/lua/wml/set_variable.lua b/data/lua/wml/set_variable.lua index 6824250b559..adeb7fb7c7d 100644 --- a/data/lua/wml/set_variable.lua +++ b/data/lua/wml/set_variable.lua @@ -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 diff --git a/src/scripting/lua_team.cpp b/src/scripting/lua_team.cpp index 18f0d8062db..3603e59f936 100644 --- a/src/scripting/lua_team.cpp +++ b/src/scripting/lua_team.cpp @@ -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(); } }