ai_instance.cpp

Go to the documentation of this file.
00001 /* $Id: ai_instance.cpp 16481 2009-05-31 12:18:03Z rubidium $ */
00002 
00005 #include "../stdafx.h"
00006 #include "../debug.h"
00007 #include "../settings_type.h"
00008 #include "../vehicle_base.h"
00009 #include "../saveload/saveload.h"
00010 #include "../gui.h"
00011 #include "table/strings.h"
00012 
00013 #include <squirrel.h>
00014 #include "../script/squirrel.hpp"
00015 #include "../script/squirrel_helper.hpp"
00016 #include "../script/squirrel_class.hpp"
00017 #include "../script/squirrel_std.hpp"
00018 
00019 #define DEFINE_SCRIPT_FILES
00020 
00021 #include "ai_info.hpp"
00022 #include "ai_config.hpp"
00023 #include "ai_storage.hpp"
00024 #include "ai_instance.hpp"
00025 #include "ai_gui.hpp"
00026 
00027 /* Convert all AI related classes to Squirrel data.
00028  * Note: this line a marker in squirrel_export.sh. Do not change! */
00029 #include "api/ai_abstractlist.hpp.sq"
00030 #include "api/ai_accounting.hpp.sq"
00031 #include "api/ai_airport.hpp.sq"
00032 #include "api/ai_base.hpp.sq"
00033 #include "api/ai_bridge.hpp.sq"
00034 #include "api/ai_bridgelist.hpp.sq"
00035 #include "api/ai_cargo.hpp.sq"
00036 #include "api/ai_cargolist.hpp.sq"
00037 #include "api/ai_company.hpp.sq"
00038 #include "api/ai_controller.hpp.sq"
00039 #include "api/ai_date.hpp.sq"
00040 #include "api/ai_depotlist.hpp.sq"
00041 #include "api/ai_engine.hpp.sq"
00042 #include "api/ai_enginelist.hpp.sq"
00043 #include "api/ai_error.hpp.sq"
00044 #include "api/ai_event.hpp.sq"
00045 #include "api/ai_event_types.hpp.sq"
00046 #include "api/ai_execmode.hpp.sq"
00047 #include "api/ai_gamesettings.hpp.sq"
00048 #include "api/ai_group.hpp.sq"
00049 #include "api/ai_grouplist.hpp.sq"
00050 #include "api/ai_industry.hpp.sq"
00051 #include "api/ai_industrylist.hpp.sq"
00052 #include "api/ai_industrytype.hpp.sq"
00053 #include "api/ai_industrytypelist.hpp.sq"
00054 #include "api/ai_list.hpp.sq"
00055 #include "api/ai_log.hpp.sq"
00056 #include "api/ai_map.hpp.sq"
00057 #include "api/ai_marine.hpp.sq"
00058 #include "api/ai_order.hpp.sq"
00059 #include "api/ai_rail.hpp.sq"
00060 #include "api/ai_railtypelist.hpp.sq"
00061 #include "api/ai_road.hpp.sq"
00062 #include "api/ai_sign.hpp.sq"
00063 #include "api/ai_signlist.hpp.sq"
00064 #include "api/ai_station.hpp.sq"
00065 #include "api/ai_stationlist.hpp.sq"
00066 #include "api/ai_subsidy.hpp.sq"
00067 #include "api/ai_subsidylist.hpp.sq"
00068 #include "api/ai_testmode.hpp.sq"
00069 #include "api/ai_tile.hpp.sq"
00070 #include "api/ai_tilelist.hpp.sq"
00071 #include "api/ai_town.hpp.sq"
00072 #include "api/ai_townlist.hpp.sq"
00073 #include "api/ai_tunnel.hpp.sq"
00074 #include "api/ai_vehicle.hpp.sq"
00075 #include "api/ai_vehiclelist.hpp.sq"
00076 #include "api/ai_waypoint.hpp.sq"
00077 #include "api/ai_waypointlist.hpp.sq"
00078 
00079 #undef DEFINE_SCRIPT_FILES
00080 
00081 /* static */ AIInstance *AIInstance::current_instance = NULL;
00082 
00083 AIStorage::~AIStorage()
00084 {
00085   /* Free our pointers */
00086   if (event_data != NULL) AIEventController::FreeEventPointer();
00087   if (log_data != NULL) AILog::FreeLogPointer();
00088 }
00089 
00090 static void PrintFunc(bool error_msg, const SQChar *message)
00091 {
00092   /* Convert to OpenTTD internal capable string */
00093   AIController::Print(error_msg, FS2OTTD(message));
00094 }
00095 
00096 AIInstance::AIInstance(AIInfo *info) :
00097   controller(NULL),
00098   storage(NULL),
00099   engine(NULL),
00100   instance(NULL),
00101   is_started(false),
00102   is_dead(false),
00103   suspend(0),
00104   callback(NULL)
00105 {
00106   /* Set the instance already, so we can use AIObject::Set commands */
00107   GetCompany(_current_company)->ai_instance = this;
00108   AIInstance::current_instance = this;
00109 
00110   this->controller = new AIController();
00111   this->storage    = new AIStorage();
00112   this->engine     = new Squirrel();
00113   this->engine->SetPrintFunction(&PrintFunc);
00114 
00115   /* The import method is available at a very early stage */
00116   this->engine->AddMethod("import", &AILibrary::Import, 4, ".ssi");
00117 
00118   /* Register the AIController */
00119   SQAIController_Register(this->engine);
00120 
00121   /* Load and execute the script for this AI */
00122   const char *main_script = info->GetMainScript();
00123   if (strcmp(main_script, "%_dummy") == 0) {
00124     extern void AI_CreateAIDummy(HSQUIRRELVM vm);
00125     AI_CreateAIDummy(this->engine->GetVM());
00126   } else if (!this->engine->LoadScript(main_script)) {
00127     this->Died();
00128     return;
00129   }
00130 
00131   /* Create the main-class */
00132   this->instance = MallocT<SQObject>(1);
00133   if (!this->engine->CreateClassInstance(info->GetInstanceName(), this->controller, this->instance)) {
00134     this->Died();
00135     return;
00136   }
00137 
00138   /* Register the API functions and classes */
00139   this->RegisterAPI();
00140 
00141   /* The topmost stack item is true if there is data from a savegame
00142    * and false otherwise. */
00143   sq_pushbool(this->engine->vm, false);
00144 }
00145 
00146 AIInstance::~AIInstance()
00147 {
00148   if (instance != NULL) this->engine->ReleaseObject(this->instance);
00149   if (engine != NULL) delete this->engine;
00150   delete this->storage;
00151   delete this->controller;
00152   free(this->instance);
00153 }
00154 
00155 void AIInstance::RegisterAPI()
00156 {
00157 /* Register all classes */
00158   squirrel_register_std(this->engine);
00159   SQAIAbstractList_Register(this->engine);
00160   SQAIAccounting_Register(this->engine);
00161   SQAIAirport_Register(this->engine);
00162   SQAIBase_Register(this->engine);
00163   SQAIBridge_Register(this->engine);
00164   SQAIBridgeList_Register(this->engine);
00165   SQAIBridgeList_Length_Register(this->engine);
00166   SQAICargo_Register(this->engine);
00167   SQAICargoList_Register(this->engine);
00168   SQAICargoList_IndustryAccepting_Register(this->engine);
00169   SQAICargoList_IndustryProducing_Register(this->engine);
00170   SQAICompany_Register(this->engine);
00171   SQAIDate_Register(this->engine);
00172   SQAIDepotList_Register(this->engine);
00173   SQAIEngine_Register(this->engine);
00174   SQAIEngineList_Register(this->engine);
00175   SQAIError_Register(this->engine);
00176   SQAIEvent_Register(this->engine);
00177   SQAIEventCompanyBankrupt_Register(this->engine);
00178   SQAIEventCompanyInTrouble_Register(this->engine);
00179   SQAIEventCompanyMerger_Register(this->engine);
00180   SQAIEventCompanyNew_Register(this->engine);
00181   SQAIEventController_Register(this->engine);
00182   SQAIEventDisasterZeppelinerCleared_Register(this->engine);
00183   SQAIEventDisasterZeppelinerCrashed_Register(this->engine);
00184   SQAIEventEngineAvailable_Register(this->engine);
00185   SQAIEventEnginePreview_Register(this->engine);
00186   SQAIEventIndustryClose_Register(this->engine);
00187   SQAIEventIndustryOpen_Register(this->engine);
00188   SQAIEventStationFirstVehicle_Register(this->engine);
00189   SQAIEventSubsidyAwarded_Register(this->engine);
00190   SQAIEventSubsidyExpired_Register(this->engine);
00191   SQAIEventSubsidyOffer_Register(this->engine);
00192   SQAIEventSubsidyOfferExpired_Register(this->engine);
00193   SQAIEventVehicleCrashed_Register(this->engine);
00194   SQAIEventVehicleLost_Register(this->engine);
00195   SQAIEventVehicleUnprofitable_Register(this->engine);
00196   SQAIEventVehicleWaitingInDepot_Register(this->engine);
00197   SQAIExecMode_Register(this->engine);
00198   SQAIGameSettings_Register(this->engine);
00199   SQAIGroup_Register(this->engine);
00200   SQAIGroupList_Register(this->engine);
00201   SQAIIndustry_Register(this->engine);
00202   SQAIIndustryList_Register(this->engine);
00203   SQAIIndustryList_CargoAccepting_Register(this->engine);
00204   SQAIIndustryList_CargoProducing_Register(this->engine);
00205   SQAIIndustryType_Register(this->engine);
00206   SQAIIndustryTypeList_Register(this->engine);
00207   SQAIList_Register(this->engine);
00208   SQAILog_Register(this->engine);
00209   SQAIMap_Register(this->engine);
00210   SQAIMarine_Register(this->engine);
00211   SQAIOrder_Register(this->engine);
00212   SQAIRail_Register(this->engine);
00213   SQAIRailTypeList_Register(this->engine);
00214   SQAIRoad_Register(this->engine);
00215   SQAISign_Register(this->engine);
00216   SQAISignList_Register(this->engine);
00217   SQAIStation_Register(this->engine);
00218   SQAIStationList_Register(this->engine);
00219   SQAIStationList_Vehicle_Register(this->engine);
00220   SQAISubsidy_Register(this->engine);
00221   SQAISubsidyList_Register(this->engine);
00222   SQAITestMode_Register(this->engine);
00223   SQAITile_Register(this->engine);
00224   SQAITileList_Register(this->engine);
00225   SQAITileList_IndustryAccepting_Register(this->engine);
00226   SQAITileList_IndustryProducing_Register(this->engine);
00227   SQAITileList_StationType_Register(this->engine);
00228   SQAITown_Register(this->engine);
00229   SQAITownList_Register(this->engine);
00230   SQAITunnel_Register(this->engine);
00231   SQAIVehicle_Register(this->engine);
00232   SQAIVehicleList_Register(this->engine);
00233   SQAIVehicleList_DefaultGroup_Register(this->engine);
00234   SQAIVehicleList_Group_Register(this->engine);
00235   SQAIVehicleList_SharedOrders_Register(this->engine);
00236   SQAIVehicleList_Station_Register(this->engine);
00237   SQAIWaypoint_Register(this->engine);
00238   SQAIWaypointList_Register(this->engine);
00239   SQAIWaypointList_Vehicle_Register(this->engine);
00240 
00241   this->engine->SetGlobalPointer(this->engine);
00242 }
00243 
00244 void AIInstance::Continue()
00245 {
00246   assert(this->suspend < 0);
00247   this->suspend = -this->suspend - 1;
00248 }
00249 
00250 void AIInstance::Died()
00251 {
00252   DEBUG(ai, 0, "The AI died unexpectedly.");
00253   this->is_dead = true;
00254 
00255   if (this->instance != NULL) this->engine->ReleaseObject(this->instance);
00256   delete this->engine;
00257   this->instance = NULL;
00258   this->engine = NULL;
00259 
00260   ShowAIDebugWindow(_current_company);
00261 
00262   const AIInfo *info = AIConfig::GetConfig(_current_company)->GetInfo();
00263   if (info != NULL) {
00264     ShowErrorMessage(INVALID_STRING_ID, STR_AI_PLEASE_REPORT_CRASH, 0, 0);
00265 
00266     if (info->GetURL() != NULL) {
00267       AILog::Info("Please report the error to the following URL:");
00268       AILog::Info(info->GetURL());
00269     }
00270   }
00271 }
00272 
00273 void AIInstance::GameLoop()
00274 {
00275   if (this->is_dead) return;
00276   if (this->engine->HasScriptCrashed()) {
00277     /* The script crashed during saving, kill it here. */
00278     this->Died();
00279     return;
00280   }
00281   this->controller->ticks++;
00282 
00283   if (this->suspend   < -1) this->suspend++; // Multiplayer suspend, increase up to -1.
00284   if (this->suspend   < 0)  return;          // Multiplayer suspend, wait for Continue().
00285   if (--this->suspend > 0)  return;          // Singleplayer suspend, decrease to 0.
00286 
00287   /* If there is a callback to call, call that first */
00288   if (this->callback != NULL) {
00289     try {
00290       this->callback(this);
00291     } catch (AI_VMSuspend e) {
00292       this->suspend  = e.GetSuspendTime();
00293       this->callback = e.GetSuspendCallback();
00294 
00295       return;
00296     }
00297   }
00298 
00299   this->suspend  = 0;
00300   this->callback = NULL;
00301 
00302   if (!this->is_started) {
00303     try {
00304       AIObject::SetAllowDoCommand(false);
00305       /* Run the constructor if it exists. Don't allow any DoCommands in it. */
00306       if (this->engine->MethodExists(*this->instance, "constructor")) {
00307         if (!this->engine->CallMethod(*this->instance, "constructor", 100000) || this->engine->IsSuspended()) {
00308           if (this->engine->IsSuspended()) AILog::Error("This AI took too long to initialize. AI is not started.");
00309           this->Died();
00310           return;
00311         }
00312       }
00313       if (!this->CallLoad() || this->engine->IsSuspended()) {
00314         if (this->engine->IsSuspended()) AILog::Error("This AI took too long in the Load function. AI is not started.");
00315         this->Died();
00316         return;
00317       }
00318       AIObject::SetAllowDoCommand(true);
00319       /* Start the AI by calling Start() */
00320       if (!this->engine->CallMethod(*this->instance, "Start",  _settings_game.ai.ai_max_opcode_till_suspend) || !this->engine->IsSuspended()) this->Died();
00321     } catch (AI_VMSuspend e) {
00322       this->suspend  = e.GetSuspendTime();
00323       this->callback = e.GetSuspendCallback();
00324     }
00325 
00326     this->is_started = true;
00327     return;
00328   }
00329 
00330   /* Continue the VM */
00331   try {
00332     if (!this->engine->Resume(_settings_game.ai.ai_max_opcode_till_suspend)) this->Died();
00333   } catch (AI_VMSuspend e) {
00334     this->suspend  = e.GetSuspendTime();
00335     this->callback = e.GetSuspendCallback();
00336   }
00337 }
00338 
00339 void AIInstance::CollectGarbage()
00340 {
00341   if (this->is_started && !this->is_dead) this->engine->CollectGarbage();
00342 }
00343 
00344 /* static */ void AIInstance::DoCommandReturn(AIInstance *instance)
00345 {
00346   instance->engine->InsertResult(AIObject::GetLastCommandRes());
00347 }
00348 
00349 /* static */ void AIInstance::DoCommandReturnVehicleID(AIInstance *instance)
00350 {
00351   instance->engine->InsertResult(AIObject::GetNewVehicleID());
00352 }
00353 
00354 /* static */ void AIInstance::DoCommandReturnSignID(AIInstance *instance)
00355 {
00356   instance->engine->InsertResult(AIObject::GetNewSignID());
00357 }
00358 
00359 /* static */ void AIInstance::DoCommandReturnGroupID(AIInstance *instance)
00360 {
00361   instance->engine->InsertResult(AIObject::GetNewGroupID());
00362 }
00363 
00364 /* static */ AIStorage *AIInstance::GetStorage()
00365 {
00366   assert(IsValidCompanyID(_current_company) && !IsHumanCompany(_current_company));
00367   return GetCompany(_current_company)->ai_instance->storage;
00368 }
00369 
00370 /*
00371  * All data is stored in the following format:
00372  * First 1 byte indicating if there is a data blob at all.
00373  * 1 byte indicating the type of data.
00374  * The data itself, this differs per type:
00375  *  - integer: a binary representation of the integer (int32).
00376  *  - string:  First one byte with the string length, then a 0-terminated char
00377  *             array. The string can't be longer then 255 bytes (including
00378  *             terminating '\0').
00379  *  - array:   All data-elements of the array are saved recursive in this
00380  *             format, and ended with an element of the type
00381  *             SQSL_ARRAY_TABLE_END.
00382  *  - table:   All key/value pairs are saved in this format (first key 1, then
00383  *             value 1, then key 2, etc.). All keys and values can have an
00384  *             arbitrary type (as long as it is supported by the save function
00385  *             of course). The table is ended with an element of the type
00386  *             SQSL_ARRAY_TABLE_END.
00387  *  - bool:    A single byte with value 1 representing true and 0 false.
00388  *  - null:    No data.
00389  */
00390 
00392 enum SQSaveLoadType {
00393   SQSL_INT             = 0x00, 
00394   SQSL_STRING          = 0x01, 
00395   SQSL_ARRAY           = 0x02, 
00396   SQSL_TABLE           = 0x03, 
00397   SQSL_BOOL            = 0x04, 
00398   SQSL_NULL            = 0x05, 
00399   SQSL_ARRAY_TABLE_END = 0xFF, 
00400 };
00401 
00402 static byte _ai_sl_byte;
00403 
00404 static const SaveLoad _ai_byte[] = {
00405   SLEG_VAR(_ai_sl_byte, SLE_UINT8),
00406   SLE_END()
00407 };
00408 
00409 enum {
00410   AISAVE_MAX_DEPTH = 25, 
00411 };
00412 
00413 /* static */ bool AIInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
00414 {
00415   if (max_depth == 0) {
00416     AILog::Error("Savedata can only be nested to 25 deep. No data saved.");
00417     return false;
00418   }
00419 
00420   switch (sq_gettype(vm, index)) {
00421     case OT_INTEGER: {
00422       if (!test) {
00423         _ai_sl_byte = SQSL_INT;
00424         SlObject(NULL, _ai_byte);
00425       }
00426       SQInteger res;
00427       sq_getinteger(vm, index, &res);
00428       if (!test) {
00429         int value = (int)res;
00430         SlArray(&value, 1, SLE_INT32);
00431       }
00432       return true;
00433     }
00434 
00435     case OT_STRING: {
00436       if (!test) {
00437         _ai_sl_byte = SQSL_STRING;
00438         SlObject(NULL, _ai_byte);
00439       }
00440       const SQChar *res;
00441       sq_getstring(vm, index, &res);
00442       /* @bug if a string longer than 512 characters is given to FS2OTTD, the
00443        *  internal buffer overflows. */
00444       const char *buf = FS2OTTD(res);
00445       size_t len = strlen(buf) + 1;
00446       if (len >= 255) {
00447         AILog::Error("Maximum string length is 254 chars. No data saved.");
00448         return false;
00449       }
00450       if (!test) {
00451         _ai_sl_byte = (byte)len;
00452         SlObject(NULL, _ai_byte);
00453         SlArray((void*)buf, len, SLE_CHAR);
00454       }
00455       return true;
00456     }
00457 
00458     case OT_ARRAY: {
00459       if (!test) {
00460         _ai_sl_byte = SQSL_ARRAY;
00461         SlObject(NULL, _ai_byte);
00462       }
00463       sq_pushnull(vm);
00464       while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
00465         /* Store the value */
00466         bool res = SaveObject(vm, -1, max_depth - 1, test);
00467         sq_pop(vm, 2);
00468         if (!res) {
00469           sq_pop(vm, 1);
00470           return false;
00471         }
00472       }
00473       sq_pop(vm, 1);
00474       if (!test) {
00475         _ai_sl_byte = SQSL_ARRAY_TABLE_END;
00476         SlObject(NULL, _ai_byte);
00477       }
00478       return true;
00479     }
00480 
00481     case OT_TABLE: {
00482       if (!test) {
00483         _ai_sl_byte = SQSL_TABLE;
00484         SlObject(NULL, _ai_byte);
00485       }
00486       sq_pushnull(vm);
00487       while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
00488         /* Store the key + value */
00489         bool res = SaveObject(vm, -2, max_depth - 1, test) && SaveObject(vm, -1, max_depth - 1, test);
00490         sq_pop(vm, 2);
00491         if (!res) {
00492           sq_pop(vm, 1);
00493           return false;
00494         }
00495       }
00496       sq_pop(vm, 1);
00497       if (!test) {
00498         _ai_sl_byte = SQSL_ARRAY_TABLE_END;
00499         SlObject(NULL, _ai_byte);
00500       }
00501       return true;
00502     }
00503 
00504     case OT_BOOL: {
00505       if (!test) {
00506         _ai_sl_byte = SQSL_BOOL;
00507         SlObject(NULL, _ai_byte);
00508       }
00509       SQBool res;
00510       sq_getbool(vm, index, &res);
00511       if (!test) {
00512         _ai_sl_byte = res ? 1 : 0;
00513         SlObject(NULL, _ai_byte);
00514       }
00515       return true;
00516     }
00517 
00518     case OT_NULL: {
00519       if (!test) {
00520         _ai_sl_byte = SQSL_NULL;
00521         SlObject(NULL, _ai_byte);
00522       }
00523       return true;
00524     }
00525 
00526     default:
00527       AILog::Error("You tried to save an unsupported type. No data saved.");
00528       return false;
00529   }
00530 }
00531 
00532 /* static */ void AIInstance::SaveEmpty()
00533 {
00534   _ai_sl_byte = 0;
00535   SlObject(NULL, _ai_byte);
00536 }
00537 
00538 void AIInstance::Save()
00539 {
00540   /* Don't save data if the AI didn't start yet or if it crashed. */
00541   if (this->engine == NULL || this->engine->HasScriptCrashed()) {
00542     SaveEmpty();
00543     return;
00544   }
00545 
00546   HSQUIRRELVM vm = this->engine->GetVM();
00547   if (!this->is_started) {
00548     SQBool res;
00549     sq_getbool(vm, -1, &res);
00550     if (!res) {
00551       SaveEmpty();
00552       return;
00553     }
00554     /* Push the loaded savegame data to the top of the stack. */
00555     sq_push(vm, -2);
00556     _ai_sl_byte = 1;
00557     SlObject(NULL, _ai_byte);
00558     /* Save the data that was just loaded. */
00559     SaveObject(vm, -1, AISAVE_MAX_DEPTH, false);
00560     sq_poptop(vm);
00561   } else if (this->engine->MethodExists(*this->instance, "Save")) {
00562     HSQOBJECT savedata;
00563     /* We don't want to be interrupted during the save function. */
00564     bool backup_allow = AIObject::GetAllowDoCommand();
00565     AIObject::SetAllowDoCommand(false);
00566     if (!this->engine->CallMethod(*this->instance, "Save", &savedata)) {
00567       /* The script crashed in the Save function. We can't kill
00568        * it here, but do so in the next AI tick. */
00569       SaveEmpty();
00570       return;
00571     }
00572     AIObject::SetAllowDoCommand(backup_allow);
00573 
00574     if (!sq_istable(savedata)) {
00575       AILog::Error("Save function should return a table.");
00576       SaveEmpty();
00577       return;
00578     }
00579     sq_pushobject(vm, savedata);
00580     if (SaveObject(vm, -1, AISAVE_MAX_DEPTH, true)) {
00581       _ai_sl_byte = 1;
00582       SlObject(NULL, _ai_byte);
00583       SaveObject(vm, -1, AISAVE_MAX_DEPTH, false);
00584     } else {
00585       _ai_sl_byte = 0;
00586       SlObject(NULL, _ai_byte);
00587     }
00588     sq_pop(vm, 1);
00589   } else {
00590     AILog::Warning("Save function is not implemented");
00591     _ai_sl_byte = 0;
00592     SlObject(NULL, _ai_byte);
00593   }
00594 
00595 }
00596 
00597 /* static */ bool AIInstance::LoadObjects(HSQUIRRELVM vm)
00598 {
00599   SlObject(NULL, _ai_byte);
00600   switch (_ai_sl_byte) {
00601     case SQSL_INT: {
00602       int value;
00603       SlArray(&value, 1, SLE_INT32);
00604       if (vm != NULL) sq_pushinteger(vm, (SQInteger)value);
00605       return true;
00606     }
00607 
00608     case SQSL_STRING: {
00609       SlObject(NULL, _ai_byte);
00610       static char buf[256];
00611       SlArray(buf, _ai_sl_byte, SLE_CHAR);
00612       if (vm != NULL) sq_pushstring(vm, OTTD2FS(buf), -1);
00613       return true;
00614     }
00615 
00616     case SQSL_ARRAY: {
00617       if (vm != NULL) sq_newarray(vm, 0);
00618       while (LoadObjects(vm)) {
00619         if (vm != NULL) sq_arrayappend(vm, -2);
00620         /* The value is popped from the stack by squirrel. */
00621       }
00622       return true;
00623     }
00624 
00625     case SQSL_TABLE: {
00626       if (vm != NULL) sq_newtable(vm);
00627       while (LoadObjects(vm)) {
00628         LoadObjects(vm);
00629         if (vm != NULL) sq_rawset(vm, -3);
00630         /* The key (-2) and value (-1) are popped from the stack by squirrel. */
00631       }
00632       return true;
00633     }
00634 
00635     case SQSL_BOOL: {
00636       SlObject(NULL, _ai_byte);
00637       if (vm != NULL) sq_pushinteger(vm, (SQBool)(_ai_sl_byte != 0));
00638       return true;
00639     }
00640 
00641     case SQSL_NULL: {
00642       if (vm != NULL) sq_pushnull(vm);
00643       return true;
00644     }
00645 
00646     case SQSL_ARRAY_TABLE_END: {
00647       return false;
00648     }
00649 
00650     default: NOT_REACHED();
00651   }
00652 }
00653 
00654 /* static */ void AIInstance::LoadEmpty()
00655 {
00656   SlObject(NULL, _ai_byte);
00657   /* Check if there was anything saved at all. */
00658   if (_ai_sl_byte == 0) return;
00659 
00660   LoadObjects(NULL);
00661 }
00662 
00663 void AIInstance::Load(int version)
00664 {
00665   if (this->engine == NULL || version == -1) {
00666     LoadEmpty();
00667     return;
00668   }
00669   HSQUIRRELVM vm = this->engine->GetVM();
00670 
00671   SlObject(NULL, _ai_byte);
00672   /* Check if there was anything saved at all. */
00673   if (_ai_sl_byte == 0) return;
00674 
00675   /* First remove the value "false" since we have data to load. */
00676   sq_poptop(vm);
00677   sq_pushinteger(vm, version);
00678   LoadObjects(vm);
00679   sq_pushbool(vm, true);
00680 }
00681 
00682 bool AIInstance::CallLoad()
00683 {
00684   HSQUIRRELVM vm = this->engine->GetVM();
00685   /* Is there save data that we should load? */
00686   SQBool res;
00687   sq_getbool(vm, -1, &res);
00688   sq_poptop(vm);
00689   if (!res) return true;
00690 
00691   if (!this->engine->MethodExists(*this->instance, "Load")) {
00692     AILog::Warning("Loading failed: there was data for the AI to load, but the AI does not have a Load() function.");
00693 
00694     /* Pop the savegame data and version. */
00695     sq_pop(vm, 2);
00696     return true;
00697   }
00698 
00699   /* Go to the instance-root */
00700   sq_pushobject(vm, *this->instance);
00701   /* Find the function-name inside the script */
00702   sq_pushstring(vm, OTTD2FS("Load"), -1);
00703   /* Change the "Load" string in a function pointer */
00704   sq_get(vm, -2);
00705   /* Push the main instance as "this" object */
00706   sq_pushobject(vm, *this->instance);
00707   /* Push the version data and savegame data as arguments */
00708   sq_push(vm, -5);
00709   sq_push(vm, -5);
00710 
00711   /* Call the AI load function. sq_call removes the arguments (but not the
00712    * function pointer) from the stack. */
00713   if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse, 100000))) return false;
00714 
00715   /* Pop 1) The version, 2) the savegame data, 3) the object instance, 4) the function pointer. */
00716   sq_pop(vm, 4);
00717   return true;
00718 }

Generated on Mon Jun 8 23:04:01 2009 for OpenTTD by  doxygen 1.5.6