mirror of https://github.com/OpenTTD/OpenTTD
(svn r15045) -Add [NoAI API CHANGE]: in info.nut you can now have (optional) a CanLoadFromVersion(version), which should return true/false, to indicate if you can load a savegame made with your AI of version 'version'
-Add [NoAI API CHANGE]: in main.nut the Load() function now should be Load(version, data), where 'version' is the version of your AI which made the savegame -Codechange [NoAI]: various of function renames to make things more sane -Add [NoAI]: push the 'version' of the AI through various of layers -Codechange [NoAI]: various of code cleanups -Add [NoAI]: store the version of the AI in the savegame toorelease/0.7
parent
e6883c5cc7
commit
bcbbf2c366
|
@ -86,11 +86,11 @@ public:
|
|||
/**
|
||||
* Load data for an AI from a savegame.
|
||||
*/
|
||||
static void Load(CompanyID company);
|
||||
static void Load(CompanyID company, int version);
|
||||
|
||||
static char *GetConsoleList(char *p, const char *last);
|
||||
static const AIInfoList *GetInfoList();
|
||||
static AIInfo *GetCompanyInfo(const char *name);
|
||||
static AIInfo *FindInfo(const char *name, int version);
|
||||
static bool ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm);
|
||||
static void Rescan();
|
||||
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
#include "ai_config.hpp"
|
||||
#include "ai_info.hpp"
|
||||
|
||||
void AIConfig::ChangeAI(const char *name)
|
||||
void AIConfig::ChangeAI(const char *name, int version)
|
||||
{
|
||||
free((void *)this->name);
|
||||
this->name = (name == NULL) ? NULL : strdup(name);
|
||||
this->info = (name == NULL) ? NULL : AI::GetCompanyInfo(this->name);
|
||||
this->info = (name == NULL) ? NULL : AI::FindInfo(this->name, version);
|
||||
this->version = (info == NULL) ? -1 : info->GetVersion();
|
||||
|
||||
for (SettingValueList::iterator it = this->settings.begin(); it != this->settings.end(); it++) {
|
||||
|
@ -45,7 +45,7 @@ AIInfo *AIConfig::GetInfo()
|
|||
|
||||
bool AIConfig::ResetInfo()
|
||||
{
|
||||
this->info = AI::GetCompanyInfo(this->name);
|
||||
this->info = AI::FindInfo(this->name, this->version);
|
||||
return this->info != NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,8 +26,10 @@ public:
|
|||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void ChangeAI(const char *name);
|
||||
void ChangeAI(const char *name, int version = -1);
|
||||
|
||||
/**
|
||||
* When ever the AI Scanner is reloaded, all infos become invalid. This
|
||||
|
|
|
@ -211,7 +211,7 @@ void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2)
|
|||
}
|
||||
}
|
||||
|
||||
/* static */ void AI::Load(CompanyID company)
|
||||
/* static */ void AI::Load(CompanyID company, int version)
|
||||
{
|
||||
if (!_networking || _network_server) {
|
||||
assert(IsValidCompanyID(company));
|
||||
|
@ -219,7 +219,7 @@ void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2)
|
|||
|
||||
CompanyID old_company = _current_company;
|
||||
_current_company = company;
|
||||
GetCompany(company)->ai_instance->Load();
|
||||
GetCompany(company)->ai_instance->Load(version);
|
||||
_current_company = old_company;
|
||||
} else {
|
||||
/* Read, but ignore, the load data */
|
||||
|
@ -237,9 +237,9 @@ void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2)
|
|||
return AI::ai_scanner->GetAIInfoList();
|
||||
}
|
||||
|
||||
/* static */ AIInfo *AI::GetCompanyInfo(const char *name)
|
||||
/* static */ AIInfo *AI::FindInfo(const char *name, int version)
|
||||
{
|
||||
return AI::ai_scanner->FindAI(name);
|
||||
return AI::ai_scanner->FindInfo(name, version);
|
||||
}
|
||||
|
||||
/* static */ bool AI::ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm)
|
||||
|
|
|
@ -70,9 +70,26 @@ const char *AIFileInfo::GetInstanceName()
|
|||
return this->instance_name;
|
||||
}
|
||||
|
||||
bool AIFileInfo::AllowStartup()
|
||||
bool AIFileInfo::CanLoadFromVersion(int version)
|
||||
{
|
||||
return true;
|
||||
if (version == -1) return true;
|
||||
if (!this->engine->MethodExists(*this->SQ_instance, "CanLoadFromVersion")) return true;
|
||||
|
||||
HSQUIRRELVM vm = this->engine->GetVM();
|
||||
int top = sq_gettop(vm);
|
||||
|
||||
sq_pushobject(vm, *this->SQ_instance);
|
||||
sq_pushstring(vm, OTTD2FS("CanLoadFromVersion"), -1);
|
||||
sq_get(vm, -2);
|
||||
sq_pushobject(vm, *this->SQ_instance);
|
||||
sq_pushinteger(vm, version);
|
||||
sq_call(vm, 2, SQTrue, SQFalse);
|
||||
|
||||
HSQOBJECT ret;
|
||||
sq_getstackobj(vm, -1, &ret);
|
||||
|
||||
sq_settop(vm, top);
|
||||
return sq_objtobool(&ret);
|
||||
}
|
||||
|
||||
const char *AIFileInfo::GetDirName()
|
||||
|
|
|
@ -74,7 +74,7 @@ public:
|
|||
/**
|
||||
* Check if we can start this AI.
|
||||
*/
|
||||
bool AllowStartup();
|
||||
bool CanLoadFromVersion(int version);
|
||||
|
||||
/**
|
||||
* Get the name of the dir this AI is in.
|
||||
|
|
|
@ -592,37 +592,38 @@ void AIInstance::Save()
|
|||
LoadObjects(NULL);
|
||||
}
|
||||
|
||||
bool AIInstance::Load()
|
||||
void AIInstance::Load(int version)
|
||||
{
|
||||
HSQUIRRELVM vm = (this->engine == NULL) ? NULL : this->engine->GetVM();
|
||||
if (this->engine == NULL || version == -1) {
|
||||
LoadEmpty();
|
||||
return;
|
||||
}
|
||||
HSQUIRRELVM vm = this->engine->GetVM();
|
||||
|
||||
SlObject(NULL, _ai_byte);
|
||||
/* Check if there was anything saved at all. */
|
||||
if (_ai_sl_byte == 0) return true;
|
||||
if (_ai_sl_byte == 0) return;
|
||||
AIObject::SetAllowDoCommand(false);
|
||||
|
||||
if (vm != NULL) {
|
||||
/* Go to the instance-root */
|
||||
sq_pushobject(vm, *this->instance);
|
||||
/* Find the function-name inside the script */
|
||||
sq_pushstring(vm, OTTD2FS("Load"), -1);
|
||||
if (SQ_FAILED(sq_get(vm, -2))) sq_pushnull(vm);
|
||||
sq_pushobject(vm, *this->instance);
|
||||
}
|
||||
/* Go to the instance-root */
|
||||
sq_pushobject(vm, *this->instance);
|
||||
/* Find the function-name inside the script */
|
||||
sq_pushstring(vm, OTTD2FS("Load"), -1);
|
||||
if (SQ_FAILED(sq_get(vm, -2))) sq_pushnull(vm);
|
||||
sq_pushobject(vm, *this->instance);
|
||||
sq_pushinteger(vm, version);
|
||||
|
||||
LoadObjects(vm);
|
||||
|
||||
if (this->engine != NULL) {
|
||||
if (this->engine->MethodExists(*this->instance, "Load")) {
|
||||
sq_call(vm, 2, SQFalse, SQFalse);
|
||||
} else {
|
||||
AILog::Warning("Loading failed: there was data for the AI to load, but the AI does not have a Load() function.");
|
||||
}
|
||||
if (this->engine->MethodExists(*this->instance, "Load")) {
|
||||
sq_call(vm, 3, SQFalse, SQFalse);
|
||||
} else {
|
||||
AILog::Warning("Loading failed: there was data for the AI to load, but the AI does not have a Load() function.");
|
||||
}
|
||||
|
||||
/* Pop 1) the object instance, 2) the function name, 3) the instance again, 4) the table. */
|
||||
if (vm != NULL) sq_pop(vm, 4);
|
||||
/* Pop 1) the object instance, 2) the function name, 3) the instance again, 4) the (null) result. */
|
||||
sq_pop(vm, 4);
|
||||
|
||||
AIObject::SetAllowDoCommand(true);
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -87,9 +87,10 @@ public:
|
|||
/**
|
||||
* Load data from a savegame and call the AI Load function if it
|
||||
* exists.
|
||||
* @return True if the loading was successfull.
|
||||
* @param version The version of the AI when saving, or -1 if this was
|
||||
* not the original AI saving the game.
|
||||
*/
|
||||
bool Load();
|
||||
void Load(int version);
|
||||
|
||||
/**
|
||||
* Load and discard data from a savegame.
|
||||
|
|
|
@ -344,26 +344,10 @@ AIInfo *AIScanner::SelectRandomAI()
|
|||
AIInfoList::iterator it = this->info_list.begin();
|
||||
for (; pos > 0; pos--) it++;
|
||||
AIInfoList::iterator first_it = it;
|
||||
AIInfo *i = (*it).second;
|
||||
|
||||
if (!i->AllowStartup()) {
|
||||
/* We can't start this AI, try to find the next best */
|
||||
do {
|
||||
it++;
|
||||
if (it == this->info_list.end()) it = this->info_list.begin();
|
||||
/* Back at the beginning? We can't start an AI. */
|
||||
if (first_it == it) {
|
||||
DEBUG(ai, 0, "No suitable AI found, loading 'dummy' AI.");
|
||||
return this->info_dummy;
|
||||
}
|
||||
|
||||
i = (*it).second;
|
||||
} while (!i->AllowStartup());
|
||||
}
|
||||
return i;
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
AIInfo *AIScanner::FindAI(const char *name)
|
||||
AIInfo *AIScanner::FindInfo(const char *name, int version)
|
||||
{
|
||||
if (this->info_list.size() == 0) return NULL;
|
||||
if (name == NULL) return NULL;
|
||||
|
@ -372,7 +356,7 @@ AIInfo *AIScanner::FindAI(const char *name)
|
|||
for (; it != this->info_list.end(); it++) {
|
||||
AIInfo *i = (*it).second;
|
||||
|
||||
if (strcasecmp(name, (*it).first) == 0 && i->AllowStartup()) {
|
||||
if (strcasecmp(name, (*it).first) == 0 && i->CanLoadFromVersion(version)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
@ -386,8 +370,7 @@ char *AIScanner::GetAIConsoleList(char *p, const char *last)
|
|||
AIInfoList::iterator it = this->info_list.begin();
|
||||
for (; it != this->info_list.end(); it++) {
|
||||
AIInfo *i = (*it).second;
|
||||
if (!i->AllowStartup()) continue;
|
||||
p += seprintf(p, last, "%10s: %s\n", (*it).first, i->GetDescription());
|
||||
p += seprintf(p, last, "%10s (v%d): %s\n", (*it).first, i->GetVersion(), i->GetDescription());
|
||||
}
|
||||
p += seprintf(p, last, "\n");
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
/**
|
||||
* Find an AI by name.
|
||||
*/
|
||||
class AIInfo *FindAI(const char *name);
|
||||
class AIInfo *FindInfo(const char *name, int version);
|
||||
|
||||
/**
|
||||
* Get the list of available AIs for the console.
|
||||
|
|
|
@ -13,12 +13,14 @@
|
|||
#include "../ai/ai.hpp"
|
||||
#include "../ai/ai_config.hpp"
|
||||
|
||||
static char _ai_saveload_ainame[64];
|
||||
static char _ai_company_convert_array[1024];
|
||||
static char _ai_saveload_name[64];
|
||||
static int _ai_saveload_version;
|
||||
static char _ai_saveload_settings[1024];
|
||||
|
||||
static const SaveLoad _ai_company[] = {
|
||||
SLEG_STR(_ai_saveload_ainame, SLE_STRB),
|
||||
SLEG_STR(_ai_company_convert_array, SLE_STRB),
|
||||
SLEG_STR(_ai_saveload_name, SLE_STRB),
|
||||
SLEG_STR(_ai_saveload_settings, SLE_STRB),
|
||||
SLEG_CONDVAR(_ai_saveload_version, SLE_UINT32, 108, SL_MAX_VERSION),
|
||||
SLE_END()
|
||||
};
|
||||
|
||||
|
@ -27,10 +29,11 @@ static void SaveReal_AIPL(int *index_ptr)
|
|||
CompanyID index = (CompanyID)*index_ptr;
|
||||
AIConfig *config = AIConfig::GetConfig(index);
|
||||
|
||||
ttd_strlcpy(_ai_saveload_ainame, config->GetName(), lengthof(_ai_saveload_ainame));
|
||||
ttd_strlcpy(_ai_saveload_name, config->GetName(), lengthof(_ai_saveload_name));
|
||||
_ai_saveload_version = config->GetVersion();
|
||||
|
||||
_ai_company_convert_array[0] = '\0';
|
||||
config->SettingsToString(_ai_company_convert_array, lengthof(_ai_company_convert_array));
|
||||
_ai_saveload_settings[0] = '\0';
|
||||
config->SettingsToString(_ai_saveload_settings, lengthof(_ai_saveload_settings));
|
||||
|
||||
SlObject(NULL, _ai_company);
|
||||
/* If the AI was active, store his data too */
|
||||
|
@ -47,27 +50,30 @@ static void Load_AIPL()
|
|||
CompanyID index;
|
||||
while ((index = (CompanyID)SlIterateArray()) != (CompanyID)-1) {
|
||||
AIConfig *config = AIConfig::GetConfig(index);
|
||||
|
||||
_ai_saveload_version = -1;
|
||||
SlObject(NULL, _ai_company);
|
||||
|
||||
if (_ai_saveload_ainame[0] == '\0' || AI::GetCompanyInfo(_ai_saveload_ainame) == NULL) {
|
||||
if (strcmp(_ai_saveload_ainame, "%_dummy") != 0) {
|
||||
DEBUG(ai, 0, "The savegame has an AI by the name '%s' which is no longer available.", _ai_saveload_ainame);
|
||||
config->ChangeAI(_ai_saveload_name, _ai_saveload_version);
|
||||
if (!config->HasAI()) {
|
||||
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.");
|
||||
} else {
|
||||
DEBUG(ai, 0, "The savegame had no AIs available at the time of saving.");
|
||||
DEBUG(ai, 0, "A random available AI will be loaded now.");
|
||||
}
|
||||
config->ChangeAI(NULL);
|
||||
} else {
|
||||
config->ChangeAI(_ai_saveload_ainame);
|
||||
/* Make sure the AI doesn't get the saveload data, as he was not the
|
||||
* writer of the saveload data in the first place */
|
||||
_ai_saveload_version = -1;
|
||||
}
|
||||
|
||||
config->StringToSettings(_ai_company_convert_array);
|
||||
config->StringToSettings(_ai_saveload_settings);
|
||||
|
||||
/* Start the AI directly if it was active in the savegame */
|
||||
if (IsValidCompanyID(index) && !IsHumanCompany(index)) {
|
||||
AI::StartNew(index);
|
||||
AI::Load(index);
|
||||
AI::Load(index, _ai_saveload_version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
|
||||
#include <list>
|
||||
|
||||
extern const uint16 SAVEGAME_VERSION = 107;
|
||||
extern const uint16 SAVEGAME_VERSION = 108;
|
||||
|
||||
SavegameType _savegame_type; ///< type of savegame we are loading
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ public:
|
|||
friend class AIController;
|
||||
friend class AIScanner;
|
||||
friend class AIInstance;
|
||||
friend class AIFileInfo;
|
||||
|
||||
Squirrel();
|
||||
~Squirrel();
|
||||
|
|
Loading…
Reference in New Issue