summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ai/ai_config.cpp229
-rw-r--r--src/ai/ai_config.hpp141
-rw-r--r--src/ai/ai_core.cpp13
-rw-r--r--src/ai/ai_gui.cpp29
-rw-r--r--src/ai/ai_info.cpp243
-rw-r--r--src/ai/ai_info.hpp71
-rw-r--r--src/ai/ai_instance.cpp1
-rw-r--r--src/ai/api/ai_changelog.hpp6
-rw-r--r--src/console_cmds.cpp4
-rw-r--r--src/saveload/ai_sl.cpp18
-rw-r--r--src/script/api/script_controller.cpp3
-rw-r--r--src/script/script_config.cpp201
-rw-r--r--src/script/script_config.hpp191
-rw-r--r--src/script/script_info.cpp212
-rw-r--r--src/script/script_info.hpp38
-rw-r--r--src/settings.cpp12
16 files changed, 779 insertions, 633 deletions
diff --git a/src/ai/ai_config.cpp b/src/ai/ai_config.cpp
index a5d656911..3f6b3c31b 100644
--- a/src/ai/ai_config.cpp
+++ b/src/ai/ai_config.cpp
@@ -14,101 +14,73 @@
#include "../core/random_func.hpp"
#include "ai.hpp"
#include "ai_config.hpp"
-
-void AIConfig::ChangeAI(const char *name, int version, bool force_exact_match, bool is_random_ai)
-{
- free(this->name);
- this->name = (name == NULL) ? NULL : strdup(name);
- this->info = (name == NULL) ? NULL : AI::FindInfo(this->name, version, force_exact_match);
- this->version = (info == NULL) ? -1 : info->GetVersion();
- this->is_random_ai = is_random_ai;
- if (this->config_list != NULL) delete this->config_list;
- this->config_list = (info == NULL) ? NULL : new AIConfigItemList();
- if (this->config_list != NULL) this->config_list->push_back(_start_date_config);
-
- /* The special casing for start_date is here to ensure that the
- * start_date setting won't change even if you chose another AI. */
- int start_date = this->GetSetting("start_date");
-
- for (SettingValueList::iterator it = this->settings.begin(); it != this->settings.end(); it++) {
- free((*it).first);
- }
- this->settings.clear();
-
- this->SetSetting("start_date", start_date);
-
- if (_game_mode == GM_NORMAL && this->info != NULL) {
- /* If we're in an existing game and the AI is changed, set all settings
- * for the AI that have the random flag to a random value. */
- for (AIConfigItemList::const_iterator it = this->info->GetConfigList()->begin(); it != this->info->GetConfigList()->end(); it++) {
- if ((*it).flags & AICONFIG_RANDOM) {
- this->SetSetting((*it).name, InteractiveRandomRange((*it).max_value - (*it).min_value) + (*it).min_value);
- }
- }
- this->AddRandomDeviation();
- }
-}
-
-AIConfig::AIConfig(const AIConfig *config)
+#include "ai_info.hpp"
+
+/** Configuration for AI start date, every AI has this setting. */
+ScriptConfigItem _start_date_config = {
+ "start_date",
+ "Number of days to start this AI after the previous one (give or take)",
+ AI::START_NEXT_MIN,
+ AI::START_NEXT_MAX,
+ AI::START_NEXT_MEDIUM,
+ AI::START_NEXT_EASY,
+ AI::START_NEXT_MEDIUM,
+ AI::START_NEXT_HARD,
+ AI::START_NEXT_DEVIATION,
+ 30,
+ SCRIPTCONFIG_NONE,
+ NULL
+};
+
+/* static */ AIConfig *AIConfig::GetConfig(CompanyID company, ScriptSettingSource source)
{
- this->name = (config->name == NULL) ? NULL : strdup(config->name);
- this->info = config->info;
- this->version = config->version;
- this->config_list = NULL;
- this->is_random_ai = config->is_random_ai;
-
- for (SettingValueList::const_iterator it = config->settings.begin(); it != config->settings.end(); it++) {
- this->settings[strdup((*it).first)] = (*it).second;
+ AIConfig **config;
+ if (source == SSS_FORCE_NEWGAME || (source == SSS_DEFAULT && _game_mode == GM_MENU)) {
+ config = &_settings_newgame.ai_config[company];
+ } else {
+ config = &_settings_game.ai_config[company];
}
- this->AddRandomDeviation();
+ if (*config == NULL) *config = new AIConfig();
+ return *config;
}
-AIConfig::~AIConfig()
+class AIInfo *AIConfig::GetInfo() const
{
- free(this->name);
- this->ResetSettings();
- if (this->config_list != NULL) delete this->config_list;
+ return static_cast<class AIInfo *>(ScriptConfig::GetInfo());
}
-AIInfo *AIConfig::GetInfo() const
+ScriptInfo *AIConfig::FindInfo(const char *name, int version, bool force_exact_match)
{
- return this->info;
+ return static_cast<ScriptInfo *>(AI::FindInfo(name, version, force_exact_match));
}
bool AIConfig::ResetInfo(bool force_exact_match)
{
- this->info = AI::FindInfo(this->name, force_exact_match ? this->version : -1, force_exact_match);
+ this->info = (ScriptInfo *)AI::FindInfo(this->name, force_exact_match ? this->version : -1, force_exact_match);
return this->info != NULL;
}
-const AIConfigItemList *AIConfig::GetConfigList()
+void AIConfig::PushExtraConfigList()
{
- if (this->info != NULL) return this->info->GetConfigList();
- if (this->config_list == NULL) {
- this->config_list = new AIConfigItemList();
- this->config_list->push_back(_start_date_config);
- }
- return this->config_list;
+ this->config_list->push_back(_start_date_config);
}
-AIConfig *AIConfig::GetConfig(CompanyID company, AISettingSource source)
+void AIConfig::ClearConfigList()
{
- AIConfig **config;
- if (source == AISS_FORCE_NEWGAME || (source == AISS_DEFAULT && _game_mode == GM_MENU)) {
- config = &_settings_newgame.ai_config[company];
- } else {
- config = &_settings_game.ai_config[company];
- }
- if (*config == NULL) *config = new AIConfig();
- return *config;
+ /* The special casing for start_date is here to ensure that the
+ * start_date setting won't change even if you chose another Script. */
+ int start_date = this->GetSetting("start_date");
+
+ ScriptConfig::ClearConfigList();
+
+ this->SetSetting("start_date", start_date);
}
int AIConfig::GetSetting(const char *name) const
{
- SettingValueList::const_iterator it = this->settings.find(name);
- /* Return the default value if the setting is not set, or if we are in a not-custom difficult level */
- if (it == this->settings.end() || GetGameSettings().difficulty.diff_level != 3) {
- if (this->info == NULL) {
+ if (this->info == NULL) {
+ SettingValueList::const_iterator it = this->settings.find(name);
+ if (it == this->settings.end() || GetGameSettings().difficulty.diff_level != 3) {
assert(strcmp("start_date", name) == 0);
switch (GetGameSettings().difficulty.diff_level) {
case 0: return AI::START_NEXT_EASY;
@@ -118,115 +90,28 @@ int AIConfig::GetSetting(const char *name) const
default: NOT_REACHED();
}
}
- return this->info->GetSettingDefaultValue(name);
- }
- return (*it).second;
-}
-
-void AIConfig::SetSetting(const char *name, int value)
-{
- /* You can only set ai specific settings if an AI is selected. */
- if (this->info == NULL && strcmp("start_date", name) != 0) return;
-
- if (this->info == NULL && strcmp("start_date", name) == 0) {
- value = Clamp(value, AI::START_NEXT_MIN, AI::START_NEXT_MAX);
- } else {
- const AIConfigItem *config_item = this->info->GetConfigItem(name);
- if (config_item == NULL) return;
-
- value = Clamp(value, config_item->min_value, config_item->max_value);
- }
- SettingValueList::iterator it = this->settings.find(name);
- if (it != this->settings.end()) {
- (*it).second = value;
- } else {
- this->settings[strdup(name)] = value;
+ return (*it).second;
}
-}
-void AIConfig::ResetSettings()
-{
- for (SettingValueList::iterator it = this->settings.begin(); it != this->settings.end(); it++) {
- free((*it).first);
- }
- this->settings.clear();
-}
-
-void AIConfig::AddRandomDeviation()
-{
- for (AIConfigItemList::const_iterator it = this->GetConfigList()->begin(); it != this->GetConfigList()->end(); it++) {
- if ((*it).random_deviation != 0) {
- this->SetSetting((*it).name, InteractiveRandomRange((*it).random_deviation * 2) - (*it).random_deviation + this->GetSetting((*it).name));
- }
- }
+ return ScriptConfig::GetSetting(name);
}
-bool AIConfig::HasAI() const
-{
- return this->info != NULL;
-}
-
-bool AIConfig::IsRandomAI() const
-{
- return this->is_random_ai;
-}
-
-const char *AIConfig::GetName() const
-{
- return this->name;
-}
-
-int AIConfig::GetVersion() const
+void AIConfig::SetSetting(const char *name, int value)
{
- return this->version;
-}
+ if (this->info == NULL) {
+ if (strcmp("start_date", name) != 0) return;
+ value = Clamp(value, AI::START_NEXT_MIN, AI::START_NEXT_MAX);
-void AIConfig::StringToSettings(const char *value)
-{
- char *value_copy = strdup(value);
- char *s = value_copy;
-
- while (s != NULL) {
- /* Analyze the string ('name=value,name=value\0') */
- char *item_name = s;
- s = strchr(s, '=');
- if (s == NULL) break;
- if (*s == '\0') break;
- *s = '\0';
- s++;
-
- char *item_value = s;
- s = strchr(s, ',');
- if (s != NULL) {
- *s = '\0';
- s++;
+ SettingValueList::iterator it = this->settings.find(name);
+ if (it != this->settings.end()) {
+ (*it).second = value;
+ } else {
+ this->settings[strdup(name)] = value;
}
- this->SetSetting(item_name, atoi(item_value));
+ return;
}
- free(value_copy);
-}
-void AIConfig::SettingsToString(char *string, size_t size) const
-{
- string[0] = '\0';
- for (SettingValueList::const_iterator it = this->settings.begin(); it != this->settings.end(); it++) {
- char no[10];
- snprintf(no, sizeof(no), "%d", (*it).second);
-
- /* Check if the string would fit in the destination */
- size_t needed_size = strlen((*it).first) + 1 + strlen(no) + 1;
- /* If it doesn't fit, skip the next settings */
- if (size <= needed_size) break;
- size -= needed_size;
-
- strcat(string, (*it).first);
- strcat(string, "=");
- strcat(string, no);
- strcat(string, ",");
- }
- /* Remove the last ',', but only if at least one setting was saved. */
- size_t len = strlen(string);
- if (len > 0) string[len - 1] = '\0';
+ ScriptConfig::SetSetting(name, value);
}
diff --git a/src/ai/ai_config.hpp b/src/ai/ai_config.hpp
index 3ed06d346..1e738d14b 100644
--- a/src/ai/ai_config.hpp
+++ b/src/ai/ai_config.hpp
@@ -11,48 +11,28 @@
#ifndef AI_CONFIG_HPP
#define AI_CONFIG_HPP
-#ifdef ENABLE_AI
-#include <map>
-#include "ai_info.hpp"
-#include "../core/string_compare_type.hpp"
-#include "../company_type.h"
-
-/**
- * AI settings for one company slot.
- */
-class AIConfig {
-private:
- /** List with name=>value pairs of all AI-specific settings */
- typedef std::map<const char *, int, StringCompare> SettingValueList;
+#include "../script/script_config.hpp"
+class AIConfig : public ScriptConfig {
public:
+ /**
+ * Get the config of a company.
+ */
+ static AIConfig *GetConfig(CompanyID company, ScriptSettingSource source = SSS_DEFAULT);
+
AIConfig() :
- name(NULL),
- version(-1),
- info(NULL),
- config_list(NULL),
- is_random_ai(false)
+ ScriptConfig()
{}
- /**
- * Create a new AI config that is a copy of an existing config.
- * @param config The object to copy.
- */
- AIConfig(const AIConfig *config);
+ AIConfig(const AIConfig *config) :
+ ScriptConfig(config)
+ {}
- /** Delete an AI configuration. */
- ~AIConfig();
+ class AIInfo *GetInfo() const;
- /**
- * Set another AI to be loaded in this slot.
- * @param name The name of the AI.
- * @param version The version of the AI to load, or -1 of latest.
- * @param force_exact_match If true try to find the exact same version
- * as specified. If false any compatible version is ok.
- * @param is_random Is the AI chosen randomly?
- */
- void ChangeAI(const char *name, int version = -1, bool force_exact_match = false, bool is_random = false);
+ /* virtual */ int GetSetting(const char *name) const;
+ /* virtual */ void SetSetting(const char *name, int value);
/**
* When ever the AI Scanner is reloaded, all infos become invalid. This
@@ -64,95 +44,10 @@ public:
*/
bool ResetInfo(bool force_exact_match);
- /**
- * Get the AIInfo linked to this AIConfig.
- */
- class AIInfo *GetInfo() const;
-
- /**
- * Get the config list for this AIConfig.
- */
- const AIConfigItemList *GetConfigList();
-
- /**
- * Where to get the config from, either default (depends on current game
- * mode) or force either newgame or normal
- */
- enum AISettingSource {
- AISS_DEFAULT, ///< Get the AI config from the current game mode
- AISS_FORCE_NEWGAME, ///< Get the newgame AI config
- AISS_FORCE_GAME, ///< Get the AI config from the current game
- };
-
- /**
- * Get the config of a company.
- */
- static AIConfig *GetConfig(CompanyID company, AISettingSource source = AISS_DEFAULT);
-
- /**
- * Get the value of a setting for this config. It might fallback to his
- * 'info' to find the default value (if not set or if not-custom difficulty
- * level).
- * @return The (default) value of the setting, or -1 if the setting was not
- * found.
- */
- int GetSetting(const char *name) const;
-
- /**
- * Set the value of a setting for this config.
- */
- void SetSetting(const char *name, int value);
-
- /**
- * Reset all settings to their default value.
- */
- void ResetSettings();
-
- /**
- * Randomize all settings the AI requested to be randomized.
- */
- void AddRandomDeviation();
-
- /**
- * Is this config attached to an AI?
- */
- bool HasAI() const;
-
- /**
- * Is the current AI a randomly chosen AI?
- */
- bool IsRandomAI() const;
-
- /**
- * Get the name of the AI.
- */
- const char *GetName() const;
-
- /**
- * Get the version of the AI.
- */
- int GetVersion() const;
-
- /**
- * Convert a string which is stored in the config file or savegames to
- * custom settings of this AI.
- */
- void StringToSettings(const char *value);
-
- /**
- * Convert the custom settings to a string that can be stored in the config
- * file or savegames.
- */
- void SettingsToString(char *string, size_t size) const;
-
-private:
- const char *name; ///< Name of the AI
- int version; ///< Version of the AI
- class AIInfo *info; ///< AIInfo object for related to this AI version
- SettingValueList settings; ///< List with all setting=>value pairs that are configure for this AI
- AIConfigItemList *config_list; ///< List with all settings defined by this AI
- bool is_random_ai; ///< True if the AI in this slot was randomly chosen.
+protected:
+ /* virtual */ void PushExtraConfigList();
+ /* virtual */ void ClearConfigList();
+ /* virtual */ ScriptInfo *FindInfo(const char *name, int version, bool force_exact_match);
};
-#endif /* ENABLE_AI */
#endif /* AI_CONFIG_HPP */
diff --git a/src/ai/ai_core.cpp b/src/ai/ai_core.cpp
index d6c0b7946..b314753d1 100644
--- a/src/ai/ai_core.cpp
+++ b/src/ai/ai_core.cpp
@@ -21,6 +21,7 @@
#include "ai_scanner.hpp"
#include "ai_instance.hpp"
#include "ai_config.hpp"
+#include "ai_info.hpp"
#include "ai.hpp"
#include "../script/api/script_error.hpp"
@@ -43,11 +44,11 @@
AIConfig *config = AIConfig::GetConfig(company);
AIInfo *info = config->GetInfo();
- if (info == NULL || (rerandomise_ai && config->IsRandomAI())) {
+ if (info == NULL || (rerandomise_ai && config->IsRandom())) {
info = AI::scanner_info->SelectRandomAI();
assert(info != NULL);
/* Load default data and store the name in the settings */
- config->ChangeAI(info->GetName(), -1, false, true);
+ config->Change(info->GetName(), -1, false, true);
}
Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
@@ -181,10 +182,10 @@
* the AIConfig. If not, remove the AI from the list (which will assign
* a random new AI on reload). */
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
- if (_settings_game.ai_config[c] != NULL && _settings_game.ai_config[c]->HasAI()) {
+ if (_settings_game.ai_config[c] != NULL && _settings_game.ai_config[c]->HasScript()) {
if (!_settings_game.ai_config[c]->ResetInfo(true)) {
DEBUG(ai, 0, "After a reload, the AI by the name '%s' was no longer found, and removed from the list.", _settings_game.ai_config[c]->GetName());
- _settings_game.ai_config[c]->ChangeAI(NULL);
+ _settings_game.ai_config[c]->Change(NULL);
if (Company::IsValidAiID(c)) {
/* The code belonging to an already running AI was deleted. We can only do
* one thing here to keep everything sane and that is kill the AI. After
@@ -198,10 +199,10 @@
Company::Get(c)->ai_info = _settings_game.ai_config[c]->GetInfo();
}
}
- if (_settings_newgame.ai_config[c] != NULL && _settings_newgame.ai_config[c]->HasAI()) {
+ if (_settings_newgame.ai_config[c] != NULL && _settings_newgame.ai_config[c]->HasScript()) {
if (!_settings_newgame.ai_config[c]->ResetInfo(false)) {
DEBUG(ai, 0, "After a reload, the AI by the name '%s' was no longer found, and removed from the list.", _settings_newgame.ai_config[c]->GetName());
- _settings_newgame.ai_config[c]->ChangeAI(NULL);
+ _settings_newgame.ai_config[c]->Change(NULL);
}
}
}
diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp
index 2ef7d5db9..a71e018e9 100644
--- a/src/ai/ai_gui.cpp
+++ b/src/ai/ai_gui.cpp
@@ -28,6 +28,7 @@
#include "ai.hpp"
#include "../script/api/script_log.hpp"
#include "ai_config.hpp"
+#include "ai_info.hpp"
#include "ai_instance.hpp"
#include "table/strings.h"
@@ -71,7 +72,7 @@ struct AIListWindow : public Window {
/* Try if we can find the currently selected AI */
this->selected = -1;
- if (AIConfig::GetConfig(slot)->HasAI()) {
+ if (AIConfig::GetConfig(slot)->HasScript()) {
AIInfo *info = AIConfig::GetConfig(slot)->GetInfo();
int i = 0;
for (ScriptInfoList::const_iterator it = this->ai_info_list->begin(); it != this->ai_info_list->end(); it++, i++) {
@@ -148,11 +149,11 @@ struct AIListWindow : public Window {
void ChangeAI()
{
if (this->selected == -1) {
- AIConfig::GetConfig(slot)->ChangeAI(NULL);
+ AIConfig::GetConfig(slot)->Change(NULL);
} else {
ScriptInfoList::const_iterator it = this->ai_info_list->begin();
for (int i = 0; i < this->selected; i++) it++;
- AIConfig::GetConfig(slot)->ChangeAI((*it).second->GetName(), (*it).second->GetVersion());
+ AIConfig::GetConfig(slot)->Change((*it).second->GetName(), (*it).second->GetVersion());
}
SetWindowDirty(WC_GAME_OPTIONS, 0);
}
@@ -272,7 +273,7 @@ struct AISettingsWindow : public Window {
int clicked_row; ///< The clicked row of settings.
int line_height; ///< Height of a row in the matrix widget.
Scrollbar *vscroll; ///< Cache of the vertical scrollbar.
- typedef std::vector<const AIConfigItem *> VisibleSettingsList;
+ typedef std::vector<const ScriptConfigItem *> VisibleSettingsList;
VisibleSettingsList visible_settings; ///< List of visible AI settings
/**
@@ -306,9 +307,9 @@ struct AISettingsWindow : public Window {
{
visible_settings.clear();
- AIConfigItemList::const_iterator it = this->ai_config->GetConfigList()->begin();
+ ScriptConfigItemList::const_iterator it = this->ai_config->GetConfigList()->begin();
for (; it != this->ai_config->GetConfigList()->end(); it++) {
- bool no_hide = (it->flags & AICONFIG_AI_DEVELOPER) == 0;
+ bool no_hide = (it->flags & SCRIPTCONFIG_DEVELOPER) == 0;
if (no_hide || _settings_client.gui.ai_developer_tools) {
visible_settings.push_back(&(*it));
}
@@ -343,9 +344,9 @@ struct AISettingsWindow : public Window {
int y = r.top;
for (; this->vscroll->IsVisible(i) && it != visible_settings.end(); i++, it++) {
- const AIConfigItem &config_item = **it;
+ const ScriptConfigItem &config_item = **it;
int current_value = config->GetSetting((config_item).name);
- bool editable = _game_mode == GM_MENU || !Company::IsValidID(this->slot) || (config_item.flags & AICONFIG_INGAME) != 0;
+ bool editable = _game_mode == GM_MENU || !Company::IsValidID(this->slot) || (config_item.flags & SCRIPTCONFIG_INGAME) != 0;
StringID str;
TextColour colour;
@@ -359,7 +360,7 @@ struct AISettingsWindow : public Window {
SetDParamStr(idx++, config_item.description);
}
- if ((config_item.flags & AICONFIG_BOOLEAN) != 0) {
+ if ((config_item.flags & SCRIPTCONFIG_BOOLEAN) != 0) {
DrawFrameRect(buttons_left, y + 2, buttons_left + 19, y + 10, (current_value != 0) ? COLOUR_GREEN : COLOUR_RED, (current_value != 0) ? FR_LOWERED : FR_NONE);
SetDParam(idx++, current_value == 0 ? STR_CONFIG_SETTING_OFF : STR_CONFIG_SETTING_ON);
} else {
@@ -403,10 +404,10 @@ struct AISettingsWindow : public Window {
VisibleSettingsList::const_iterator it = this->visible_settings.begin();
for (int i = 0; i < num; i++) it++;
- const AIConfigItem config_item = **it;
- if (_game_mode == GM_NORMAL && Company::IsValidID(this->slot) && (config_item.flags & AICONFIG_INGAME) == 0) return;
+ const ScriptConfigItem config_item = **it;
+ if (_game_mode == GM_NORMAL && Company::IsValidID(this->slot) && (config_item.flags & SCRIPTCONFIG_INGAME) == 0) return;
- bool bool_item = (config_item.flags & AICONFIG_BOOLEAN) != 0;
+ bool bool_item = (config_item.flags & SCRIPTCONFIG_BOOLEAN) != 0;
int x = pt.x - wid->pos_x;
if (_current_text_dir == TD_RTL) x = wid->current_x - x;
@@ -462,9 +463,9 @@ struct AISettingsWindow : public Window {
virtual void OnQueryTextFinished(char *str)
{
if (StrEmpty(str)) return;
- AIConfigItemList::const_iterator it = this->ai_config->GetConfigList()->begin();
+ ScriptConfigItemList::const_iterator it = this->ai_config->GetConfigList()->begin();
for (int i = 0; i < this->clicked_row; i++) it++;
- if (_game_mode == GM_NORMAL && Company::IsValidID(this->slot) && (it->flags & AICONFIG_INGAME) == 0) return;
+ if (_game_mode == GM_NORMAL && Company::IsValidID(this->slot) && (it->flags & SCRIPTCONFIG_INGAME) == 0) return;
int32 value = atoi(str);
this->ai_config->SetSetting((*it).name, value);
this->CheckDifficultyLevel();
diff --git a/src/ai/ai_info.cpp b/src/ai/ai_info.cpp
index a56064e01..d43ff8f7d 100644
--- a/src/ai/ai_info.cpp
+++ b/src/ai/ai_info.cpp
@@ -23,22 +23,6 @@
/** Maximum number of operations allowed for getting a particular setting. */
static const int MAX_GET_SETTING_OPS = 100000;
-/** Configuration for AI start date, every AI has this setting. */
-AIConfigItem _start_date_config = {
- "start_date",
- "Number of days to start this AI after the previous one (give or take)",
- AI::START_NEXT_MIN,
- AI::START_NEXT_MAX,
- AI::START_NEXT_MEDIUM,
- AI::START_NEXT_EASY,
- AI::START_NEXT_MEDIUM,
- AI::START_NEXT_HARD,
- AI::START_NEXT_DEVIATION,
- 30,
- AICONFIG_NONE,
- NULL
-};
-
/**
* Check if the API version provided by the AI is supported.
* @param api_version The API version as provided by the AI.
@@ -61,11 +45,18 @@ template <> const char *GetClassName<AIInfo, ST_AI>() { return "AIInfo"; }
SQAIInfo.AddConstructor<void (AIInfo::*)(), 1>(engine, "x");
SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddSetting, "AddSetting");
SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddLabels, "AddLabels");
- SQAIInfo.DefSQConst(engine, AICONFIG_NONE, "AICONFIG_NONE");
- SQAIInfo.DefSQConst(engine, AICONFIG_RANDOM, "AICONFIG_RANDOM");
- SQAIInfo.DefSQConst(engine, AICONFIG_BOOLEAN, "AICONFIG_BOOLEAN");
- SQAIInfo.DefSQConst(engine, AICONFIG_INGAME, "AICONFIG_INGAME");
- SQAIInfo.DefSQConst(engine, AICONFIG_AI_DEVELOPER, "AICONFIG_AI_DEVELOPER");
+ SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "CONFIG_NONE");
+ SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_RANDOM, "CONFIG_RANDOM");
+ SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_BOOLEAN, "CONFIG_BOOLEAN");
+ SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_INGAME, "CONFIG_INGAME");
+ SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_DEVELOPER, "CONFIG_DEVELOPER");
+
+ /* Pre 1.2 had an AI prefix */
+ SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "AICONFIG_NONE");
+ SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_RANDOM, "AICONFIG_RANDOM");
+ SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_BOOLEAN, "AICONFIG_BOOLEAN");
+ SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_INGAME, "AICONFIG_INGAME");
+
SQAIInfo.PostRegister(engine);
engine->AddMethod("RegisterAI", &AIInfo::Constructor, 2, "tx");
engine->AddMethod("RegisterDummyAI", &AIInfo::DummyConstructor, 2, "tx");
@@ -81,15 +72,11 @@ template <> const char *GetClassName<AIInfo, ST_AI>() { return "AIInfo"; }
SQInteger res = ScriptInfo::Constructor(vm, info);
if (res != 0) return res;
- AIConfigItem config = _start_date_config;
+ ScriptConfigItem config = _start_date_config;
config.name = strdup(config.name);
config.description = strdup(config.description);
- info->config_list.push_back(config);
+ info->config_list.push_front(config);
- /* Check if we have settings */
- if (info->engine->MethodExists(*info->SQ_instance, "GetSettings")) {
- if (!info->GetSettings()) return SQ_ERROR;
- }
if (info->engine->MethodExists(*info->SQ_instance, "MinVersionToLoad")) {
if (!info->engine->CallIntegerMethod(*info->SQ_instance, "MinVersionToLoad", &info->min_loadable_version, MAX_GET_SETTING_OPS)) return SQ_ERROR;
} else {
@@ -141,11 +128,6 @@ template <> const char *GetClassName<AIInfo, ST_AI>() { return "AIInfo"; }
return 0;
}
-bool AIInfo::GetSettings()
-{
- return this->engine->CallMethod(*this->SQ_instance, "GetSettings", NULL, MAX_GET_SETTING_OPS);
-}
-
AIInfo::AIInfo() :
min_loadable_version(0),
use_as_random(false),
@@ -155,18 +137,6 @@ AIInfo::AIInfo() :
AIInfo::~AIInfo()
{
- /* Free all allocated strings */
- for (AIConfigItemList::iterator it = this->config_list.begin(); it != this->config_list.end(); it++) {
- free((*it).name);
- free((*it).description);
- if (it->labels != NULL) {
- for (LabelMapping::iterator it2 = (*it).labels->Begin(); it2 != (*it).labels->End(); it2++) {
- free(it2->second);
- }
- delete it->labels;
- }
- }
- this->config_list.clear();
free(this->api_version);
}
@@ -176,191 +146,6 @@ bool AIInfo::CanLoadFromVersion(int version) const
return version >= this->min_loadable_version && version <= this->GetVersion();
}
-SQInteger AIInfo::AddSetting(HSQUIRRELVM vm)
-{
- AIConfigItem config;
- memset(&config, 0, sizeof(config));
- config.max_value = 1;
- config.step_size = 1;
- uint items = 0;
-
- /* Read the table, and find all properties we care about */
- sq_pushnull(vm);
- while (SQ_SUCCEEDED(sq_next(vm, -2))) {
- const SQChar *sqkey;
- if (SQ_FAILED(sq_getstring(vm, -2, &sqkey))) return SQ_ERROR;
- const char *key = SQ2OTTD(sqkey);
-
- if (strcmp(key, "name") == 0) {
- const SQChar *sqvalue;
- if (SQ_FAILED(sq_getstring(vm, -1, &sqvalue))) return SQ_ERROR;
- char *name = strdup(SQ2OTTD(sqvalue));
- char *s;
- /* Don't allow '=' and ',' in configure setting names, as we need those
- * 2 chars to nicely store the settings as a string. */
- while ((s = strchr(name, '=')) != NULL) *s = '_';
- while ((s = strchr(name, ',')) != NULL) *s = '_';
- config.name = name;
- items |= 0x001;
- } else if (strcmp(key, "description") == 0) {
- const SQChar *sqdescription;
- if (SQ_FAILED(sq_getstring(vm, -1, &sqdescription))) return SQ_ERROR;
- config.description = strdup(SQ2OTTD(sqdescription));
- items |= 0x002;
- } else if (strcmp(key, "min_value") == 0) {
- SQInteger res;
- if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
- config.min_value = res;
- items |= 0x004;
- } else if (strcmp(key, "max_value") == 0) {
- SQInteger res;
- if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
- config.max_value = res;
- items |= 0x008;
- } else if (strcmp(key, "easy_value") == 0) {
- SQInteger res;
- if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
- config.easy_value = res;
- items |= 0x010;
- } else if (strcmp(key, "medium_value") == 0) {
- SQInteger res;
- if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
- config.medium_value = res;
- items |= 0x020;
- } else if (strcmp(key, "hard_value") == 0) {
- SQInteger res;
- if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
- config.hard_value = res;
- items |= 0x040;
- } else if (strcmp(key, "random_deviation") == 0) {
- SQInteger res;
- if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
- config.random_deviation = res;
- items |= 0x200;
- } else if (strcmp(key, "custom_value") == 0) {
- SQInteger res;
- if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
- config.custom_value = res;
- items |= 0x080;
- } else if (strcmp(key, "step_size") == 0) {
- SQInteger res;
- if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
- config.step_size = res;
- } else if (strcmp(key, "flags") == 0) {
- SQInteger res;
- if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
- config.flags = (AIConfigFlags)res;
- items |= 0x100;
- } else {
- char error[1024];
- snprintf(error, sizeof(error), "unknown setting property '%s'", key);
- this->engine->ThrowError(error);
- return SQ_ERROR;
- }
-
- sq_pop(vm, 2);
- }
- sq_pop(vm, 1);
-
- /* Don't allow both random_deviation and AICONFIG_RANDOM to
- * be set for the same config item. */
- if ((items & 0x200) != 0 && (config.flags & AICONFIG_RANDOM) != 0) {
- char error[1024];
- snprintf(error, sizeof(error), "Setting both random_deviation and AICONFIG_RANDOM is not allowed");
- this->engine->ThrowError(error);
- 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 & AICONFIG_BOOLEAN) ? 0x1F3 : 0x1FF;
- if (items != mask) {
- char error[1024];
- snprintf(error, sizeof(error), "please define all properties of a setting (min/max not allowed for booleans)");
- this->engine->ThrowError(error);
- return SQ_ERROR;
- }
-
- this->config_list.push_back(config);
- return 0;
-}
-
-SQInteger AIInfo::AddLabels(HSQUIRRELVM vm)
-{
- const SQChar *sq_setting_name;
- if (SQ_FAILED(sq_getstring(vm, -2, &sq_setting_name))) return SQ_ERROR;
- const char *setting_name = SQ2OTTD(sq_setting_name);
-
- AIConfigItem *config = NULL;
- for (AIConfigItemList::iterator it = this->config_list.begin(); it != this->config_list.end(); it++) {
- if (strcmp((*it).name, setting_name) == 0) config = &(*it);
- }
-
- if (config == NULL) {
- char error[1024];
- snprintf(error, sizeof(error), "Trying to add labels for non-defined setting '%s'", setting_name);
- this->engine->ThrowError(error);
- return SQ_ERROR;
- }
- if (config->labels != NULL) return SQ_ERROR;
-
- config->labels = new LabelMapping;
-
- /* Read the table and find all labels */
- sq_pushnull(vm);
- while (SQ_SUCCEEDED(sq_next(vm, -2))) {
- const SQChar *sq_key;
- const SQChar *sq_label;
- if (SQ_FAILED(sq_getstring(vm, -2, &sq_key))) return SQ_ERROR;
- if (SQ_FAILED(sq_getstring(vm, -1, &sq_label))) return SQ_ERROR;
- /* Because squirrel doesn't support identifiers starting with a digit,
- * we skip the first character. */
- const char *key_string = SQ2OTTD(sq_key);
- int key = atoi(key_string + 1);
- const char *label = SQ2OTTD(sq_label);
-
- /* !Contains() prevents strdup from leaking. */
- if (!config->labels->Contains(key)) config->labels->Insert(key, strdup(label));
-
- sq_pop(vm, 2);
- }
- sq_pop(vm, 1);
-
- return 0;
-}
-
-const AIConfigItemList *AIInfo::GetConfigList() const
-{
- return &this->config_list;
-}
-
-const AIConfigItem *AIInfo::GetConfigItem(const char *name) const
-{
- for (AIConfigItemList::const_iterator it = this->config_list.begin(); it != this->config_list.end(); it++) {
- if (strcmp((*it).name, name) == 0) return &(*it);
- }
- return NULL;
-}
-
-int AIInfo::GetSettingDefaultValue(const char *name) const
-{
- for (AIConfigItemList::const_iterator it = this->config_list.begin(); it != this->config_list.end(); it++) {
- if (strcmp((*it).name, name) != 0) continue;
- /* The default value depends on the difficulty level */
- switch (GetGameSettings().difficulty.diff_level) {
- case 0: return (*it).easy_value;
- case 1: return (*it).medium_value;
- case 2: return (*it).hard_value;
- case 3: return (*it).custom_value;
- default: NOT_REACHED();
- }
- }
-
- /* There is no such setting */
- return -1;
-}
-
AILibrary::~AILibrary()
{
diff --git a/src/ai/ai_info.hpp b/src/ai/ai_info.hpp
index 82ca6f925..924ddd2e0 100644
--- a/src/ai/ai_info.hpp
+++ b/src/ai/ai_info.hpp
@@ -14,40 +14,8 @@
#ifdef ENABLE_AI
-#include <list>
-#include "../core/smallmap_type.hpp"
#include "../script/script_info.hpp"
-
-/** Bitmask of flags for AI settings. */
-enum AIConfigFlags {
- AICONFIG_NONE = 0x0, ///< No flags set.
- AICONFIG_RANDOM = 0x1, ///< When randomizing the AI, pick any value between min_value and max_value when on custom difficulty setting.
- AICONFIG_BOOLEAN = 0x2, ///< This value is a boolean (either 0 (false) or 1 (true) ).
- AICONFIG_INGAME = 0x4, ///< This setting can be changed while the AI is running.
- AICONFIG_AI_DEVELOPER = 0x8, ///< This setting will only be visible when the ai development tools are active.
-};
-
-typedef SmallMap<int, char *> LabelMapping; ///< Map-type used to map the setting numbers to labels.
-
-/** Info about a single AI setting. */
-struct AIConfigItem {
- const char *name; ///< The name of the configuration setting.
- const char *description; ///< The description of the configuration setting.
- int min_value; ///< The minimal value this configuration setting can have.
- int max_value; ///< The maximal value this configuration setting can have.
- int custom_value; ///< The default value on custom difficulty setting.
- int easy_value; ///< The default value on easy difficulty setting.
- int medium_value; ///< The default value on medium difficulty setting.
- int hard_value; ///< The default value on hard difficulty setting.
- int random_deviation; ///< The maximum random deviation from the default value.
- int step_size; ///< The step size in the gui.
- AIConfigFlags flags; ///< Flags for the configuration setting.
- LabelMapping *labels; ///< Text labels for the integer values.
-};
-
-extern AIConfigItem _start_date_config;
-
-typedef std::list<AIConfigItem> AIConfigItemList; ///< List of AIConfig items.
+#include "../script/script_config.hpp"
/** All static information from an AI like name, version, etc. */
class AIInfo : public ScriptInfo {
@@ -71,41 +39,11 @@ public:
static SQInteger DummyConstructor(HSQUIRRELVM vm);
/**
- * Get the settings of the AI.
- */
- bool GetSettings();
-
- /**
- * Get the config list for this AI.
- */
- const AIConfigItemList *GetConfigList() const;
-
- /**
- * Get the description of a certain ai config option.
- */
- const AIConfigItem *GetConfigItem(const char *name) const;
-
- /**
* Check if we can start this AI.
*/
bool CanLoadFromVersion(int version) const;
/**
- * Set a setting.
- */
- SQInteger AddSetting(HSQUIRRELVM vm);
-
- /**
- * Add labels for a setting.
- */
- SQInteger AddLabels(HSQUIRRELVM vm);
-
- /**
- * Get the default value for a setting.
- */
- int GetSettingDefaultValue(const char *name) const;
-
- /**
* Use this AI as a random AI.
*/
bool UseAsRandomAI() const { return this->use_as_random; }
@@ -116,10 +54,9 @@ public:
const char *GetAPIVersion() const { return this->api_version; }
private:
- AIConfigItemList config_list; ///< List of settings from this AI.
- int min_loadable_version; ///< The AI can load savegame data if the version is equal or greater than this.
- bool use_as_random; ///< Should this AI be used when the user wants a "random AI"?
- const char *api_version; ///< API version used by this AI.
+ int min_loadable_version; ///< The AI can load savegame data if the version is equal or greater than this.
+ bool use_as_random; ///< Should this AI be used when the user wants a "random AI"?
+ const char *api_version; ///< API version used by this AI.
};
/** All static information from an AI library like name, version, etc. */
diff --git a/src/ai/ai_instance.cpp b/src/ai/ai_instance.cpp
index 940ba4eb3..11459f649 100644
--- a/src/ai/ai_instance.cpp
+++ b/src/ai/ai_instance.cpp
@@ -22,6 +22,7 @@
#include "../script/script_fatalerror.hpp"
#include "../script/script_suspend.hpp"
#include "../script/script_storage.hpp"
+#include "ai_info.hpp"
#include "ai_instance.hpp"
/* Convert all AI related classes to Squirrel data.
diff --git a/src/ai/api/ai_changelog.hpp b/src/ai/api/ai_changelog.hpp
index 8e6a2afb7..99d892497 100644
--- a/src/ai/api/ai_changelog.hpp
+++ b/src/ai/api/ai_changelog.hpp
@@ -31,7 +31,7 @@
* \li AICompany::GetQuarterlyPerformanceRating
* \li AICompany::GetQuarterlyCompanyValue
* \li AIController::GetOpsTillSuspend
- * \li AIInfo::AICONFIG_AI_DEVELOPER
+ * \li AIInfo::CONFIG_DEVELOPER
* \li AIOrder::GetOrderRefit
* \li AIOrder::IsRefitOrder
* \li AIOrder::SetOrderRefit
@@ -44,7 +44,9 @@
*
* API renames:
* \li AITown::GetLastMonthTransported to AITown::GetLastMonthSupplied to better
- * reflect what it does
+ * reflect what it does.
+ * \li AIInfo has all its configure settings renamed from AICONFIG to just CONFIG
+ * like CONFIG_RANDOM.
*
* API removals:
* \li AICompany::GetCompanyValue, use AICompany::GetQuarterlyCompanyValue instead.
diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp
index 6b44b455e..4c6a51ba8 100644
--- a/src/console_cmds.cpp
+++ b/src/console_cmds.cpp
@@ -1150,8 +1150,8 @@ DEF_CONSOLE_CMD(ConStartAI)
AIConfig *config = AIConfig::GetConfig((CompanyID)n);
if (argc >= 2) {
- config->ChangeAI(argv[1], -1, true);
- if (!config->HasAI()) {
+ config->Change(argv[1], -1, true);
+ if (!config->HasScript()) {
IConsoleWarning("Failed to load the specified AI");
return true;
}
diff --git a/src/saveload/ai_sl.cpp b/src/saveload/ai_sl.cpp
index c3a95f89b..cfb90c34c 100644
--- a/src/saveload/ai_sl.cpp
+++ b/src/saveload/ai_sl.cpp
@@ -39,7 +39,7 @@ static void SaveReal_AIPL(int *index_ptr)
CompanyID index = (CompanyID)*index_ptr;
AIConfig *config = AIConfig::GetConfig(index);
- if (config->HasAI()) {
+ if (config->HasScript()) {
ttd_strlcpy(_ai_saveload_name, config->GetName(), lengthof(_ai_saveload_name));
_ai_saveload_version = config->GetVersion();
} else {
@@ -48,7 +48,7 @@ static void SaveReal_AIPL(int *index_ptr)
_ai_saveload_version = -1;
}
- _ai_saveload_is_random = config->IsRandomAI();
+ _ai_saveload_is_random = config->IsRandom();
_ai_saveload_settings[0] = '\0';
config->SettingsToString(_ai_saveload_settings, lengthof(_ai_saveload_settings));
@@ -61,7 +61,7 @@ static void Load_AIPL()
{
/* Free all current data */
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
- AIConfig::GetConfig(c, AIConfig::AISS_FORCE_GAME)->ChangeAI(NULL);
+ AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->Change(NULL);
}
CompanyID index;
@@ -76,17 +76,17 @@ static void Load_AIPL()
continue;
}
- AIConfig *config = AIConfig::GetConfig(index, AIConfig::AISS_FORCE_GAME);
+ AIConfig *config = AIConfig::GetConfig(index, AIConfig::SSS_FORCE_GAME);
if (StrEmpty(_ai_saveload_name)) {
/* A random AI. */
- config->ChangeAI(NULL, -1, false, true);
+ config->Change(NULL, -1, false, true);
} else {
- config->ChangeAI(_ai_saveload_name, _ai_saveload_version, false, _ai_saveload_is_random);
- if (!config->HasAI()) {
+ config->Change(_ai_saveload_name, _ai_saveload_version, false, _ai_saveload_is_random);
+ if (!config->HasScript()) {
/* No version of the AI available that can load the data. Try to load the
* latest version of the AI instead. */
- config->ChangeAI(_ai_saveload_name, -1, false, _ai_saveload_is_random);
- if (!config->HasAI()) {
+ config->Change(_ai_saveload_name, -1, false, _ai_saveload_is_random);
+ if (!config->HasScript()) {
if (strcmp(_ai_saveload_name, "%_dummy") != 0) {
DEBUG(ai, 0, "The savegame has an AI by the name '%s', version %d which is no longer available.", _ai_saveload_name, _ai_saveload_version);
DEBUG(ai, 0, "A random other AI will be loaded in its place.");
diff --git a/src/script/api/script_controller.cpp b/src/script/api/script_controller.cpp
index e81af3603..0638a6651 100644
--- a/src/script/api/script_controller.cpp
+++ b/src/script/api/script_controller.cpp
@@ -21,6 +21,7 @@
#include "../../ai/ai_config.hpp"
#include "../../ai/ai.hpp"
#include "../script_fatalerror.hpp"
+#include "../script_info.hpp"
#include "../script_suspend.hpp"
#include "script_log.hpp"
@@ -96,7 +97,7 @@ ScriptController::~ScriptController()
snprintf(library_name, sizeof(library_name), "%s.%d", library, version);
strtolower(library_name);
- AILibrary *lib = AI::FindLibrary(library, version);
+ ScriptInfo *lib = (ScriptInfo *)AI::FindLibrary(library, version);
if (lib == NULL) {
char error[1024];
snprintf(error, sizeof(error), "couldn't find library '%s' with version %d", library, version);
diff --git a/src/script/script_config.cpp b/src/script/script_config.cpp
new file mode 100644
index 000000000..1818270e8
--- /dev/null
+++ b/src/script/script_config.cpp
@@ -0,0 +1,201 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file script_config.cpp Implementation of ScriptConfig. */
+
+#include "../stdafx.h"
+#include "../settings_type.h"
+#include "../core/random_func.hpp"
+#include "script_info.hpp"
+#include "script_config.hpp"
+
+void ScriptConfig::Change(const char *name, int version, bool force_exact_match, bool is_random)
+{
+ free(this->name);
+ this->name = (name == NULL) ? NULL : strdup(name);
+ this->info = (name == NULL) ? NULL : this->FindInfo(this->name, version, force_exact_match);
+ this->version = (info == NULL) ? -1 : info->GetVersion();
+ this->is_random = is_random;
+ if (this->config_list != NULL) delete this->config_list;
+ this->config_list = (info == NULL) ? NULL : new ScriptConfigItemList();
+ if (this->config_list != NULL) this->PushExtraConfigList();
+
+ this->ClearConfigList();
+
+ if (_game_mode == GM_NORMAL && this->info != NULL) {
+ /* If we're in an existing game and the Script is changed, set all settings
+ * for the Script that have the random flag to a random value. */
+ for (ScriptConfigItemList::const_iterator it = this->info->GetConfigList()->begin(); it != this->info->GetConfigList()->end(); it++) {
+ if ((*it).flags & SCRIPTCONFIG_RANDOM) {
+ this->SetSetting((*it).name, InteractiveRandomRange((*it).max_value - (*it).min_value) + (*it).min_value);
+ }
+ }
+ this->AddRandomDeviation();
+ }
+}
+
+ScriptConfig::ScriptConfig(const ScriptConfig *config)
+{
+ this->name = (config->name == NULL) ? NULL : strdup(config->name);
+ this->info = config->info;
+ this->version = config->version;
+ this->config_list = NULL;
+ this->is_random = config->is_random;
+
+ for (SettingValueList::const_iterator it = config->settings.begin(); it != config->settings.end(); it++) {
+ this->settings[strdup((*it).first)] = (*it).second;
+ }
+ this->AddRandomDeviation();
+}
+
+ScriptConfig::~ScriptConfig()
+{
+ free(this->name);
+ this->ResetSettings();
+ if (this->config_list != NULL) delete this->config_list;
+}
+
+ScriptInfo *ScriptConfig::GetInfo() const
+{
+ return this->info;
+}
+
+const ScriptConfigItemList *ScriptConfig::GetConfigList()
+{
+ if (this->info != NULL) return this->info->GetConfigList();
+ if (this->config_list == NULL) {
+ this->config_list = new ScriptConfigItemList();
+ this->PushExtraConfigList();
+ }
+ return this->config_list;
+}
+
+void ScriptConfig::ClearConfigList()
+{
+ for (SettingValueList::iterator it = this->settings.begin(); it != this->settings.end(); it++) {
+ free((*it).first);
+ }
+ this->settings.clear();
+}
+
+int ScriptConfig::GetSetting(const char *name) const
+{
+ /* Return default values if the difficulty is not set to Custom */
+ if (GetGameSettings().difficulty.diff_level != 3) {
+ return this->info->GetSettingDefaultValue(name);
+ }
+
+ SettingValueList::const_iterator it = this->settings.find(name);
+ if (it == this->settings.end()) return this->info->GetSettingDefaultValue(name);
+ return (*it).second;
+}
+
+void ScriptConfig::SetSetting(const char *name, int value)
+{
+ /* You can only set Script specific settings if an Script is selected. */
+ if (this->info == NULL) return;
+
+ const ScriptConfigItem *config_item = this->info->GetConfigItem(name);
+ if (config_item == NULL) return;
+
+ value = Clamp(value, config_item->min_value, config_item->max_value);
+
+ SettingValueList::iterator it = this->settings.find(name);
+ if (it != this->settings.end()) {
+ (*it).second = value;
+ } else {
+ this->settings[strdup(name)] = value;
+ }
+}
+
+void ScriptConfig::ResetSettings()
+{
+ for (SettingValueList::iterator it = this->settings.begin(); it != this->settings.end(); it++) {
+ free((*it).first);
+ }
+ this->settings.clear();
+}
+
+void ScriptConfig::AddRandomDeviation()
+{
+ for (ScriptConfigItemList::const_iterator it = this->GetConfigList()->begin(); it != this->GetConfigList()->end(); it++) {
+ if ((*it).random_deviation != 0) {
+ this->SetSetting((*it).name, InteractiveRandomRange((*it).random_deviation * 2) - (*it).random_deviation + this->GetSetting((*it).name));
+ }
+ }
+}
+
+bool ScriptConfig::HasScript() const
+{
+ return this->info != NULL;
+}
+
+bool ScriptConfig::IsRandom() const
+{
+ return this->is_random;
+}
+
+const char *ScriptConfig::GetName() const
+{
+ return this->name;
+}
+
+int ScriptConfig::GetVersion() const
+{
+ return this->version;
+}
+
+void ScriptConfig::StringToSettings(const char *value)
+{
+ char *value_copy = strdup(value);
+ char *s = value_copy;
+
+ while (s != NULL) {
+ /* Analyze the string ('name=value,name=value\0') */
+ char *item_name = s;
+ s = strchr(s, '=');
+ if (s == NULL) break;
+ if (*s == '\0') break;
+ *s = '\0';
+ s++;
+
+ char *item_value = s;
+ s = strchr(s, ',');
+ if (s != NULL) {
+ *s = '\0';
+ s++;
+ }
+
+ this->SetSetting(item_name, atoi(item_value));
+ }
+ free(value_copy);
+}
+
+void ScriptConfig::SettingsToString(char *string, size_t size) const
+{
+ string[0] = '\0';
+ for (SettingValueList::const_iterator it = this->settings.begin(); it != this->settings.end(); it++) {
+ char no[10];
+ snprintf(no, sizeof(no), "%d", (*it).second);
+
+ /* Check if the string would fit in the destination */
+ size_t needed_size = strlen((*it).first) + 1 + strlen(no) + 1;
+ /* If it doesn't fit, skip the next settings */
+ if (size <= needed_size) break;
+ size -= needed_size;
+
+ strcat(string, (*it).first);
+ strcat(string, "=");
+ strcat(string, no);
+ strcat(string, ",");
+ }
+ /* Remove the last ',', but only if at least one setting was saved. */
+ size_t len = strlen(string);
+ if (len > 0) string[len - 1] = '\0';
+}
diff --git a/src/script/script_config.hpp b/src/script/script_config.hpp
new file mode 100644
index 000000000..e16f8999a
--- /dev/null
+++ b/src/script/script_config.hpp
@@ -0,0 +1,191 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file script_config.hpp ScriptConfig stores the configuration settings of every Script. */
+
+#ifndef SCRIPT_CONFIG_HPP
+#define SCRIPT_CONFIG_HPP
+
+#include <map>
+#include <list>
+#include "../core/smallmap_type.hpp"
+#include "../core/string_compare_type.hpp"
+#include "../company_type.h"
+
+/** Bitmask of flags for Script settings. */
+enum ScriptConfigFlags {
+ SCRIPTCONFIG_NONE = 0x0, ///< No flags set.
+ SCRIPTCONFIG_RANDOM = 0x1, ///< When randomizing the Script, pick any value between min_value and max_value when on custom difficulty setting.
+ SCRIPTCONFIG_BOOLEAN = 0x2, ///< This value is a boolean (either 0 (false) or 1 (true) ).
+ SCRIPTCONFIG_INGAME = 0x4, ///< This setting can be changed while the Script is running.
+ SCRIPTCONFIG_DEVELOPER = 0x8, ///< This setting will only be visible when the Script development tools are active.
+};
+
+typedef SmallMap<int, char *> LabelMapping; ///< Map-type used to map the setting numbers to labels.
+
+/** Info about a single Script setting. */
+struct ScriptConfigItem {
+ const char *name; ///< The name of the configuration setting.
+ const char *description; ///< The description of the configuration setting.
+ int min_value; ///< The minimal value this configuration setting can have.
+ int max_value; ///< The maximal value this configuration setting can have.
+ int custom_value; ///< The default value on custom difficulty setting.
+ int easy_value; ///< The default value on easy difficulty setting.
+ int medium_value; ///< The default value on medium difficulty setting.
+ int hard_value; ///< The default value on hard difficulty setting.
+ int random_deviation; ///< The maximum random deviation from the default value.
+ int step_size; ///< The step size in the gui.
+ ScriptConfigFlags flags; ///< Flags for the configuration setting.
+ LabelMapping *labels; ///< Text labels for the integer values.
+};
+
+typedef std::list<ScriptConfigItem> ScriptConfigItemList; ///< List of ScriptConfig items.
+
+extern ScriptConfigItem _start_date_config;
+
+/**
+ * Script settings.
+ */
+class ScriptConfig {
+protected:
+ /** List with name=>value pairs of all script-specific settings */
+ typedef std::map<const char *, int, StringCompare> SettingValueList;
+
+public:
+ ScriptConfig() :
+ name(NULL),
+ version(-1),
+ info(NULL),
+ config_list(NULL),
+ is_random(false)
+ {}
+
+ /**
+ * Create a new Script config that is a copy of an existing config.
+ * @param config The object to copy.
+ */
+ ScriptConfig(const ScriptConfig *config);
+
+ /** Delete an Script configuration. */
+ virtual ~ScriptConfig();
+
+ /**
+ * Set another Script to be loaded in this slot.
+ * @param name The name of the Script.
+ * @param version The version of the Script to load, or -1 of latest.
+ * @param force_exact_match If true try to find the exact same version
+ * as specified. If false any compatible version is ok.
+ * @param is_random Is the Script chosen randomly?
+ */
+ void Change(const char *name, int version = -1, bool force_exact_match = false, bool is_random = false);
+
+ /**
+ * Get the ScriptInfo linked to this ScriptConfig.
+ */
+ class ScriptInfo *GetInfo() const;
+
+ /**
+ * Get the config list for this ScriptConfig.
+ */
+ const ScriptConfigItemList *GetConfigList();
+
+ /**
+ * Where to get the config from, either default (depends on current game
+ * mode) or force either newgame or normal
+ */
+ enum ScriptSettingSource {
+ SSS_DEFAULT, ///< Get the Script config from the current game mode
+ SSS_FORCE_NEWGAME, ///< Get the newgame Script config
+ SSS_FORCE_GAME, ///< Get the Script config from the current game
+ };
+
+ /**
+ * Get the value of a setting for this config. It might fallback to his
+ * 'info' to find the default value (if not set or if not-custom difficulty
+ * level).
+ * @return The (default) value of the setting, or -1 if the setting was not
+ * found.
+ */
+ virtual int GetSetting(const char *name) const;
+
+ /**
+ * Set the value of a setting for this config.
+ */
+ virtual void SetSetting(const char *name, int value);
+
+ /**
+ * Reset all settings to their default value.
+ */
+ void ResetSettings();
+
+ /**
+ * Randomize all settings the Script requested to be randomized.
+ */
+ void AddRandomDeviation();
+
+ /**
+ * Is this config attached to an Script? In other words, is there a Script
+ * that is assigned to this slot.
+ */
+ bool HasScript() const;
+
+ /**
+ * Is the current Script a randomly chosen Script?
+ */
+ bool IsRandom() const;
+
+ /**
+ * Get the name of the Script.
+ */
+ const char *GetName() const;
+
+ /**
+ * Get the version of the Script.
+ */
+ int GetVersion() const;
+
+ /**
+ * Convert a string which is stored in the config file or savegames to
+ * custom settings of this Script.
+ */
+ void StringToSettings(const char *value);
+
+ /**
+ * Convert the custom settings to a string that can be stored in the config
+ * file or savegames.
+ */
+ void SettingsToString(char *string, size_t size) const;
+
+protected:
+ const char *name; ///< Name of the Script
+ int version; ///< Version of the Script
+ class ScriptInfo *info; ///< ScriptInfo object for related to this Script version
+ SettingValueList settings; ///< List with all setting=>value pairs that are configure for this Script
+ ScriptConfigItemList *config_list; ///< List with all settings defined by this Script
+ bool is_random; ///< True if the AI in this slot was randomly chosen.
+
+ /**
+ * In case you have mandatory non-Script-definable config entries in your
+ * list, add them to this function.
+ */
+ virtual void PushExtraConfigList() {};
+
+ /**
+ * Routine that clears the config list.
+ */
+ virtual void ClearConfigList();
+
+ /**
+ * This function should call back to the Scanner in charge of this Config,
+ * to find the ScriptInfo belonging to a name+version.
+ */
+ virtual ScriptInfo *FindInfo(const char *name, int version, bool force_exact_match) = 0;
+};
+
+#endif /* SCRIPT_CONFIG_HPP */
diff --git a/src/script/script_info.cpp b/src/script/script_info.cpp
index 862907c8b..afa90f40e 100644
--- a/src/script/script_info.cpp
+++ b/src/script/script_info.cpp
@@ -10,6 +10,7 @@
/** @file script_info.cpp Implementation of ScriptInfo. */
#include "../stdafx.h"
+#include "../settings_type.h"
#include "squirrel_helper.hpp"
@@ -20,9 +21,25 @@
static const int MAX_GET_OPS = 1000;
/** Number of operations to create an instance of a script. */
static const int MAX_CREATEINSTANCE_OPS = 100000;
+/** Maximum number of operations allowed for getting a particular setting. */
+static const int MAX_GET_SETTING_OPS = 100000;
+
ScriptInfo::~ScriptInfo()
{
+ /* Free all allocated strings */
+ for (ScriptConfigItemList::iterator it = this->config_list.begin(); it != this->config_list.end(); it++) {
+ free((*it).name);
+ free((*it).description);
+ if (it->labels != NULL) {
+ for (LabelMapping::iterator it2 = (*it).labels->Begin(); it2 != (*it).labels->End(); it2++) {
+ free(it2->second);
+ }
+ delete it->labels;
+ }
+ }
+ this->config_list.clear();
+
free(this->author);
free(this->name);
free(this->short_name);
@@ -90,5 +107,200 @@ bool ScriptInfo::CheckMethod(const char *name) const
if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetURL", &info->url, MAX_GET_OPS)) return SQ_ERROR;
}
+ /* Check if we have settings */
+ if (info->engine->MethodExists(*info->SQ_instance, "GetSettings")) {
+ if (!info->GetSettings()) return SQ_ERROR;
+ }
+
return 0;
}
+
+bool ScriptInfo::GetSettings()
+{
+ return this->engine->CallMethod(*this->SQ_instance, "GetSettings", NULL, MAX_GET_SETTING_OPS);
+}
+
+SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm)
+{
+ ScriptConfigItem config;
+ memset(&config, 0, sizeof(config));
+ config.max_value = 1;
+ config.step_size = 1;
+ uint items = 0;
+
+ /* Read the table, and find all properties we care about */
+ sq_pushnull(vm);
+ while (SQ_SUCCEEDED(sq_next(vm, -2))) {
+ const SQChar *sqkey;
+ if (SQ_FAILED(sq_getstring(vm, -2, &sqkey))) return SQ_ERROR;
+ const char *key = SQ2OTTD(sqkey);
+
+ if (strcmp(key, "name") == 0) {
+ const SQChar *sqvalue;
+ if (SQ_FAILED(sq_getstring(vm, -1, &sqvalue))) return SQ_ERROR;
+ char *name = strdup(SQ2OTTD(sqvalue));
+ char *s;
+ /* Don't allow '=' and ',' in configure setting names, as we need those
+ * 2 chars to nicely store the settings as a string. */
+ while ((s = strchr(name, '=')) != NULL) *s = '_';
+ while ((s = strchr(name, ',')) != NULL) *s = '_';
+ config.name = name;
+ items |= 0x001;
+ } else if (strcmp(key, "description") == 0) {
+ const SQChar *sqdescription;
+ if (SQ_FAILED(sq_getstring(vm, -1, &sqdescription))) return SQ_ERROR;
+ config.description = strdup(SQ2OTTD(sqdescription));
+ items |= 0x002;
+ } else if (strcmp(key, "min_value") == 0) {
+ SQInteger res;
+ if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
+ config.min_value = res;
+ items |= 0x004;
+ } else if (strcmp(key, "max_value") == 0) {
+ SQInteger res;
+ if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
+ config.max_value = res;
+ items |= 0x008;
+ } else if (strcmp(key, "easy_value") == 0) {
+ SQInteger res;
+ if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
+ config.easy_value = res;
+ items |= 0x010;
+ } else if (strcmp(key, "medium_value") == 0) {
+ SQInteger res;
+ if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
+ config.medium_value = res;
+ items |= 0x020;
+ } else if (strcmp(key, "hard_value") == 0) {
+ SQInteger res;
+ if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
+ config.hard_value = res;
+ items |= 0x040;
+ } else if (strcmp(key, "random_deviation") == 0) {
+ SQInteger res;
+ if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
+ config.random_deviation = res;
+ items |= 0x200;
+ } else if (strcmp(key, "custom_value") == 0) {
+ SQInteger res;
+ if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
+ config.custom_value = res;
+ items |= 0x080;
+ } else if (strcmp(key, "step_size") == 0) {
+ SQInteger res;
+ if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
+ config.step_size = res;
+ } else if (strcmp(key, "flags") == 0) {
+ SQInteger res;
+ if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
+ config.flags = (ScriptConfigFlags)res;
+ items |= 0x100;
+ } else {
+ char error[1024];
+ snprintf(error, sizeof(error), "unknown setting property '%s'", key);
+ this->engine->ThrowError(error);
+ return SQ_ERROR;
+ }
+
+ sq_pop(vm, 2);
+ }
+ sq_pop(vm, 1);
+
+ /* Don't allow both random_deviation and SCRIPTCONFIG_RANDOM to
+ * be set for the same config item. */
+ if ((items & 0x200) != 0 && (config.flags & SCRIPTCONFIG_RANDOM) != 0) {
+ char error[1024];
+ snprintf(error, sizeof(error), "Setting both random_deviation and SCRIPTCONFIG_RANDOM is not allowed");
+ this->engine->ThrowError(error);
+ 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) {
+ char error[1024];
+ snprintf(error, sizeof(error), "please define all properties of a setting (min/max not allowed for booleans)");
+ this->engine->ThrowError(error);
+ return SQ_ERROR;
+ }
+
+ this->config_list.push_back(config);
+ return 0;
+}
+
+SQInteger ScriptInfo::AddLabels(HSQUIRRELVM vm)
+{
+ const SQChar *sq_setting_name;
+ if (SQ_FAILED(sq_getstring(vm, -2, &sq_setting_name))) return SQ_ERROR;
+ const char *setting_name = SQ2OTTD(sq_setting_name);
+
+ ScriptConfigItem *config = NULL;
+ for (ScriptConfigItemList::iterator it = this->config_list.begin(); it != this->config_list.end(); it++) {
+ if (strcmp((*it).name, setting_name) == 0) config = &(*it);
+ }
+
+ if (config == NULL) {
+ char error[1024];
+ snprintf(error, sizeof(error), "Trying to add labels for non-defined setting '%s'", setting_name);
+ this->engine->ThrowError(error);
+ return SQ_ERROR;
+ }
+ if (config->labels != NULL) return SQ_ERROR;
+
+ config->labels = new LabelMapping;
+
+ /* Read the table and find all labels */
+ sq_pushnull(vm);
+ while (SQ_SUCCEEDED(sq_next(vm, -2))) {
+ const SQChar *sq_key;
+ const SQChar *sq_label;
+ if (SQ_FAILED(sq_getstring(vm, -2, &sq_key))) return SQ_ERROR;
+ if (SQ_FAILED(sq_getstring(vm, -1, &sq_label))) return SQ_ERROR;
+ /* Because squirrel doesn't support identifiers starting with a digit,
+ * we skip the first character. */
+ const char *key_string = SQ2OTTD(sq_key);
+ int key = atoi(key_string + 1);
+ const char *label = SQ2OTTD(sq_label);
+
+ /* !Contains() prevents strdup from leaking. */
+ if (!config->labels->Contains(key)) config->labels->Insert(key, strdup(label));
+
+ sq_pop(vm, 2);
+ }
+ sq_pop(vm, 1);
+
+ return 0;
+}
+
+const ScriptConfigItemList *ScriptInfo::GetConfigList() const
+{
+ return &this->config_list;
+}
+
+const ScriptConfigItem *ScriptInfo::GetConfigItem(const char *name) const
+{
+ for (ScriptConfigItemList::const_iterator it = this->config_list.begin(); it != this->config_list.end(); it++) {
+ if (strcmp((*it).name, name) == 0) return &(*it);
+ }
+ return NULL;
+}
+
+int ScriptInfo::GetSettingDefaultValue(const char *name) const
+{
+ for (ScriptConfigItemList::const_iterator it = this->config_list.begin(); it != this->config_list.end(); it++) {
+ if (strcmp((*it).name, name) != 0) continue;
+ /* The default value depends on the difficulty level */
+ switch (GetGameSettings().difficulty.diff_level) {
+ case 0: return (*it).easy_value;
+ case 1: return (*it).medium_value;
+ case 2: return (*it).hard_value;
+ case 3: return (*it).custom_value;
+ default: NOT_REACHED();
+ }
+ }
+
+ /* There is no such setting */
+ return -1;
+}
diff --git a/src/script/script_info.hpp b/src/script/script_info.hpp
index 1e118f164..b686d53f4 100644
--- a/src/script/script_info.hpp
+++ b/src/script/script_info.hpp
@@ -15,6 +15,8 @@
#include <squirrel.h>
#include "../misc/countedptr.hpp"
+#include "script_config.hpp"
+
class ScriptInfo : public SimpleCountedObject {
public:
ScriptInfo() :
@@ -98,9 +100,41 @@ public:
*/
virtual class ScriptScanner *GetScanner() { return this->scanner; }
+ /**
+ * Get the settings of the Script.
+ */
+ bool GetSettings();
+
+ /**
+ * Get the config list for this Script.
+ */
+ const ScriptConfigItemList *GetConfigList() const;
+
+ /**
+ * Get the description of a certain Script config option.
+ */
+ const ScriptConfigItem *GetConfigItem(const char *name) const;
+
+ /**
+ * Set a setting.
+ */
+ SQInteger AddSetting(HSQUIRRELVM vm);
+
+ /**
+ * Add labels for a setting.
+ */
+ SQInteger AddLabels(HSQUIRRELVM vm);
+
+ /**
+ * Get the default value for a setting.
+ */
+ int GetSettingDefaultValue(const char *name) const;
+
+
protected:
- class Squirrel *engine; ///< Engine used to register for Squirrel.
- HSQOBJECT *SQ_instance; ///< The Squirrel instance created for this info.
+ class Squirrel *engine; ///< Engine used to register for Squirrel.
+ HSQOBJECT *SQ_instance; ///< The Squirrel instance created for this info.
+ ScriptConfigItemList config_list; ///< List of settings from this Script.
private:
char *main_script; ///< The full path of the script.
diff --git a/src/settings.cpp b/src/settings.cpp
index b5b5c0d52..ddbf7f583 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -1320,7 +1320,7 @@ static void AILoadConfig(IniFile *ini, const char *grpname)
/* Clean any configured AI */
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
- AIConfig::GetConfig(c, AIConfig::AISS_FORCE_NEWGAME)->ChangeAI(NULL);
+ AIConfig::GetConfig(c, AIConfig::SSS_FORCE_NEWGAME)->Change(NULL);
}
/* If no group exists, return */
@@ -1328,10 +1328,10 @@ static void AILoadConfig(IniFile *ini, const char *grpname)
CompanyID c = COMPANY_FIRST;
for (item = group->item; c < MAX_COMPANIES && item != NULL; c++, item = item->next) {
- AIConfig *config = AIConfig::GetConfig(c, AIConfig::AISS_FORCE_NEWGAME);
+ AIConfig *config = AIConfig::GetConfig(c, AIConfig::SSS_FORCE_NEWGAME);
- config->ChangeAI(item->name);
- if (!config->HasAI()) {
+ config->Change(item->name);
+ if (!config->HasScript()) {
if (strcmp(item->name, "none") != 0) {
DEBUG(ai, 0, "The AI by the name '%s' was no longer found, and removed from the list.", item->name);
continue;
@@ -1443,12 +1443,12 @@ static void AISaveConfig(IniFile *ini, const char *grpname)
group->Clear();
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
- AIConfig *config = AIConfig::GetConfig(c, AIConfig::AISS_FORCE_NEWGAME);
+ AIConfig *config = AIConfig::GetConfig(c, AIConfig::SSS_FORCE_NEWGAME);
const char *name;
char value[1024];
config->SettingsToString(value, lengthof(value));
- if (config->HasAI()) {
+ if (config->HasScript()) {
name = config->GetName();
} else {
name = "none";