Changeset - r18510:9d33e9643f80
[Not reviewed]
master
0 19 2
truebrain - 13 years ago 2011-11-29 23:26:35
truebrain@openttd.org
(svn r23364) -Codechange: refactor AIConfig, moving it mostly to Scriptconfig
21 files changed with 817 insertions and 645 deletions:
0 comments (0 inline, 0 general)
projects/openttd_vs100.vcxproj
Show inline comments
 
@@ -788,6 +788,8 @@
 
    <ClInclude Include="..\src\table\water_land.h" />
 
    <ClCompile Include="..\src\3rdparty\md5\md5.cpp" />
 
    <ClInclude Include="..\src\3rdparty\md5\md5.h" />
 
    <ClCompile Include="..\src\script\script_config.cpp" />
 
    <ClInclude Include="..\src\script\script_config.hpp" />
 
    <ClInclude Include="..\src\script\script_fatalerror.hpp" />
 
    <ClCompile Include="..\src\script\script_info.cpp" />
 
    <ClInclude Include="..\src\script\script_info.hpp" />
projects/openttd_vs100.vcxproj.filters
Show inline comments
 
@@ -1587,6 +1587,12 @@
 
    <ClInclude Include="..\src\3rdparty\md5\md5.h">
 
      <Filter>MD5</Filter>
 
    </ClInclude>
 
    <ClCompile Include="..\src\script\script_config.cpp">
 
      <Filter>Script</Filter>
 
    </ClCompile>
 
    <ClInclude Include="..\src\script\script_config.hpp">
 
      <Filter>Script</Filter>
 
    </ClInclude>
 
    <ClInclude Include="..\src\script\script_fatalerror.hpp">
 
      <Filter>Script</Filter>
 
    </ClInclude>
projects/openttd_vs80.vcproj
Show inline comments
 
@@ -2459,6 +2459,14 @@
 
			Name="Script"
 
			>
 
			<File
 
				RelativePath=".\..\src\script\script_config.cpp"
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\script\script_config.hpp"
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\script\script_fatalerror.hpp"
 
				>
 
			</File>
projects/openttd_vs90.vcproj
Show inline comments
 
@@ -2456,6 +2456,14 @@
 
			Name="Script"
 
			>
 
			<File
 
				RelativePath=".\..\src\script\script_config.cpp"
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\script\script_config.hpp"
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\script\script_fatalerror.hpp"
 
				>
 
			</File>
source.list
Show inline comments
 
@@ -553,6 +553,8 @@ 3rdparty/md5/md5.h
 

	
 
#if AI
 
# Script
 
script/script_config.cpp
 
script/script_config.hpp
 
script/script_fatalerror.hpp
 
script/script_info.cpp
 
script/script_info.hpp
src/ai/ai_config.cpp
Show inline comments
 
@@ -14,87 +14,28 @@
 
#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();
 
	}
 
}
 
#include "ai_info.hpp"
 

	
 
AIConfig::AIConfig(const AIConfig *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_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;
 
	}
 
	this->AddRandomDeviation();
 
}
 

	
 
AIConfig::~AIConfig()
 
{
 
	free(this->name);
 
	this->ResetSettings();
 
	if (this->config_list != NULL) delete this->config_list;
 
}
 
/** 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
 
};
 

	
 
AIInfo *AIConfig::GetInfo() const
 
{
 
	return this->info;
 
}
 

	
 
bool AIConfig::ResetInfo(bool force_exact_match)
 
{
 
	this->info = AI::FindInfo(this->name, force_exact_match ? this->version : -1, force_exact_match);
 
	return this->info != NULL;
 
}
 

	
 
const AIConfigItemList *AIConfig::GetConfigList()
 
{
 
	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;
 
}
 

	
 
AIConfig *AIConfig::GetConfig(CompanyID company, AISettingSource source)
 
/* static */ AIConfig *AIConfig::GetConfig(CompanyID company, ScriptSettingSource source)
 
