Ported [set_variable] to Lua

This commit is contained in:
Elvish_Hunter 2016-04-04 21:23:00 +02:00
parent 0a7190c605
commit d6747f5098
2 changed files with 107 additions and 238 deletions

View file

@ -1787,3 +1787,110 @@ wml_actions.teleport = function(cfg)
end
wesnoth.teleport(unit, cfg.check_passability == false, cfg.clear_shroud ~= false, cfg.animate)
end
function wml_actions.set_variable(cfg)
local name = cfg.name or helper.wml_error "trying to set a variable with an empty name"
local var = wesnoth.get_variable(name)
if cfg.value ~= nil then -- check for nil because user may try to set a variable as false
wesnoth.set_variable(name, cfg.__shallow_parsed.value)
elseif cfg.literal ~= nil then
wesnoth.set_variable(name, cfg.__shallow_literal.literal)
elseif cfg.to_variable then
wesnoth.set_variable(name, wesnoth.get_variable(cfg.to_variable))
elseif cfg.add then
wesnoth.set_variable(name, (tonumber(var) or 0) + (tonumber(cfg.add) or 0))
elseif cfg.sub then
wesnoth.set_variable(name, (tonumber(var) or 0) - (tonumber(cfg.sub) or 0))
elseif cfg.multiply then
wesnoth.set_variable(name, (tonumber(var) or 0) * (tonumber(cfg.multiply) or 0))
elseif cfg.divide then
local divide = tonumber(cfg.divide) or 0
if divide == 0 then helper.wml_error("division by zero on variable " .. name) end
wesnoth.set_variable(name, (tonumber(var) or 0) / divide)
elseif cfg.modulo then
local modulo = tonumber(cfg.modulo) or 0
if modulo == 0 then helper.wml_error("division by zero on variable " .. name) end
wesnoth.set_variable(name, (tonumber(var) or 0) % modulo)
elseif cfg.round then
local round_val = cfg.round
if round_val == "ceil" then
wesnoth.set_variable(name, math.ceil(tonumber(var) or 0))
elseif round_val == "floor" then
wesnoth.set_variable(name, math.floor(tonumber(var) or 0))
else
local decimals, discarded = math.modf(tonumber(round_val) or 0)
local value = (tonumber(var) or 0) * (10 ^ decimals)
value = helper.round(value)
value = value * (10 ^ -decimals)
wesnoth.set_variable(name, value)
end
-- unlike the other math operations, ipart and fpart do not act on
-- the value already contained in the variable
-- but on the value assigned to the respective key
elseif cfg.ipart then
local ivalue, fvalue = math.modf(tonumber(cfg.ipart) or 0)
wesnoth.set_variable(name, ivalue)
elseif cfg.fpart then
local ivalue, fvalue = math.modf(tonumber(cfg.fpart) or 0)
wesnoth.set_variable(name, fvalue)
elseif cfg.string_length ~= nil then
wesnoth.set_variable(name, string.len(tostring(cfg.string_length)))
elseif cfg.time then
if cfg.time == "stamp" then
wesnoth.set_variable(name, wesnoth.get_time_stamp())
end
elseif cfg.rand then
local random_string = cfg.rand
local choices = {}
-- split on commas
for word in random_string:gmatch("[^,]+") do
-- does the word contain two dots? If yes, that's a range
local dots_start, dots_end = word:find("%.%.")
if dots_start then
-- split on the dots if so and cast as numbers
local low = tonumber(word:sub(1, dots_start-1))
local high = tonumber(word:sub(dots_end+1))
-- perhaps someone passed a string as part of the range, intercept the issue
if not (low and high) then
wesnoth.message("<WML>","Malformed range: rand " .. cfg.rand)
table.insert(choices, word)
else
if low > high then
-- low is greater than high, swap them
low, high = high, low
end
-- insert all the values in range into choices (both ends included)
for value = low, high do
table.insert(choices, value)
end
end
else
-- handle as a string
table.insert(choices, word)
end
end
local idx = wesnoth.random(1, #choices)
wesnoth.set_variable(name, choices[idx])
else
local join_child = helper.get_child(cfg, "join")
if not join_child then return end
local array_name = join_child.variable or helper.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
local string_to_join = {}
for i, element in ipairs(helper.get_variable_array(array_name)) do
if element[key_name] ~= nil or (not remove_empty) then
table.insert(string_to_join, tostring(element[key_name]))
end
end
wesnoth.set_variable(name, table.concat(string_to_join, separator))
end
end

View file

@ -689,244 +689,6 @@ WML_HANDLER_FUNCTION(set_global_variable, pcfg)
verify_and_set_global_variable(pcfg);
}
WML_HANDLER_FUNCTION(set_variable, cfg)
{
game_data *gameinfo = resources::gamedata;
const std::string name = cfg["name"];
const std::string to_variable = cfg["to_variable"];
try
{
if(name.empty()) {
ERR_NG << "trying to set a variable with an empty name:\n" << cfg.get_config().debug();
return;
}
config::attribute_value &var = gameinfo->get_variable(name);
config::attribute_value literal = cfg.get_config()["literal"]; // no $var substitution
if (!literal.blank()) {
var = literal;
}
config::attribute_value value = cfg["value"];
if (!value.blank()) {
var = value;
}
if(to_variable.empty() == false) {
var = gameinfo->get_variable_access_read(to_variable).as_scalar();
}
config::attribute_value add = cfg["add"];
if (!add.empty()) {
var = var.to_double() + add.to_double();
}
config::attribute_value sub = cfg["sub"];
if (!sub.empty()) {
var = var.to_double() - sub.to_double();
}
config::attribute_value multiply = cfg["multiply"];
if (!multiply.empty()) {
var = var.to_double() * multiply.to_double();
}
config::attribute_value divide = cfg["divide"];
if (!divide.empty()) {
if (divide.to_double() == 0) {
ERR_NG << "division by zero on variable " << name << std::endl;
return;
}
var = var.to_double() / divide.to_double();
}
config::attribute_value modulo = cfg["modulo"];
if (!modulo.empty()) {
if (modulo.to_double() == 0) {
ERR_NG << "division by zero on variable " << name << std::endl;
return;
}
var = std::fmod(var.to_double(), modulo.to_double());
}
config::attribute_value round_val = cfg["round"];
if (!round_val.empty()) {
double value = var.to_double();
if (round_val == "ceil") {
var = int(std::ceil(value));
} else if (round_val == "floor") {
var = int(std::floor(value));
} else {
// We assume the value is an integer.
// Any non-numerical values will be interpreted as 0
// Which is probably what was intended anyway
int decimals = round_val.to_int();
value *= std::pow(10.0, decimals); //add $decimals zeroes
value = round_portable(value); // round() isn't implemented everywhere
value *= std::pow(10.0, -decimals); //and remove them
var = value;
}
}
config::attribute_value ipart = cfg["ipart"];
if (!ipart.empty()) {
double result;
std::modf(ipart.to_double(), &result);
var = int(result);
}
config::attribute_value fpart = cfg["fpart"];
if (!fpart.empty()) {
double ignore;
var = std::modf(fpart.to_double(), &ignore);
}
config::attribute_value string_length_target = cfg["string_length"];
if (!string_length_target.blank()) {
var = int(string_length_target.str().size());
}
// Note: maybe we add more options later, eg. strftime formatting.
// For now make the stamp mandatory.
const std::string time = cfg["time"];
if(time == "stamp") {
var = int(SDL_GetTicks());
}
// Random generation works as follows:
// rand=[comma delimited list]
// Each element in the list will be considered a separate choice,
// unless it contains "..". In this case, it must be a numerical
// range (i.e. -1..-10, 0..100, -10..10, etc).
const std::string rand = cfg["rand"];
// The new random generator, the logic is a copy paste of the old random.
if(rand.empty() == false) {
assert(gameinfo);
// A default value in case something goes really wrong.
var = "";
std::string word;
std::vector<std::string> words;
std::vector<std::pair<long,long> > ranges;
long num_choices = 0;
std::string::size_type pos = 0, pos2 = std::string::npos;
std::stringstream ss(std::stringstream::in|std::stringstream::out);
while (pos2 != rand.length()) {
pos = pos2+1;
pos2 = rand.find(",", pos);
if (pos2 == std::string::npos)
pos2 = rand.length();
word = rand.substr(pos, pos2-pos);
words.push_back(word);
std::string::size_type tmp = word.find("..");
if (tmp == std::string::npos) {
// Treat this element as a string
ranges.push_back(std::pair<long, long>(0,0));
num_choices += 1;
}
else {
// Treat as a numerical range
const std::string first = word.substr(0, tmp);
const std::string second = word.substr(tmp+2,
rand.length());
long low, high;
ss << first + " " + second;
if ( !(ss >> low) || !(ss >> high) ) {
ERR_NG << "Malformed range: rand = \"" << rand << "\"" << std::endl;
// Treat this element as a string?
ranges.push_back(std::pair<long, long>(0,0));
num_choices += 1;
}
else {
if (low > high) {
std::swap(low, high);
}
ranges.push_back(std::pair<long, long>(low,high));
num_choices += (high - low) + 1;
// Make 0..0 ranges work
if (high == 0 && low == 0) {
words.pop_back();
words.push_back("0");
}
}
ss.clear();
}
}
assert(num_choices > 0);
// On most plattforms long can never hold a bigger value than a uint32_t, but there are exceptions where long is 64 bit.
if(static_cast<unsigned long>(num_choices) > std::numeric_limits<uint32_t>::max()) {
WRN_NG << "Requested random number with an upper bound of "
<< num_choices
<< " however the maximum number generated will be "
<< std::numeric_limits<uint32_t>::max()
<< ".\n";
}
uint32_t choice = random_new::generator->next_random() % num_choices;
uint32_t tmp = 0;
for(size_t i = 0; i < ranges.size(); ++i) {
tmp += (ranges[i].second - ranges[i].first) + 1;
if (tmp > choice) {
if (ranges[i].first == 0 && ranges[i].second == 0) {
var = words[i];
}
else {
var = (ranges[i].second - (tmp - choice)) + 1;
}
break;
}
}
}
const vconfig::child_list join_elements = cfg.get_children("join");
if(!join_elements.empty())
{
const vconfig & join_element = join_elements.front();
std::string array_name=join_element["variable"];
std::string separator=join_element["separator"];
std::string key_name=join_element["key"];
if(key_name.empty())
{
key_name="value";
}
bool remove_empty = join_element["remove_empty"].to_bool();
std::string joined_string;
variable_access_const vi = resources::gamedata->get_variable_access_read(array_name);
bool first = true;
for (const config &cfg : vi.as_array())
{
std::string current_string = cfg[key_name];
if (remove_empty && current_string.empty()) continue;
if (first) first = false;
else joined_string += separator;
joined_string += current_string;
}
var = joined_string;
}
}
catch(const invalid_variablename_exception&)
{
ERR_NG << "Found invalid variablename in \n[set_variable]\n" << cfg.get_config().debug() << "[/set_variable]\n where name = " << name << " to_variable = " << to_variable << "\n";
}
}
WML_HANDLER_FUNCTION(set_variables, cfg)
{
const t_string& name = cfg["name"];