Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
* API removals:
|
||||
* \li AIError::ERR_PRECONDITION_TOO_MANY_PARAMETERS, that error is never returned anymore.
|
||||
* \li AIInfo::CONFIG_RANDOM, no longer used.
|
||||
* \li AIInfo::AddSettings random_deviation is no longer used.
|
||||
*
|
||||
* Other changes:
|
||||
* \li AIGroupList accepts an optional filter function
|
||||
|
||||
@@ -93,6 +93,7 @@
|
||||
* API removals:
|
||||
* \li GSError::ERR_PRECONDITION_TOO_MANY_PARAMETERS, that error is never returned anymore.
|
||||
* \li GSInfo::CONFIG_RANDOM, no longer used.
|
||||
* \li GSInfo::AddSettings random_deviation is no longer used.
|
||||
*
|
||||
* Other changes:
|
||||
* \li GSGroupList accepts an optional filter function
|
||||
|
||||
@@ -224,12 +224,6 @@ public:
|
||||
* clamped in the range [MIN(int32_t), MAX(int32_t)] (inclusive).
|
||||
* - default_value The default value. Required. The value will be
|
||||
* clamped in the range [MIN(int32_t), MAX(int32_t)] (inclusive).
|
||||
* - random_deviation If this property has a nonzero value, then the
|
||||
* actual value of the setting in game will be randomised in the range
|
||||
* [user_configured_value - random_deviation, user_configured_value + random_deviation] (inclusive).
|
||||
* random_deviation sign is ignored and the value is clamped in the range [0, MAX(int32_t)] (inclusive).
|
||||
* The randomisation will happen just before the Script start.
|
||||
* Not allowed if the CONFIG_BOOLEAN flag is set, otherwise optional.
|
||||
* - step_size The increase/decrease of the value every time the user
|
||||
* clicks one of the up/down arrow buttons. Optional, default is 1.
|
||||
* - flags Bitmask of some flags, see ScriptConfigFlags. Required.
|
||||
|
||||
@@ -160,45 +160,62 @@ SQInteger ScriptText::_set(HSQUIRRELVM vm)
|
||||
|
||||
std::string ScriptText::GetEncodedText()
|
||||
{
|
||||
StringIDList seen_ids;
|
||||
ScriptTextList seen_texts;
|
||||
ParamList params;
|
||||
int param_count = 0;
|
||||
std::string result;
|
||||
auto output = std::back_inserter(result);
|
||||
this->_FillParamList(params);
|
||||
this->_GetEncodedText(output, param_count, seen_ids, params);
|
||||
this->_FillParamList(params, seen_texts);
|
||||
this->_GetEncodedText(output, param_count, params, true);
|
||||
if (param_count > SCRIPT_TEXT_MAX_PARAMETERS) throw Script_FatalError(fmt::format("{}: Too many parameters", GetGameStringName(this->string)));
|
||||
return result;
|
||||
}
|
||||
|
||||
void ScriptText::_FillParamList(ParamList ¶ms)
|
||||
void ScriptText::_FillParamList(ParamList ¶ms, ScriptTextList &seen_texts)
|
||||
{
|
||||
if (std::find(seen_texts.begin(), seen_texts.end(), this) != seen_texts.end()) throw Script_FatalError(fmt::format("{}: Circular reference detected", GetGameStringName(this->string)));
|
||||
seen_texts.push_back(this);
|
||||
|
||||
for (int i = 0; i < this->paramc; i++) {
|
||||
Param *p = &this->param[i];
|
||||
params.emplace_back(this->string, i, p);
|
||||
if (!std::holds_alternative<ScriptTextRef>(*p)) continue;
|
||||
std::get<ScriptTextRef>(*p)->_FillParamList(params);
|
||||
std::get<ScriptTextRef>(*p)->_FillParamList(params, seen_texts);
|
||||
}
|
||||
|
||||
seen_texts.pop_back();
|
||||
|
||||
/* Fill with dummy parameters to match FormatString() behaviour. */
|
||||
if (seen_texts.empty()) {
|
||||
static Param dummy = 0;
|
||||
int nb_extra = SCRIPT_TEXT_MAX_PARAMETERS - (int)params.size();
|
||||
for (int i = 0; i < nb_extra; i++)
|
||||
params.emplace_back(-1, i, &dummy);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptText::ParamCheck::Encode(std::back_insert_iterator<std::string> &output)
|
||||
void ScriptText::ParamCheck::Encode(std::back_insert_iterator<std::string> &output, const char *cmd)
|
||||
{
|
||||
if (this->cmd == nullptr) this->cmd = cmd;
|
||||
if (this->used) return;
|
||||
if (std::holds_alternative<std::string>(*this->param)) fmt::format_to(output, ":\"{}\"", std::get<std::string>(*this->param));
|
||||
if (std::holds_alternative<SQInteger>(*this->param)) fmt::format_to(output, ":{:X}", std::get<SQInteger>(*this->param));
|
||||
if (std::holds_alternative<ScriptTextRef>(*this->param)) fmt::format_to(output, ":{:X}", this->owner);
|
||||
if (std::holds_alternative<ScriptTextRef>(*this->param)) {
|
||||
fmt::format_to(output, ":");
|
||||
Utf8Encode(output, SCC_ENCODED);
|
||||
fmt::format_to(output, "{:X}", std::get<ScriptTextRef>(*this->param)->string);
|
||||
}
|
||||
this->used = true;
|
||||
}
|
||||
|
||||
void ScriptText::_GetEncodedText(std::back_insert_iterator<std::string> &output, int ¶m_count, StringIDList &seen_ids, ParamSpan args)
|
||||
void ScriptText::_GetEncodedText(std::back_insert_iterator<std::string> &output, int ¶m_count, ParamSpan args, bool first)
|
||||
{
|
||||
const std::string &name = GetGameStringName(this->string);
|
||||
|
||||
if (std::find(seen_ids.begin(), seen_ids.end(), this->string) != seen_ids.end()) throw Script_FatalError(fmt::format("{}: Circular reference detected", name));
|
||||
seen_ids.push_back(this->string);
|
||||
|
||||
Utf8Encode(output, SCC_ENCODED);
|
||||
fmt::format_to(output, "{:X}", this->string);
|
||||
if (first) {
|
||||
Utf8Encode(output, SCC_ENCODED);
|
||||
fmt::format_to(output, "{:X}", this->string);
|
||||
}
|
||||
|
||||
const StringParams ¶ms = GetGameStringParams(this->string);
|
||||
|
||||
@@ -212,51 +229,58 @@ void ScriptText::_GetEncodedText(std::back_insert_iterator<std::string> &output,
|
||||
auto skip_args = [&](size_t nb) { idx += nb; };
|
||||
|
||||
for (const StringParam &cur_param : params) {
|
||||
switch (cur_param.type) {
|
||||
case StringParam::UNUSED:
|
||||
skip_args(cur_param.consumes);
|
||||
break;
|
||||
try {
|
||||
switch (cur_param.type) {
|
||||
case StringParam::UNUSED:
|
||||
skip_args(cur_param.consumes);
|
||||
break;
|
||||
|
||||
case StringParam::RAW_STRING: {
|
||||
ParamCheck &p = *get_next_arg();
|
||||
if (!std::holds_alternative<std::string>(*p.param)) ScriptLog::Error(fmt::format("{}({}): {{{}}} expects a raw string", name, param_count + 1, cur_param.cmd));
|
||||
p.Encode(output);
|
||||
break;
|
||||
}
|
||||
|
||||
case StringParam::STRING: {
|
||||
ParamCheck &p = *get_next_arg();
|
||||
if (!std::holds_alternative<ScriptTextRef>(*p.param)){
|
||||
ScriptLog::Error(fmt::format("{}({}): {{{}}} expects a GSText", name, param_count + 1, cur_param.cmd));
|
||||
p.Encode(output);
|
||||
case StringParam::RAW_STRING:
|
||||
{
|
||||
ParamCheck &p = *get_next_arg();
|
||||
p.Encode(output, cur_param.cmd);
|
||||
if (p.cmd != cur_param.cmd) throw 1;
|
||||
if (!std::holds_alternative<std::string>(*p.param)) ScriptLog::Error(fmt::format("{}({}): {{{}}} expects a raw string", name, param_count + 1, cur_param.cmd));
|
||||
break;
|
||||
}
|
||||
int count = 0;
|
||||
fmt::format_to(output, ":");
|
||||
ScriptTextRef &ref = std::get<ScriptTextRef>(*p.param);
|
||||
ref->_GetEncodedText(output, count, seen_ids, args.subspan(idx));
|
||||
p.used = true;
|
||||
if (++count != cur_param.consumes) {
|
||||
ScriptLog::Error(fmt::format("{}({}): {{{}}} expects {} to be consumed, but {} consumes {}", name, param_count + 1, cur_param.cmd, cur_param.consumes - 1, GetGameStringName(ref->string), count - 1));
|
||||
/* Fill missing params if needed. */
|
||||
for (int i = count; i < cur_param.consumes; i++) fmt::format_to(output, ":0");
|
||||
|
||||
case StringParam::STRING:
|
||||
{
|
||||
ParamCheck &p = *get_next_arg();
|
||||
p.Encode(output, cur_param.cmd);
|
||||
if (p.cmd != cur_param.cmd) throw 1;
|
||||
if (!std::holds_alternative<ScriptTextRef>(*p.param)) {
|
||||
ScriptLog::Error(fmt::format("{}({}): {{{}}} expects a GSText", name, param_count + 1, cur_param.cmd));
|
||||
param_count++;
|
||||
continue;
|
||||
}
|
||||
int count = 0;
|
||||
ScriptTextRef &ref = std::get<ScriptTextRef>(*p.param);
|
||||
ref->_GetEncodedText(output, count, args.subspan(idx), false);
|
||||
if (++count != cur_param.consumes) {
|
||||
ScriptLog::Warning(fmt::format("{}({}): {{{}}} expects {} to be consumed, but {} consumes {}", name, param_count + 1, cur_param.cmd, cur_param.consumes - 1, GetGameStringName(ref->string), count - 1));
|
||||
/* Fill missing params if needed. */
|
||||
for (int i = count; i < cur_param.consumes; i++) fmt::format_to(output, ":0");
|
||||
}
|
||||
skip_args(cur_param.consumes - 1);
|
||||
break;
|
||||
}
|
||||
skip_args(cur_param.consumes - 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
for (int i = 0; i < cur_param.consumes; i++) {
|
||||
ParamCheck &p = *get_next_arg();
|
||||
p.Encode(output, i == 0 ? cur_param.cmd : nullptr);
|
||||
if (i == 0 && p.cmd != cur_param.cmd) throw 1;
|
||||
if (!std::holds_alternative<SQInteger>(*p.param)) ScriptLog::Error(fmt::format("{}({}): {{{}}} expects an integer", name, param_count + i + 1, cur_param.cmd));
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
for (int i = 0; i < cur_param.consumes; i++) {
|
||||
ParamCheck &p = *get_next_arg();
|
||||
if (!std::holds_alternative<SQInteger>(*p.param)) ScriptLog::Error(fmt::format("{}({}): {{{}}} expects an integer", name, param_count + i + 1, cur_param.cmd));
|
||||
p.Encode(output);
|
||||
}
|
||||
param_count += cur_param.consumes;
|
||||
} catch (int nb) {
|
||||
param_count += nb;
|
||||
ScriptLog::Warning(fmt::format("{}({}): Invalid parameter", name, param_count));
|
||||
}
|
||||
|
||||
param_count += cur_param.consumes;
|
||||
}
|
||||
|
||||
seen_ids.pop_back();
|
||||
}
|
||||
|
||||
const std::string Text::GetDecodedText()
|
||||
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
|
||||
private:
|
||||
using ScriptTextRef = ScriptObjectRef<ScriptText>;
|
||||
using StringIDList = std::vector<StringID>;
|
||||
using ScriptTextList = std::vector<ScriptText *>;
|
||||
using Param = std::variant<SQInteger, std::string, ScriptTextRef>;
|
||||
|
||||
struct ParamCheck {
|
||||
@@ -137,10 +137,11 @@ private:
|
||||
int idx;
|
||||
Param *param;
|
||||
bool used;
|
||||
const char *cmd;
|
||||
|
||||
ParamCheck(StringID owner, int idx, Param *param) : owner(owner), idx(idx), param(param), used(false) {}
|
||||
ParamCheck(StringID owner, int idx, Param *param) : owner(owner), idx(idx), param(param), used(false), cmd(nullptr) {}
|
||||
|
||||
void Encode(std::back_insert_iterator<std::string> &output);
|
||||
void Encode(std::back_insert_iterator<std::string> &output, const char *cmd);
|
||||
};
|
||||
|
||||
using ParamList = std::vector<ParamCheck>;
|
||||
@@ -155,17 +156,19 @@ private:
|
||||
* The parameters are added as _GetEncodedText used to encode them
|
||||
* before the addition of parameter validation.
|
||||
* @param params The list of parameters to fill.
|
||||
* @param seen_texts The list of seen ScriptText.
|
||||
*/
|
||||
void _FillParamList(ParamList ¶ms);
|
||||
void _FillParamList(ParamList ¶ms, ScriptTextList &seen_texts);
|
||||
|
||||
/**
|
||||
* Internal function for recursive calling this function over multiple
|
||||
* instances, while writing in the same buffer.
|
||||
* @param output The output to write the encoded text to.
|
||||
* @param param_count The number of parameters that are in the string.
|
||||
* @param seen_ids The list of seen StringID.
|
||||
* @param param_count The number of parameters that are consumed by the string.
|
||||
* @param args The parameters to be consumed.
|
||||
* @param first Whether it's the first call in the recursion.
|
||||
*/
|
||||
void _GetEncodedText(std::back_insert_iterator<std::string> &output, int ¶m_count, StringIDList &seen_ids, ParamSpan args);
|
||||
void _GetEncodedText(std::back_insert_iterator<std::string> &output, int ¶m_count, ParamSpan args, bool first);
|
||||
|
||||
/**
|
||||
* Set a parameter, where the value is the first item on the stack.
|
||||
|
||||
@@ -123,15 +123,6 @@ void ScriptConfig::ResetEditableSettings(bool yet_to_start)
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptConfig::AddRandomDeviation(CompanyID owner)
|
||||
{
|
||||
for (const auto &item : *this->GetConfigList()) {
|
||||
if (item.random_deviation != 0) {
|
||||
this->SetSetting(item.name, ScriptObject::GetRandomizer(owner).Next(item.random_deviation * 2 + 1) - item.random_deviation + this->GetSetting(item.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptConfig::HasScript() const
|
||||
{
|
||||
return this->info != nullptr;
|
||||
|
||||
@@ -35,7 +35,6 @@ struct ScriptConfigItem {
|
||||
int min_value = 0; ///< The minimal value this configuration setting can have.
|
||||
int max_value = 1; ///< The maximal value this configuration setting can have.
|
||||
int default_value = 0; ///< The default value of this configuration setting.
|
||||
int random_deviation = 0; ///< The maximum random deviation from the default value.
|
||||
int step_size = 1; ///< The step size in the gui.
|
||||
ScriptConfigFlags flags = SCRIPTCONFIG_NONE; ///< Flags for the configuration setting.
|
||||
LabelMapping labels; ///< Text labels for the integer values.
|
||||
@@ -132,11 +131,6 @@ public:
|
||||
*/
|
||||
void ResetEditableSettings(bool yet_to_start);
|
||||
|
||||
/**
|
||||
* Randomize all settings the Script requested to be randomized.
|
||||
*/
|
||||
void AddRandomDeviation(CompanyID owner);
|
||||
|
||||
/**
|
||||
* Is this config attached to an Script? In other words, is there a Script
|
||||
* that is assigned to this slot.
|
||||
|
||||
@@ -379,18 +379,10 @@ struct ScriptSettingsWindow : public Window {
|
||||
TextColour colour;
|
||||
uint idx = 0;
|
||||
if (config_item.description.empty()) {
|
||||
if (this->slot != OWNER_DEITY && !Company::IsValidID(this->slot) && config_item.random_deviation != 0) {
|
||||
str = STR_AI_SETTINGS_JUST_DEVIATION;
|
||||
} else {
|
||||
str = STR_JUST_STRING1;
|
||||
}
|
||||
str = STR_JUST_STRING1;
|
||||
colour = TC_ORANGE;
|
||||
} else {
|
||||
if (this->slot != OWNER_DEITY && !Company::IsValidID(this->slot) && config_item.random_deviation != 0) {
|
||||
str = STR_AI_SETTINGS_SETTING_DEVIATION;
|
||||
} else {
|
||||
str = STR_AI_SETTINGS_SETTING;
|
||||
}
|
||||
str = STR_AI_SETTINGS_SETTING;
|
||||
colour = TC_LIGHT_BLUE;
|
||||
SetDParamStr(idx++, config_item.description);
|
||||
}
|
||||
@@ -405,35 +397,13 @@ struct ScriptSettingsWindow : public Window {
|
||||
DrawArrowButtons(br.left, y + button_y_offset, COLOUR_YELLOW, (this->clicked_button == i) ? 1 + (this->clicked_increase != rtl) : 0, editable && current_value > config_item.min_value, editable && current_value < config_item.max_value);
|
||||
}
|
||||
|
||||
if (this->slot == OWNER_DEITY || Company::IsValidID(this->slot) || config_item.random_deviation == 0) {
|
||||
auto config_iterator = config_item.labels.find(current_value);
|
||||
if (config_iterator != config_item.labels.end()) {
|
||||
SetDParam(idx++, STR_JUST_RAW_STRING);
|
||||
SetDParamStr(idx++, config_iterator->second);
|
||||
} else {
|
||||
SetDParam(idx++, STR_JUST_INT);
|
||||
SetDParam(idx++, current_value);
|
||||
}
|
||||
auto config_iterator = config_item.labels.find(current_value);
|
||||
if (config_iterator != config_item.labels.end()) {
|
||||
SetDParam(idx++, STR_JUST_RAW_STRING);
|
||||
SetDParamStr(idx++, config_iterator->second);
|
||||
} else {
|
||||
int min_deviated = std::max(config_item.min_value, current_value - config_item.random_deviation);
|
||||
auto config_iterator = config_item.labels.find(min_deviated);
|
||||
if (config_iterator != config_item.labels.end()) {
|
||||
SetDParam(idx++, STR_JUST_RAW_STRING);
|
||||
SetDParamStr(idx++, config_iterator->second);
|
||||
} else {
|
||||
SetDParam(idx++, STR_JUST_INT);
|
||||
SetDParam(idx++, min_deviated);
|
||||
}
|
||||
|
||||
int max_deviated = std::min(config_item.max_value, current_value + config_item.random_deviation);
|
||||
config_iterator = config_item.labels.find(max_deviated);
|
||||
if (config_iterator != config_item.labels.end()) {
|
||||
SetDParam(idx++, STR_JUST_RAW_STRING);
|
||||
SetDParamStr(idx++, config_iterator->second);
|
||||
} else {
|
||||
SetDParam(idx++, STR_JUST_INT);
|
||||
SetDParam(idx++, max_deviated);
|
||||
}
|
||||
SetDParam(idx++, STR_JUST_INT);
|
||||
SetDParam(idx++, current_value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,9 +87,7 @@ SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm)
|
||||
ScriptConfigItem config;
|
||||
uint items = 0;
|
||||
|
||||
int easy_value = INT32_MIN;
|
||||
int medium_value = INT32_MIN;
|
||||
int hard_value = INT32_MIN;
|
||||
|
||||
/* Read the table, and find all properties we care about */
|
||||
sq_pushnull(vm);
|
||||
@@ -124,9 +122,7 @@ SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm)
|
||||
config.max_value = ClampTo<int32_t>(res);
|
||||
items |= 0x008;
|
||||
} else if (key == "easy_value") {
|
||||
SQInteger res;
|
||||
if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
|
||||
easy_value = ClampTo<int32_t>(res);
|
||||
// No longer parsed.
|
||||
items |= 0x010;
|
||||
} else if (key == "medium_value") {
|
||||
SQInteger res;
|
||||
@@ -134,9 +130,7 @@ SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm)
|
||||
medium_value = ClampTo<int32_t>(res);
|
||||
items |= 0x020;
|
||||
} else if (key == "hard_value") {
|
||||
SQInteger res;
|
||||
if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
|
||||
hard_value = ClampTo<int32_t>(res);
|
||||
// No longer parsed.
|
||||
items |= 0x040;
|
||||
} else if (key == "custom_value") {
|
||||
// No longer parsed.
|
||||
@@ -146,10 +140,7 @@ SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm)
|
||||
config.default_value = ClampTo<int32_t>(res);
|
||||
items |= 0x080;
|
||||
} else if (key == "random_deviation") {
|
||||
SQInteger res;
|
||||
if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
|
||||
config.random_deviation = ClampTo<int32_t>(abs(res));
|
||||
items |= 0x200;
|
||||
// No longer parsed.
|
||||
} else if (key == "step_size") {
|
||||
SQInteger res;
|
||||
if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
|
||||
@@ -179,27 +170,12 @@ SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm)
|
||||
}
|
||||
|
||||
config.default_value = medium_value;
|
||||
/* If not boolean and no random deviation set, calculate it based on easy/hard difference. */
|
||||
if ((config.flags & SCRIPTCONFIG_BOOLEAN) == 0 && (items & 0x200) == 0) {
|
||||
config.random_deviation = abs(hard_value - easy_value) / 2;
|
||||
items |= 0x200;
|
||||
}
|
||||
items |= 0x080;
|
||||
} else {
|
||||
/* For compatibility, also act like the default sets the easy/medium/hard. */
|
||||
items |= 0x010 | 0x020 | 0x040;
|
||||
}
|
||||
|
||||
/* Don't allow both random_deviation and SCRIPTCONFIG_BOOLEAN to
|
||||
* be set for the same config item. */
|
||||
if ((items & 0x200) != 0 && (config.flags & SCRIPTCONFIG_BOOLEAN) != 0) {
|
||||
this->engine->ThrowError("setting both random_deviation and CONFIG_BOOLEAN is not allowed");
|
||||
return SQ_ERROR;
|
||||
}
|
||||
|
||||
/* Reset the bit for random_deviation as it's optional. */
|
||||
items &= ~0x200;
|
||||
|
||||
/* Make sure all properties are defined */
|
||||
uint mask = (config.flags & SCRIPTCONFIG_BOOLEAN) ? 0x1F3 : 0x1FF;
|
||||
if (items != mask) {
|
||||
|
||||
Reference in New Issue
Block a user