{
 
	AIConfig **config;
 
	if (source == AISS_FORCE_NEWGAME || (source == AISS_DEFAULT && _game_mode == GM_MENU)) {
 
	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];
 
@@ -103,12 +44,43 @@ AIConfig *AIConfig::GetConfig(CompanyID 
 
	return *config;
 
}
 

	
 
class AIInfo *AIConfig::GetInfo() const
 
{
 
	return static_cast<class AIInfo *>(ScriptConfig::GetInfo());
 
}
 

	
 
ScriptInfo *AIConfig::FindInfo(const char *name, int version, bool force_exact_match)
 
{
 
	return static_cast<ScriptInfo *>(AI::FindInfo(name, version, force_exact_match));
 
}
 

	
 
bool AIConfig::ResetInfo(bool force_exact_match)
 
{
 
	this->info = (ScriptInfo *)AI::FindInfo(this->name, force_exact_match ? this->version : -1, force_exact_match);
 
	return this->info != NULL;
 
}
 

	
 
void AIConfig::PushExtraConfigList()
 
{
 
	this->config_list->push_back(_start_date_config);
 
}
 

	
 
void AIConfig::ClearConfigList()
 
{
 
	/* 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 *nam
 
				default: NOT_REACHED();
 
			}
 
		}
 
		return this->info->GetSettingDefaultValue(name);
 

	
 
		return (*it).second;
 
	}
 
	return (*it).second;
 

	
 
	return ScriptConfig::GetSetting(name);
 
}
 

	
 
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) {
 
	if (this->info == NULL) {
 
		if (strcmp("start_date", name) != 0) return;
 
		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;
 
	}
 

	
 
	SettingValueList::iterator it = this->settings.find(name);
 
	if (it != this->settings.end()) {
 
		(*it).second = value;
 
	} else {
 
		this->settings[strdup(name)] = value;
 
	}
 
}
 

	
 
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));
 
		}
 
	}
 
}
 

	
 
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
 
{
 
	return this->version;
 
	ScriptConfig::SetSetting(name, value);
 
}
 

	
 
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++;
 
		}
 

	
 
		this->SetSetting(item_name, atoi(item_value));
 
	}
 
	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';
 
}
src/ai/ai_config.hpp
Show inline comments
 
@@ -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"
 
#include "../script/script_config.hpp"
 

	
 
/**
 
 * 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;
 
class AIConfig : public ScriptConfig {
 
public:
 
	/**
 
	 * Get the config of a company.
 
	 */
 
	static AIConfig *GetConfig(CompanyID company, ScriptSettingSource source = SSS_DEFAULT);
 

	
 
public:
 
	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);
 

	
 
	/** Delete an AI configuration. */
 
	~AIConfig();
 
	AIConfig(const AIConfig *config) :
 
		ScriptConfig(config)
 
	{}
 

	
 
	/**
 
	 * 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);
 
	class AIInfo *GetInfo() const;
 

	
 
	/* 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 */
src/ai/ai_core.cpp
Show inline comments
 
@@ -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);
 
			}
 
		}
 
	}
src/ai/ai_gui.cpp
Show inline comments
 
@@ -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();
src/ai/ai_info.cpp
Show inline comments
 
@@ -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<AII
 
	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<AII
 
	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<AII
 
	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 vers
 
	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()
 
{
src/ai/ai_info.hpp
Show inline comments
 
@@ -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. */
src/ai/ai_instance.cpp
Show inline comments
 
@@ -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.
src/ai/api/ai_changelog.hpp
Show inline comments
 
@@ -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.
src/console_cmds.cpp
Show inline comments
 
@@ -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;
 
		}
src/saveload/ai_sl.cpp
Show inline comments
 
@@ -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.");
src/script/api/script_controller.cpp
Show inline comments
 
@@ -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);
src/script/script_config.cpp
Show inline comments
 
new file 100644
 
/* $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';
 
}
src/script/script_config.hpp
Show inline comments
 
new file 100644
 
/* $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 */
src/script/script_info.cpp
Show inline comments
 
@@ -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 
 
		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;
 
}
src/script/script_info.hpp
Show inline comments
 
@@ -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.
src/settings.cpp
Show inline comments
 
@@ -1320,7 +1320,7 @@ static void AILoadConfig(IniFile *ini, c
 

	
 
	/* 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, c
 

	
 
	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, c
 
	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";
0 comments (0 inline, 0 general)