(svn r15027) -Merge: tomatos and bananas left to be, here is NoAI for all to see.
NoAI is an API (a framework) to build your own AIs in. See: http://wiki.openttd.org/wiki/index.php/AI:Main_Page With many thanks to: - glx and Rubidium for their syncing, feedback and hard work - Yexo for his feedback, patches, and AIs which tested the system very deep - Morloth for his feedback and patches - TJIP for hosting a challenge which kept NoAI on track - All AI authors for testing our AI API, and all other people who helped in one way or another -Remove: all old AIs and their cheats/hacks
This commit is contained in:
628
src/ai/ai_instance.cpp
Normal file
628
src/ai/ai_instance.cpp
Normal file
@@ -0,0 +1,628 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_instance.cpp Implementation of AIInstance. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../openttd.h"
|
||||
#include "../debug.h"
|
||||
#include "../company_func.h"
|
||||
#include "../core/alloc_func.hpp"
|
||||
#include "../string_func.h"
|
||||
#include "../settings_type.h"
|
||||
#include "../company_base.h"
|
||||
#include "../saveload/saveload.h"
|
||||
#include "table/strings.h"
|
||||
|
||||
#include <squirrel.h>
|
||||
#include "../script/squirrel.hpp"
|
||||
#include "../script/squirrel_helper.hpp"
|
||||
#include "../script/squirrel_class.hpp"
|
||||
#include "../script/squirrel_std.hpp"
|
||||
#include "ai.hpp"
|
||||
#include "api/ai_controller.hpp"
|
||||
#include "ai_info.hpp"
|
||||
#include "ai_storage.hpp"
|
||||
#include "ai_instance.hpp"
|
||||
|
||||
/* Convert all AI related classes to Squirrel data.
|
||||
* Note: this line a marker in squirrel_export.sh. Do not change! */
|
||||
#include "api/ai_abstractlist.hpp.sq"
|
||||
#include "api/ai_accounting.hpp.sq"
|
||||
#include "api/ai_airport.hpp.sq"
|
||||
#include "api/ai_base.hpp.sq"
|
||||
#include "api/ai_bridge.hpp.sq"
|
||||
#include "api/ai_bridgelist.hpp.sq"
|
||||
#include "api/ai_cargo.hpp.sq"
|
||||
#include "api/ai_cargolist.hpp.sq"
|
||||
#include "api/ai_company.hpp.sq"
|
||||
#include "api/ai_controller.hpp.sq"
|
||||
#include "api/ai_date.hpp.sq"
|
||||
#include "api/ai_depotlist.hpp.sq"
|
||||
#include "api/ai_engine.hpp.sq"
|
||||
#include "api/ai_enginelist.hpp.sq"
|
||||
#include "api/ai_error.hpp.sq"
|
||||
#include "api/ai_event.hpp.sq"
|
||||
#include "api/ai_event_types.hpp.sq"
|
||||
#include "api/ai_execmode.hpp.sq"
|
||||
#include "api/ai_gamesettings.hpp.sq"
|
||||
#include "api/ai_group.hpp.sq"
|
||||
#include "api/ai_grouplist.hpp.sq"
|
||||
#include "api/ai_industry.hpp.sq"
|
||||
#include "api/ai_industrylist.hpp.sq"
|
||||
#include "api/ai_industrytype.hpp.sq"
|
||||
#include "api/ai_industrytypelist.hpp.sq"
|
||||
#include "api/ai_list.hpp.sq"
|
||||
#include "api/ai_log.hpp.sq"
|
||||
#include "api/ai_map.hpp.sq"
|
||||
#include "api/ai_marine.hpp.sq"
|
||||
#include "api/ai_order.hpp.sq"
|
||||
#include "api/ai_rail.hpp.sq"
|
||||
#include "api/ai_railtypelist.hpp.sq"
|
||||
#include "api/ai_road.hpp.sq"
|
||||
#include "api/ai_sign.hpp.sq"
|
||||
#include "api/ai_station.hpp.sq"
|
||||
#include "api/ai_stationlist.hpp.sq"
|
||||
#include "api/ai_subsidy.hpp.sq"
|
||||
#include "api/ai_subsidylist.hpp.sq"
|
||||
#include "api/ai_testmode.hpp.sq"
|
||||
#include "api/ai_tile.hpp.sq"
|
||||
#include "api/ai_tilelist.hpp.sq"
|
||||
#include "api/ai_town.hpp.sq"
|
||||
#include "api/ai_townlist.hpp.sq"
|
||||
#include "api/ai_tunnel.hpp.sq"
|
||||
#include "api/ai_vehicle.hpp.sq"
|
||||
#include "api/ai_vehiclelist.hpp.sq"
|
||||
|
||||
/* static */ AIInstance *AIInstance::current_instance = NULL;
|
||||
|
||||
AIStorage::~AIStorage()
|
||||
{
|
||||
/* Free our pointers */
|
||||
if (event_data != NULL) AIEventController::FreeEventPointer();
|
||||
if (log_data != NULL) AILog::FreeLogPointer();
|
||||
}
|
||||
|
||||
static void PrintFunc(bool error_msg, const SQChar *message)
|
||||
{
|
||||
/* Convert to OpenTTD internal capable string */
|
||||
AIController::Print(error_msg, FS2OTTD(message));
|
||||
}
|
||||
|
||||
AIInstance::AIInstance(AIInfo *info) :
|
||||
controller(NULL),
|
||||
storage(NULL),
|
||||
engine(NULL),
|
||||
instance(NULL),
|
||||
is_started(false),
|
||||
is_dead(false),
|
||||
suspend(0),
|
||||
callback(NULL)
|
||||
{
|
||||
/* Set the instance already, so we can use AIObject::Set commands */
|
||||
GetCompany(_current_company)->ai_instance = this;
|
||||
AIInstance::current_instance = this;
|
||||
|
||||
this->controller = new AIController();
|
||||
this->storage = new AIStorage();
|
||||
this->engine = new Squirrel();
|
||||
this->engine->SetPrintFunction(&PrintFunc);
|
||||
|
||||
/* The import method is available at a very early stage */
|
||||
this->engine->AddMethod("import", &AILibrary::Import, 4, "?ssi");
|
||||
|
||||
/* Register the AIController */
|
||||
SQAIController_Register(this->engine);
|
||||
|
||||
/* Load and execute the script for this AI */
|
||||
const char *script_name = info->GetScriptName();
|
||||
if (strcmp(script_name, "%_dummy") == 0) {
|
||||
extern void AI_CreateAIDummy(HSQUIRRELVM vm);
|
||||
AI_CreateAIDummy(this->engine->GetVM());
|
||||
} else if (!this->engine->LoadScript(script_name)) {
|
||||
this->Died();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create the main-class */
|
||||
this->instance = MallocT<SQObject>(1);
|
||||
if (!this->engine->CreateClassInstance(info->GetInstanceName(), this->controller, this->instance)) {
|
||||
this->Died();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Register the API functions and classes */
|
||||
this->RegisterAPI();
|
||||
|
||||
/* Run the constructor if it exists. Don't allow any DoCommands in it. */
|
||||
if (this->engine->MethodExists(*this->instance, "constructor")) {
|
||||
AIObject::SetAllowDoCommand(false);
|
||||
this->engine->CallMethod(*this->instance, "constructor");
|
||||
AIObject::SetAllowDoCommand(true);
|
||||
}
|
||||
}
|
||||
|
||||
AIInstance::~AIInstance()
|
||||
{
|
||||
if (engine != NULL) delete this->engine;
|
||||
delete this->storage;
|
||||
delete this->controller;
|
||||
free(this->instance);
|
||||
}
|
||||
|
||||
void AIInstance::RegisterAPI()
|
||||
{
|
||||
/* Register all classes */
|
||||
squirrel_register_std(this->engine);
|
||||
SQAIAbstractList_Register(this->engine);
|
||||
SQAIAccounting_Register(this->engine);
|
||||
SQAIAirport_Register(this->engine);
|
||||
SQAIBase_Register(this->engine);
|
||||
SQAIBridge_Register(this->engine);
|
||||
SQAIBridgeList_Register(this->engine);
|
||||
SQAIBridgeList_Length_Register(this->engine);
|
||||
SQAICargo_Register(this->engine);
|
||||
SQAICargoList_Register(this->engine);
|
||||
SQAICargoList_IndustryAccepting_Register(this->engine);
|
||||
SQAICargoList_IndustryProducing_Register(this->engine);
|
||||
SQAICompany_Register(this->engine);
|
||||
SQAIDate_Register(this->engine);
|
||||
SQAIDepotList_Register(this->engine);
|
||||
SQAIEngine_Register(this->engine);
|
||||
SQAIEngineList_Register(this->engine);
|
||||
SQAIError_Register(this->engine);
|
||||
SQAIEvent_Register(this->engine);
|
||||
SQAIEventCompanyBankrupt_Register(this->engine);
|
||||
SQAIEventCompanyInTrouble_Register(this->engine);
|
||||
SQAIEventCompanyMerger_Register(this->engine);
|
||||
SQAIEventCompanyNew_Register(this->engine);
|
||||
SQAIEventController_Register(this->engine);
|
||||
SQAIEventEngineAvailable_Register(this->engine);
|
||||
SQAIEventEnginePreview_Register(this->engine);
|
||||
SQAIEventIndustryClose_Register(this->engine);
|
||||
SQAIEventIndustryOpen_Register(this->engine);
|
||||
SQAIEventStationFirstVehicle_Register(this->engine);
|
||||
SQAIEventSubsidyAwarded_Register(this->engine);
|
||||
SQAIEventSubsidyExpired_Register(this->engine);
|
||||
SQAIEventSubsidyOffer_Register(this->engine);
|
||||
SQAIEventSubsidyOfferExpired_Register(this->engine);
|
||||
SQAIEventTest_Register(this->engine);
|
||||
SQAIEventVehicleCrashed_Register(this->engine);
|
||||
SQAIEventVehicleLost_Register(this->engine);
|
||||
SQAIEventVehicleUnprofitable_Register(this->engine);
|
||||
SQAIEventVehicleWaitingInDepot_Register(this->engine);
|
||||
SQAIExecMode_Register(this->engine);
|
||||
SQAIGameSettings_Register(this->engine);
|
||||
SQAIGroup_Register(this->engine);
|
||||
SQAIGroupList_Register(this->engine);
|
||||
SQAIIndustry_Register(this->engine);
|
||||
SQAIIndustryList_Register(this->engine);
|
||||
SQAIIndustryList_CargoAccepting_Register(this->engine);
|
||||
SQAIIndustryList_CargoProducing_Register(this->engine);
|
||||
SQAIIndustryType_Register(this->engine);
|
||||
SQAIIndustryTypeList_Register(this->engine);
|
||||
SQAIList_Register(this->engine);
|
||||
SQAILog_Register(this->engine);
|
||||
SQAIMap_Register(this->engine);
|
||||
SQAIMarine_Register(this->engine);
|
||||
SQAIOrder_Register(this->engine);
|
||||
SQAIRail_Register(this->engine);
|
||||
SQAIRailTypeList_Register(this->engine);
|
||||
SQAIRoad_Register(this->engine);
|
||||
SQAISign_Register(this->engine);
|
||||
SQAIStation_Register(this->engine);
|
||||
SQAIStationList_Register(this->engine);
|
||||
SQAIStationList_Vehicle_Register(this->engine);
|
||||
SQAISubsidy_Register(this->engine);
|
||||
SQAISubsidyList_Register(this->engine);
|
||||
SQAITestMode_Register(this->engine);
|
||||
SQAITile_Register(this->engine);
|
||||
SQAITileList_Register(this->engine);
|
||||
SQAITileList_IndustryAccepting_Register(this->engine);
|
||||
SQAITileList_IndustryProducing_Register(this->engine);
|
||||
SQAITileList_StationType_Register(this->engine);
|
||||
SQAITown_Register(this->engine);
|
||||
SQAITownList_Register(this->engine);
|
||||
SQAITunnel_Register(this->engine);
|
||||
SQAIVehicle_Register(this->engine);
|
||||
SQAIVehicleList_Register(this->engine);
|
||||
SQAIVehicleList_Station_Register(this->engine);
|
||||
|
||||
this->engine->SetGlobalPointer(this->engine);
|
||||
}
|
||||
|
||||
void AIInstance::Continue()
|
||||
{
|
||||
assert(this->suspend < 0);
|
||||
this->suspend = -this->suspend - 1;
|
||||
}
|
||||
|
||||
void AIInstance::Died()
|
||||
{
|
||||
DEBUG(ai, 0, "The AI died unexpectedly.");
|
||||
this->is_dead = true;
|
||||
|
||||
delete this->engine;
|
||||
this->engine = NULL;
|
||||
}
|
||||
|
||||
void AIInstance::GameLoop()
|
||||
{
|
||||
if (this->is_dead) return;
|
||||
this->controller->ticks++;
|
||||
|
||||
if (this->suspend < -1) this->suspend++; // Multiplayer suspend, increase up to -1.
|
||||
if (this->suspend < 0) return; // Multiplayer suspend, wait for Continue().
|
||||
if (--this->suspend > 0) return; // Singleplayer suspend, decrease to 0.
|
||||
|
||||
/* If there is a callback to call, call that first */
|
||||
if (this->callback != NULL) {
|
||||
try {
|
||||
this->callback(this);
|
||||
} catch (AI_VMSuspend e) {
|
||||
this->suspend = e.GetSuspendTime();
|
||||
this->callback = e.GetSuspendCallback();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->suspend = 0;
|
||||
this->callback = NULL;
|
||||
|
||||
if (!this->is_started) {
|
||||
/* Start the AI by calling Start() */
|
||||
try {
|
||||
if (!this->engine->CallMethod(*this->instance, "Start", _settings_game.ai.ai_max_opcode_till_suspend)) this->Died();
|
||||
} catch (AI_VMSuspend e) {
|
||||
this->suspend = e.GetSuspendTime();
|
||||
this->callback = e.GetSuspendCallback();
|
||||
}
|
||||
|
||||
this->is_started = true;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Continue the VM */
|
||||
try {
|
||||
if (!this->engine->Resume(_settings_game.ai.ai_max_opcode_till_suspend)) this->Died();
|
||||
} catch (AI_VMSuspend e) {
|
||||
this->suspend = e.GetSuspendTime();
|
||||
this->callback = e.GetSuspendCallback();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AIInstance::DoCommandReturn(AIInstance *instance)
|
||||
{
|
||||
instance->engine->InsertResult(AIObject::GetLastCommandRes());
|
||||
}
|
||||
|
||||
/* static */ void AIInstance::DoCommandReturnVehicleID(AIInstance *instance)
|
||||
{
|
||||
instance->engine->InsertResult(AIObject::GetNewVehicleID());
|
||||
}
|
||||
|
||||
/* static */ void AIInstance::DoCommandReturnSignID(AIInstance *instance)
|
||||
{
|
||||
instance->engine->InsertResult(AIObject::GetNewSignID());
|
||||
}
|
||||
|
||||
/* static */ void AIInstance::DoCommandReturnGroupID(AIInstance *instance)
|
||||
{
|
||||
instance->engine->InsertResult(AIObject::GetNewGroupID());
|
||||
}
|
||||
|
||||
/* static */ AIStorage *AIInstance::GetStorage()
|
||||
{
|
||||
assert(IsValidCompanyID(_current_company) && !IsHumanCompany(_current_company));
|
||||
return GetCompany(_current_company)->ai_instance->storage;
|
||||
}
|
||||
|
||||
/*
|
||||
* All data is stored in the following format:
|
||||
* First 1 byte indicating if there is a data blob at all.
|
||||
* 1 byte indicating the type of data.
|
||||
* The data itself, this differs per type:
|
||||
* - integer: a binary representation of the integer (int32).
|
||||
* - string: First one byte with the string length, then a 0-terminated char
|
||||
* array. The string can't be longer then 255 bytes (including
|
||||
* terminating '\0').
|
||||
* - array: All data-elements of the array are saved recursive in this
|
||||
* format, and ended with an element of the type
|
||||
* SQSL_ARRAY_TABLE_END.
|
||||
* - table: All key/value pairs are saved in this format (first key 1, then
|
||||
* value 1, then key 2, etc.). All keys and values can have an
|
||||
* arbitrary type (as long as it is supported by the save function
|
||||
* of course). The table is ended with an element of the type
|
||||
* SQSL_ARRAY_TABLE_END.
|
||||
* - bool: A single byte with value 1 representing true and 0 false.
|
||||
* - null: No data.
|
||||
*/
|
||||
|
||||
/** The type of the data that follows in the savegame. */
|
||||
enum SQSaveLoadType {
|
||||
SQSL_INT = 0x00, ///< The following data is an integer.
|
||||
SQSL_STRING = 0x01, ///< The following data is an string.
|
||||
SQSL_ARRAY = 0x02, ///< The following data is an array.
|
||||
SQSL_TABLE = 0x03, ///< The following data is an table.
|
||||
SQSL_BOOL = 0x04, ///< The following data is a boolean.
|
||||
SQSL_NULL = 0x05, ///< A null variable.
|
||||
SQSL_ARRAY_TABLE_END = 0xFF, ///< Marks the end of an array or table, no data follows.
|
||||
};
|
||||
|
||||
static byte _ai_sl_byte;
|
||||
|
||||
static const SaveLoad _ai_byte[] = {
|
||||
SLEG_VAR(_ai_sl_byte, SLE_UINT8),
|
||||
SLE_END()
|
||||
};
|
||||
|
||||
enum {
|
||||
AISAVE_MAX_DEPTH = 25, ///< The maximum recursive depth for items stored in the savegame.
|
||||
};
|
||||
|
||||
/* static */ bool AIInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
|
||||
{
|
||||
if (max_depth == 0) {
|
||||
AILog::Error("Savedata can only be nested to 5 deep. No data saved.");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (sq_gettype(vm, index)) {
|
||||
case OT_INTEGER: {
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_INT;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
SQInteger res;
|
||||
sq_getinteger(vm, index, &res);
|
||||
if (!test) {
|
||||
int value = (int)res;
|
||||
SlArray(&value, 1, SLE_INT32);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case OT_STRING: {
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_STRING;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
const SQChar *res;
|
||||
sq_getstring(vm, index, &res);
|
||||
/* @bug if a string longer than 512 characters is given to FS2OTTD, the
|
||||
* internal buffer overflows. */
|
||||
const char *buf = FS2OTTD(res);
|
||||
size_t len = strlen(buf) + 1;
|
||||
if (len >= 255) {
|
||||
AILog::Error("Maximum string length is 254 chars. No data saved.");
|
||||
return false;
|
||||
}
|
||||
if (!test) {
|
||||
_ai_sl_byte = (byte)len;
|
||||
SlObject(NULL, _ai_byte);
|
||||
SlArray((void*)buf, len, SLE_CHAR);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case OT_ARRAY: {
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_ARRAY;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
sq_pushnull(vm);
|
||||
while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
|
||||
/* Store the value */
|
||||
bool res = SaveObject(vm, -1, max_depth - 1, test);
|
||||
sq_pop(vm, 2);
|
||||
if (!res) {
|
||||
sq_pop(vm, 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
sq_pop(vm, 1);
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_ARRAY_TABLE_END;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case OT_TABLE: {
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_TABLE;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
sq_pushnull(vm);
|
||||
while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
|
||||
/* Store the key + value */
|
||||
bool res = SaveObject(vm, -2, max_depth - 1, test) && SaveObject(vm, -1, max_depth - 1, test);
|
||||
sq_pop(vm, 2);
|
||||
if (!res) {
|
||||
sq_pop(vm, 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
sq_pop(vm, 1);
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_ARRAY_TABLE_END;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case OT_BOOL: {
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_BOOL;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
SQBool res;
|
||||
sq_getbool(vm, index, &res);
|
||||
if (!test) {
|
||||
_ai_sl_byte = res ? 1 : 0;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case OT_NULL: {
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_NULL;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
AILog::Error("You tried to save unsupported type. No data saved.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AIInstance::SaveEmpty()
|
||||
{
|
||||
_ai_sl_byte = 0;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
|
||||
void AIInstance::Save()
|
||||
{
|
||||
/* Don't save data if the AI didn't start yet. */
|
||||
if (this->engine == NULL) {
|
||||
SaveEmpty();
|
||||
return;
|
||||
}
|
||||
|
||||
/* We don't want to be interrupted during the save function. */
|
||||
AIObject::SetAllowDoCommand(false);
|
||||
|
||||
HSQOBJECT savedata;
|
||||
if (this->engine->MethodExists(*this->instance, "Save")) {
|
||||
this->engine->CallMethod(*this->instance, "Save", &savedata);
|
||||
if (!sq_istable(savedata)) {
|
||||
AILog::Error("Save function should return a table.");
|
||||
_ai_sl_byte = 0;
|
||||
SlObject(NULL, _ai_byte);
|
||||
AIObject::SetAllowDoCommand(true);
|
||||
return;
|
||||
}
|
||||
HSQUIRRELVM vm = this->engine->GetVM();
|
||||
sq_pushobject(vm, savedata);
|
||||
if (SaveObject(vm, -1, AISAVE_MAX_DEPTH, true)) {
|
||||
_ai_sl_byte = 1;
|
||||
SlObject(NULL, _ai_byte);
|
||||
SaveObject(vm, -1, AISAVE_MAX_DEPTH, false);
|
||||
} else {
|
||||
_ai_sl_byte = 0;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
sq_pop(vm, 1);
|
||||
} else {
|
||||
AILog::Warning("Save function is not implemented");
|
||||
_ai_sl_byte = 0;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
|
||||
AIObject::SetAllowDoCommand(true);
|
||||
}
|
||||
|
||||
/* static */ bool AIInstance::LoadObjects(HSQUIRRELVM vm)
|
||||
{
|
||||
SlObject(NULL, _ai_byte);
|
||||
switch (_ai_sl_byte) {
|
||||
case SQSL_INT: {
|
||||
int value;
|
||||
SlArray(&value, 1, SLE_INT32);
|
||||
if (vm != NULL) sq_pushinteger(vm, (SQInteger)value);
|
||||
return true;
|
||||
}
|
||||
|
||||
case SQSL_STRING: {
|
||||
SlObject(NULL, _ai_byte);
|
||||
static char buf[256];
|
||||
SlArray(buf, _ai_sl_byte, SLE_CHAR);
|
||||
if (vm != NULL) sq_pushstring(vm, OTTD2FS(buf), -1);
|
||||
return true;
|
||||
}
|
||||
|
||||
case SQSL_ARRAY: {
|
||||
if (vm != NULL) sq_newarray(vm, 0);
|
||||
while (LoadObjects(vm)) {
|
||||
if (vm != NULL) sq_arrayappend(vm, -2);
|
||||
/* The value is popped from the stack by squirrel. */
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case SQSL_TABLE: {
|
||||
if (vm != NULL) sq_newtable(vm);
|
||||
while (LoadObjects(vm)) {
|
||||
LoadObjects(vm);
|
||||
if (vm != NULL) sq_rawset(vm, -3);
|
||||
/* The key (-2) and value (-1) are popped from the stack by squirrel. */
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case SQSL_BOOL: {
|
||||
SlObject(NULL, _ai_byte);
|
||||
if (vm != NULL) sq_pushinteger(vm, (SQBool)(_ai_sl_byte != 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
case SQSL_NULL: {
|
||||
if (vm != NULL) sq_pushnull(vm);
|
||||
return true;
|
||||
}
|
||||
|
||||
case SQSL_ARRAY_TABLE_END: {
|
||||
return false;
|
||||
}
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AIInstance::LoadEmpty()
|
||||
{
|
||||
SlObject(NULL, _ai_byte);
|
||||
/* Check if there was anything saved at all. */
|
||||
if (_ai_sl_byte == 0) return;
|
||||
|
||||
LoadObjects(NULL);
|
||||
}
|
||||
|
||||
bool AIInstance::Load()
|
||||
{
|
||||
HSQUIRRELVM vm = (this->engine == NULL) ? NULL : this->engine->GetVM();
|
||||
|
||||
SlObject(NULL, _ai_byte);
|
||||
/* Check if there was anything saved at all. */
|
||||
if (_ai_sl_byte == 0) return true;
|
||||
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);
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
/* Pop 1) the object instance, 2) the function name, 3) the instance again, 4) the table. */
|
||||
if (vm != NULL) sq_pop(vm, 4);
|
||||
|
||||
AIObject::SetAllowDoCommand(true);
|
||||
return true;
|
||||
}
|
Reference in New Issue
Block a user