2009-01-12 17:11:45 +00:00
|
|
|
/* $Id$ */
|
|
|
|
|
2009-08-21 20:21:05 +00:00
|
|
|
/*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
2009-01-12 17:11:45 +00:00
|
|
|
/** @file ai_instance.cpp Implementation of AIInstance. */
|
|
|
|
|
|
|
|
#include "../stdafx.h"
|
|
|
|
#include "../debug.h"
|
|
|
|
#include "../saveload/saveload.h"
|
2009-03-14 01:32:04 +00:00
|
|
|
#include "../gui.h"
|
2009-01-12 17:11:45 +00:00
|
|
|
|
|
|
|
#include "../script/squirrel_class.hpp"
|
2009-01-16 00:05:26 +00:00
|
|
|
|
2009-04-19 15:14:23 +00:00
|
|
|
#include "ai_config.hpp"
|
2009-01-12 17:11:45 +00:00
|
|
|
#include "ai_storage.hpp"
|
|
|
|
#include "ai_instance.hpp"
|
2009-03-14 01:32:04 +00:00
|
|
|
#include "ai_gui.hpp"
|
2009-01-12 17:11:45 +00:00
|
|
|
|
|
|
|
/* Convert all AI related classes to Squirrel data.
|
2011-11-19 15:42:40 +00:00
|
|
|
* Note: this line is a marker in squirrel_export.sh. Do not change! */
|
2009-01-12 17:11:45 +00:00
|
|
|
#include "api/ai_accounting.hpp.sq"
|
|
|
|
#include "api/ai_airport.hpp.sq"
|
|
|
|
#include "api/ai_base.hpp.sq"
|
2009-07-31 22:30:54 +00:00
|
|
|
#include "api/ai_basestation.hpp.sq"
|
2009-01-12 17:11:45 +00:00
|
|
|
#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"
|
2010-08-19 15:37:28 +00:00
|
|
|
#include "api/ai_list.hpp.sq"
|
2009-01-12 17:11:45 +00:00
|
|
|
#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"
|
2009-05-23 14:54:55 +00:00
|
|
|
#include "api/ai_signlist.hpp.sq"
|
2009-01-12 17:11:45 +00:00
|
|
|
#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"
|
2009-02-19 07:40:08 +00:00
|
|
|
#include "api/ai_waypoint.hpp.sq"
|
|
|
|
#include "api/ai_waypointlist.hpp.sq"
|
2009-01-12 17:11:45 +00:00
|
|
|
|
2010-01-15 16:41:15 +00:00
|
|
|
#include "../company_base.h"
|
2011-01-22 14:52:20 +00:00
|
|
|
#include "../company_func.h"
|
2009-08-18 18:51:42 +00:00
|
|
|
#include "../fileio_func.h"
|
|
|
|
|
2010-11-24 17:00:37 +00:00
|
|
|
/** The maximum number of operations for saving or loading the data of an AI. */
|
|
|
|
static const int MAX_SL_OPS = 100000;
|
|
|
|
/** The maximum number of operations for initial start of an AI. */
|
|
|
|
static const int MAX_CONSTRUCTOR_OPS = 100000;
|
|
|
|
|
2009-01-12 17:11:45 +00:00
|
|
|
AIStorage::~AIStorage()
|
|
|
|
{
|
|
|
|
/* Free our pointers */
|
|
|
|
if (event_data != NULL) AIEventController::FreeEventPointer();
|
|
|
|
if (log_data != NULL) AILog::FreeLogPointer();
|
|
|
|
}
|
|
|
|
|
2010-07-31 22:16:34 +00:00
|
|
|
/**
|
|
|
|
* Callback called by squirrel when an AI uses "print" and for error messages.
|
|
|
|
* @param error_msg Is this an error message?
|
|
|
|
* @param message The actual message text.
|
|
|
|
*/
|
2009-01-12 17:11:45 +00:00
|
|
|
static void PrintFunc(bool error_msg, const SQChar *message)
|
|
|
|
{
|
|
|
|
/* Convert to OpenTTD internal capable string */
|
2010-01-18 15:41:38 +00:00
|
|
|
AIController::Print(error_msg, SQ2OTTD(message));
|
2009-01-12 17:11:45 +00:00
|
|
|
}
|
|
|
|
|
2011-11-13 20:43:48 +00:00
|
|
|
AIInstance::AIInstance() :
|
2009-01-12 17:11:45 +00:00
|
|
|
controller(NULL),
|
|
|
|
storage(NULL),
|
|
|
|
engine(NULL),
|
|
|
|
instance(NULL),
|
|
|
|
is_started(false),
|
|
|
|
is_dead(false),
|
2009-07-15 19:47:06 +00:00
|
|
|
is_save_data_on_stack(false),
|
2009-01-12 17:11:45 +00:00
|
|
|
suspend(0),
|
|
|
|
callback(NULL)
|
|
|
|
{
|
2011-11-13 20:43:48 +00:00
|
|
|
this->storage = new AIStorage();
|
|
|
|
this->engine = new Squirrel();
|
|
|
|
this->engine->SetPrintFunction(&PrintFunc);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AIInstance::Initialize(AIInfo *info)
|
|
|
|
{
|
|
|
|
AIObject::ActiveInstance active(this);
|
2009-01-12 17:11:45 +00:00
|
|
|
|
|
|
|
this->controller = new AIController();
|
|
|
|
|
|
|
|
/* The import method is available at a very early stage */
|
2009-02-21 00:43:18 +00:00
|
|
|
this->engine->AddMethod("import", &AILibrary::Import, 4, ".ssi");
|
2009-01-12 17:11:45 +00:00
|
|
|
|
|
|
|
/* Register the AIController */
|
|
|
|
SQAIController_Register(this->engine);
|
|
|
|
|
2009-08-02 18:26:32 +00:00
|
|
|
/* Register the API functions and classes */
|
|
|
|
this->RegisterAPI();
|
|
|
|
|
2009-08-18 18:51:42 +00:00
|
|
|
if (!this->LoadCompatibilityScripts(info->GetAPIVersion())) {
|
|
|
|
this->Died();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-09-15 16:16:28 +00:00
|
|
|
try {
|
|
|
|
AIObject::SetAllowDoCommand(false);
|
|
|
|
/* Load and execute the script for this AI */
|
|
|
|
const char *main_script = info->GetMainScript();
|
|
|
|
if (strcmp(main_script, "%_dummy") == 0) {
|
|
|
|
extern void AI_CreateAIDummy(HSQUIRRELVM vm);
|
|
|
|
AI_CreateAIDummy(this->engine->GetVM());
|
2009-09-15 16:18:10 +00:00
|
|
|
} else if (!this->engine->LoadScript(main_script) || this->engine->IsSuspended()) {
|
|
|
|
if (this->engine->IsSuspended()) AILog::Error("This AI took too long to load script. AI is not started.");
|
2009-09-15 16:16:28 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
AIObject::SetAllowDoCommand(true);
|
|
|
|
} catch (AI_FatalError e) {
|
|
|
|
this->is_dead = true;
|
|
|
|
this->engine->ThrowError(e.GetErrorMessage());
|
|
|
|
this->engine->ResumeError();
|
2009-01-12 17:11:45 +00:00
|
|
|
this->Died();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
AIInstance::~AIInstance()
|
|
|
|
{
|
2011-11-13 20:43:48 +00:00
|
|
|
AIObject::ActiveInstance active(this);
|
|
|
|
|
2009-01-17 15:38:37 +00:00
|
|
|
if (instance != NULL) this->engine->ReleaseObject(this->instance);
|
2009-01-12 17:11:45 +00:00
|
|
|
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);
|
2010-08-19 15:37:28 +00:00
|
|
|
SQAIList_Register(this->engine);
|
2009-01-12 17:11:45 +00:00
|
|
|
SQAIAccounting_Register(this->engine);
|
|
|
|
SQAIAirport_Register(this->engine);
|
|
|
|
SQAIBase_Register(this->engine);
|
2009-07-31 22:30:54 +00:00
|
|
|
SQAIBaseStation_Register(this->engine);
|
2009-01-12 17:11:45 +00:00
|
|
|
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);
|
2011-11-08 15:44:53 +00:00
|
|
|
SQAICargoList_StationAccepting_Register(this->engine);
|
2009-01-12 17:11:45 +00:00
|
|
|
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);
|
2009-09-01 12:57:04 +00:00
|
|
|
SQAIEventCompanyAskMerger_Register(this->engine);
|
2009-01-12 17:11:45 +00:00
|
|
|
SQAIEventCompanyBankrupt_Register(this->engine);
|
|
|
|
SQAIEventCompanyInTrouble_Register(this->engine);
|
|
|
|
SQAIEventCompanyMerger_Register(this->engine);
|
|
|
|
SQAIEventCompanyNew_Register(this->engine);
|
|
|
|
SQAIEventController_Register(this->engine);
|
2009-02-05 17:38:47 +00:00
|
|
|
SQAIEventDisasterZeppelinerCleared_Register(this->engine);
|
|
|
|
SQAIEventDisasterZeppelinerCrashed_Register(this->engine);
|
2009-01-12 17:11:45 +00:00
|
|
|
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);
|
2010-12-29 23:44:39 +00:00
|
|
|
SQAIEventTownFounded_Register(this->engine);
|
2009-01-12 17:11:45 +00:00
|
|
|
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);
|
|
|
|
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);
|
2009-05-23 14:54:55 +00:00
|
|
|
SQAISignList_Register(this->engine);
|
2009-01-12 17:11:45 +00:00
|
|
|
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);
|
2009-03-04 22:37:25 +00:00
|
|
|
SQAIVehicleList_DefaultGroup_Register(this->engine);
|
2009-09-09 09:46:08 +00:00
|
|
|
SQAIVehicleList_Depot_Register(this->engine);
|
2009-03-04 22:37:25 +00:00
|
|
|
SQAIVehicleList_Group_Register(this->engine);
|
2009-02-05 17:28:37 +00:00
|
|
|
SQAIVehicleList_SharedOrders_Register(this->engine);
|
2009-01-12 17:11:45 +00:00
|
|
|
SQAIVehicleList_Station_Register(this->engine);
|
2009-02-20 10:03:16 +00:00
|
|
|
SQAIWaypoint_Register(this->engine);
|
|
|
|
SQAIWaypointList_Register(this->engine);
|
|
|
|
SQAIWaypointList_Vehicle_Register(this->engine);
|
2009-01-12 17:11:45 +00:00
|
|
|
|
|
|
|
this->engine->SetGlobalPointer(this->engine);
|
|
|
|
}
|
|
|
|
|
2009-08-18 18:51:42 +00:00
|
|
|
bool AIInstance::LoadCompatibilityScripts(const char *api_version)
|
|
|
|
{
|
|
|
|
char script_name[32];
|
|
|
|
seprintf(script_name, lastof(script_name), "compat_%s.nut", api_version);
|
|
|
|
char buf[MAX_PATH];
|
|
|
|
Searchpath sp;
|
|
|
|
FOR_ALL_SEARCHPATHS(sp) {
|
|
|
|
FioAppendDirectory(buf, MAX_PATH, sp, AI_DIR);
|
|
|
|
ttd_strlcat(buf, script_name, MAX_PATH);
|
|
|
|
if (!FileExists(buf)) continue;
|
|
|
|
|
|
|
|
if (this->engine->LoadScript(buf)) return true;
|
|
|
|
|
|
|
|
AILog::Error("Failed to load API compatibility script");
|
|
|
|
DEBUG(ai, 0, "Error compiling / running API compatibility script: %s", buf);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
AILog::Warning("API compatibility script not found");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-01-12 17:11:45 +00:00
|
|
|
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;
|
|
|
|
|
2009-01-17 22:22:53 +00:00
|
|
|
if (this->instance != NULL) this->engine->ReleaseObject(this->instance);
|
2009-01-12 17:11:45 +00:00
|
|
|
delete this->engine;
|
2009-01-17 15:38:37 +00:00
|
|
|
this->instance = NULL;
|
2009-01-12 17:11:45 +00:00
|
|
|
this->engine = NULL;
|
2009-03-14 01:32:04 +00:00
|
|
|
|
|
|
|
ShowAIDebugWindow(_current_company);
|
2009-04-19 15:14:23 +00:00
|
|
|
|
|
|
|
const AIInfo *info = AIConfig::GetConfig(_current_company)->GetInfo();
|
2009-04-26 19:30:31 +00:00
|
|
|
if (info != NULL) {
|
2010-02-24 14:46:15 +00:00
|
|
|
ShowErrorMessage(STR_ERROR_AI_PLEASE_REPORT_CRASH, INVALID_STRING_ID, WL_WARNING);
|
2009-04-26 19:30:31 +00:00
|
|
|
|
|
|
|
if (info->GetURL() != NULL) {
|
|
|
|
AILog::Info("Please report the error to the following URL:");
|
|
|
|
AILog::Info(info->GetURL());
|
|
|
|
}
|
2009-04-19 15:14:23 +00:00
|
|
|
}
|
2009-01-12 17:11:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AIInstance::GameLoop()
|
|
|
|
{
|
2011-11-13 20:43:48 +00:00
|
|
|
AIObject::ActiveInstance active(this);
|
|
|
|
|
2009-06-10 19:23:25 +00:00
|
|
|
if (this->IsDead()) return;
|
2009-02-13 17:17:34 +00:00
|
|
|
if (this->engine->HasScriptCrashed()) {
|
|
|
|
/* The script crashed during saving, kill it here. */
|
|
|
|
this->Died();
|
|
|
|
return;
|
|
|
|
}
|
2009-01-12 17:11:45 +00:00
|
|
|
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) {
|
2009-07-15 19:47:06 +00:00
|
|
|
if (this->is_save_data_on_stack) {
|
|
|
|
sq_poptop(this->engine->GetVM());
|
|
|
|
this->is_save_data_on_stack = false;
|
|
|
|
}
|
2009-01-12 17:11:45 +00:00
|
|
|
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) {
|
|
|
|
try {
|
2009-02-10 19:22:09 +00:00
|
|
|
AIObject::SetAllowDoCommand(false);
|
2009-02-03 20:49:08 +00:00
|
|
|
/* Run the constructor if it exists. Don't allow any DoCommands in it. */
|
|
|
|
if (this->engine->MethodExists(*this->instance, "constructor")) {
|
2010-11-24 17:00:37 +00:00
|
|
|
if (!this->engine->CallMethod(*this->instance, "constructor", MAX_CONSTRUCTOR_OPS) || this->engine->IsSuspended()) {
|
2009-05-25 21:55:47 +00:00
|
|
|
if (this->engine->IsSuspended()) AILog::Error("This AI took too long to initialize. AI is not started.");
|
|
|
|
this->Died();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!this->CallLoad() || this->engine->IsSuspended()) {
|
|
|
|
if (this->engine->IsSuspended()) AILog::Error("This AI took too long in the Load function. AI is not started.");
|
|
|
|
this->Died();
|
|
|
|
return;
|
2009-02-03 20:49:08 +00:00
|
|
|
}
|
2009-02-10 19:22:09 +00:00
|
|
|
AIObject::SetAllowDoCommand(true);
|
2009-02-03 20:49:08 +00:00
|
|
|
/* Start the AI by calling Start() */
|
2009-02-13 17:17:34 +00:00
|
|
|
if (!this->engine->CallMethod(*this->instance, "Start", _settings_game.ai.ai_max_opcode_till_suspend) || !this->engine->IsSuspended()) this->Died();
|
2009-01-12 17:11:45 +00:00
|
|
|
} catch (AI_VMSuspend e) {
|
|
|
|
this->suspend = e.GetSuspendTime();
|
|
|
|
this->callback = e.GetSuspendCallback();
|
2009-08-19 16:14:15 +00:00
|
|
|
} catch (AI_FatalError e) {
|
2009-08-20 10:39:39 +00:00
|
|
|
this->is_dead = true;
|
2009-08-19 16:14:15 +00:00
|
|
|
this->engine->ThrowError(e.GetErrorMessage());
|
|
|
|
this->engine->ResumeError();
|
|
|
|
this->Died();
|
2009-01-12 17:11:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this->is_started = true;
|
|
|
|
return;
|
|
|
|
}
|
2009-07-15 19:47:06 +00:00
|
|
|
if (this->is_save_data_on_stack) {
|
|
|
|
sq_poptop(this->engine->GetVM());
|
|
|
|
this->is_save_data_on_stack = false;
|
|
|
|
}
|
2009-01-12 17:11:45 +00:00
|
|
|
|
|
|
|
/* 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();
|
2009-08-19 16:14:15 +00:00
|
|
|
} catch (AI_FatalError e) {
|
2009-08-20 10:39:39 +00:00
|
|
|
this->is_dead = true;
|
2009-08-19 16:14:15 +00:00
|
|
|
this->engine->ThrowError(e.GetErrorMessage());
|
|
|
|
this->engine->ResumeError();
|
|
|
|
this->Died();
|
2009-01-12 17:11:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-07 00:09:27 +00:00
|
|
|
void AIInstance::CollectGarbage() const
|
2009-02-03 22:42:42 +00:00
|
|
|
{
|
2009-06-10 19:23:25 +00:00
|
|
|
if (this->is_started && !this->IsDead()) this->engine->CollectGarbage();
|
2009-02-03 22:42:42 +00:00
|
|
|
}
|
|
|
|
|
2009-01-12 17:11:45 +00:00
|
|
|
/* 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());
|
|
|
|
}
|
|
|
|
|
2011-11-13 20:43:48 +00:00
|
|
|
AIStorage *AIInstance::GetStorage()
|
2009-01-12 17:11:45 +00:00
|
|
|
{
|
2011-11-13 20:43:48 +00:00
|
|
|
return this->storage;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *AIInstance::GetLogPointer()
|
|
|
|
{
|
|
|
|
AIObject::ActiveInstance active(this);
|
|
|
|
|
|
|
|
return AIObject::GetLogPointer();
|
2009-01-12 17:11:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2010-05-09 18:13:36 +00:00
|
|
|
* array. The string can't be longer than 255 bytes (including
|
2009-01-12 17:11:45 +00:00
|
|
|
* 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.
|
|
|
|
};
|
|
|
|
|
2010-08-01 19:36:56 +00:00
|
|
|
static byte _ai_sl_byte; ///< Used as source/target by the AI saveload code to store/load a single byte.
|
2009-01-12 17:11:45 +00:00
|
|
|
|
2010-07-31 22:16:34 +00:00
|
|
|
/** SaveLoad array that saves/loads exactly one byte. */
|
2009-01-12 17:11:45 +00:00
|
|
|
static const SaveLoad _ai_byte[] = {
|
|
|
|
SLEG_VAR(_ai_sl_byte, SLE_UINT8),
|
|
|
|
SLE_END()
|
|
|
|
};
|
|
|
|
|
2010-05-13 10:14:29 +00:00
|
|
|
static const uint AISAVE_MAX_DEPTH = 25; ///< The maximum recursive depth for items stored in the savegame.
|
2009-01-12 17:11:45 +00:00
|
|
|
|
|
|
|
/* static */ bool AIInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
|
|
|
|
{
|
|
|
|
if (max_depth == 0) {
|
2009-03-11 07:32:31 +00:00
|
|
|
AILog::Error("Savedata can only be nested to 25 deep. No data saved.");
|
2009-01-12 17:11:45 +00:00
|
|
|
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);
|
2010-01-18 15:41:38 +00:00
|
|
|
/* @bug if a string longer than 512 characters is given to SQ2OTTD, the
|
2009-01-12 17:11:45 +00:00
|
|
|
* internal buffer overflows. */
|
2010-01-18 15:41:38 +00:00
|
|
|
const char *buf = SQ2OTTD(res);
|
2009-01-12 17:11:45 +00:00
|
|
|
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);
|
2011-11-12 08:37:12 +00:00
|
|
|
SlArray(const_cast<char *>(buf), len, SLE_CHAR);
|
2009-01-12 17:11:45 +00:00
|
|
|
}
|
|
|
|
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:
|
2009-03-11 07:32:31 +00:00
|
|
|
AILog::Error("You tried to save an unsupported type. No data saved.");
|
2009-01-12 17:11:45 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ void AIInstance::SaveEmpty()
|
|
|
|
{
|
|
|
|
_ai_sl_byte = 0;
|
|
|
|
SlObject(NULL, _ai_byte);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AIInstance::Save()
|
|
|
|
{
|
2011-11-13 20:43:48 +00:00
|
|
|
AIObject::ActiveInstance active(this);
|
|
|
|
|
2009-02-13 17:17:34 +00:00
|
|
|
/* Don't save data if the AI didn't start yet or if it crashed. */
|
|
|
|
if (this->engine == NULL || this->engine->HasScriptCrashed()) {
|
2009-01-12 17:11:45 +00:00
|
|
|
SaveEmpty();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-02-03 20:49:08 +00:00
|
|
|
HSQUIRRELVM vm = this->engine->GetVM();
|
2009-07-15 19:47:06 +00:00
|
|
|
if (this->is_save_data_on_stack) {
|
2009-02-03 20:49:08 +00:00
|
|
|
_ai_sl_byte = 1;
|
|
|
|
SlObject(NULL, _ai_byte);
|
|
|
|
/* Save the data that was just loaded. */
|
|
|
|
SaveObject(vm, -1, AISAVE_MAX_DEPTH, false);
|
2009-07-15 19:47:06 +00:00
|
|
|
} else if (!this->is_started) {
|
|
|
|
SaveEmpty();
|
|
|
|
return;
|
2009-02-03 20:49:08 +00:00
|
|
|
} else if (this->engine->MethodExists(*this->instance, "Save")) {
|
|
|
|
HSQOBJECT savedata;
|
|
|
|
/* We don't want to be interrupted during the save function. */
|
2009-02-10 19:22:09 +00:00
|
|
|
bool backup_allow = AIObject::GetAllowDoCommand();
|
2009-02-03 20:49:08 +00:00
|
|
|
AIObject::SetAllowDoCommand(false);
|
2009-08-20 10:58:22 +00:00
|
|
|
try {
|
2010-11-24 17:00:37 +00:00
|
|
|
if (!this->engine->CallMethod(*this->instance, "Save", &savedata, MAX_SL_OPS)) {
|
2009-08-20 10:58:22 +00:00
|
|
|
/* The script crashed in the Save function. We can't kill
|
|
|
|
* it here, but do so in the next AI tick. */
|
|
|
|
SaveEmpty();
|
|
|
|
this->engine->CrashOccurred();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} catch (AI_FatalError e) {
|
|
|
|
/* If we don't mark the AI as dead here cleaning up the squirrel
|
|
|
|
* stack could throw AI_FatalError again. */
|
|
|
|
this->is_dead = true;
|
|
|
|
this->engine->ThrowError(e.GetErrorMessage());
|
|
|
|
this->engine->ResumeError();
|
2009-02-13 17:17:34 +00:00
|
|
|
SaveEmpty();
|
2009-08-20 10:58:22 +00:00
|
|
|
/* We can't kill the AI here, so mark it as crashed (not dead) and
|
|
|
|
* kill it in the next AI tick. */
|
|
|
|
this->is_dead = false;
|
2009-08-20 11:02:35 +00:00
|
|
|
this->engine->CrashOccurred();
|
2009-02-13 17:17:34 +00:00
|
|
|
return;
|
|
|
|
}
|
2009-02-10 19:22:09 +00:00
|
|
|
AIObject::SetAllowDoCommand(backup_allow);
|
2009-02-03 20:49:08 +00:00
|
|
|
|
2009-01-12 17:11:45 +00:00
|
|
|
if (!sq_istable(savedata)) {
|
2010-11-24 17:00:37 +00:00
|
|
|
AILog::Error(this->engine->IsSuspended() ? "This AI took too long to Save." : "Save function should return a table.");
|
2009-02-03 20:49:08 +00:00
|
|
|
SaveEmpty();
|
2009-07-15 19:47:06 +00:00
|
|
|
this->engine->CrashOccurred();
|
2009-01-12 17:11:45 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
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);
|
2009-07-15 19:47:06 +00:00
|
|
|
this->is_save_data_on_stack = true;
|
2009-01-12 17:11:45 +00:00
|
|
|
} else {
|
2009-07-15 19:47:06 +00:00
|
|
|
SaveEmpty();
|
|
|
|
this->engine->CrashOccurred();
|
2009-01-12 17:11:45 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
AILog::Warning("Save function is not implemented");
|
|
|
|
_ai_sl_byte = 0;
|
|
|
|
SlObject(NULL, _ai_byte);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-02 17:35:20 +00:00
|
|
|
void AIInstance::Suspend()
|
|
|
|
{
|
|
|
|
HSQUIRRELVM vm = this->engine->GetVM();
|
|
|
|
Squirrel::DecreaseOps(vm, _settings_game.ai.ai_max_opcode_till_suspend);
|
|
|
|
}
|
|
|
|
|
2009-01-12 17:11:45 +00:00
|
|
|
/* 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);
|
2010-01-18 15:41:38 +00:00
|
|
|
if (vm != NULL) sq_pushstring(vm, OTTD2SQ(buf), -1);
|
2009-01-12 17:11:45 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2009-01-13 01:46:46 +00:00
|
|
|
void AIInstance::Load(int version)
|
2009-01-12 17:11:45 +00:00
|
|
|
{
|
2011-11-13 20:43:48 +00:00
|
|
|
AIObject::ActiveInstance active(this);
|
|
|
|
|
2009-01-13 01:46:46 +00:00
|
|
|
if (this->engine == NULL || version == -1) {
|
|
|
|
LoadEmpty();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
HSQUIRRELVM vm = this->engine->GetVM();
|
2009-01-12 17:11:45 +00:00
|
|
|
|
|
|
|
SlObject(NULL, _ai_byte);
|
|
|
|
/* Check if there was anything saved at all. */
|
2009-01-13 01:46:46 +00:00
|
|
|
if (_ai_sl_byte == 0) return;
|
2009-02-03 20:49:08 +00:00
|
|
|
|
|
|
|
sq_pushinteger(vm, version);
|
2009-02-03 21:05:32 +00:00
|
|
|
LoadObjects(vm);
|
2009-07-15 19:47:06 +00:00
|
|
|
this->is_save_data_on_stack = true;
|
2009-02-03 20:49:08 +00:00
|
|
|
}
|
|
|
|
|
2009-02-13 17:17:34 +00:00
|
|
|
bool AIInstance::CallLoad()
|
2009-02-03 20:49:08 +00:00
|
|
|
{
|
|
|
|
HSQUIRRELVM vm = this->engine->GetVM();
|
|
|
|
/* Is there save data that we should load? */
|
2009-07-15 19:47:06 +00:00
|
|
|
if (!this->is_save_data_on_stack) return true;
|
|
|
|
/* Whatever happens, after CallLoad the savegame data is removed from the stack. */
|
|
|
|
this->is_save_data_on_stack = false;
|
2009-02-03 20:49:08 +00:00
|
|
|
|
|
|
|
if (!this->engine->MethodExists(*this->instance, "Load")) {
|
|
|
|
AILog::Warning("Loading failed: there was data for the AI to load, but the AI does not have a Load() function.");
|
|
|
|
|
|
|
|
/* Pop the savegame data and version. */
|
|
|
|
sq_pop(vm, 2);
|
2009-02-13 17:17:34 +00:00
|
|
|
return true;
|
2009-02-03 20:49:08 +00:00
|
|
|
}
|
|
|
|
|
2009-01-13 01:46:46 +00:00
|
|
|
/* Go to the instance-root */
|
|
|
|
sq_pushobject(vm, *this->instance);
|
|
|
|
/* Find the function-name inside the script */
|
2010-01-18 15:41:38 +00:00
|
|
|
sq_pushstring(vm, OTTD2SQ("Load"), -1);
|
2009-02-03 20:49:08 +00:00
|
|
|
/* Change the "Load" string in a function pointer */
|
|
|
|
sq_get(vm, -2);
|
|
|
|
/* Push the main instance as "this" object */
|
2009-01-13 01:46:46 +00:00
|
|
|
sq_pushobject(vm, *this->instance);
|
2009-02-03 21:05:32 +00:00
|
|
|
/* Push the version data and savegame data as arguments */
|
2009-02-03 20:49:08 +00:00
|
|
|
sq_push(vm, -5);
|
|
|
|
sq_push(vm, -5);
|
2009-01-27 13:11:11 +00:00
|
|
|
|
2009-02-03 20:49:08 +00:00
|
|
|
/* Call the AI load function. sq_call removes the arguments (but not the
|
|
|
|
* function pointer) from the stack. */
|
2010-11-24 17:00:37 +00:00
|
|
|
if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse, MAX_SL_OPS))) return false;
|
2009-01-12 17:11:45 +00:00
|
|
|
|
2009-02-03 21:05:32 +00:00
|
|
|
/* Pop 1) The version, 2) the savegame data, 3) the object instance, 4) the function pointer. */
|
2009-02-03 20:49:08 +00:00
|
|
|
sq_pop(vm, 4);
|
2009-02-13 17:17:34 +00:00
|
|
|
return true;
|
2009-01-12 17:11:45 +00:00
|
|
|
}
|
2011-11-04 23:20:14 +00:00
|
|
|
|
|
|
|
SQInteger AIInstance::GetOpsTillSuspend()
|
|
|
|
{
|
|
|
|
return this->engine->GetOpsTillSuspend();
|
|
|
|
}
|
2011-11-13 20:43:48 +00:00
|
|
|
|
|
|
|
void AIInstance::DoCommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
|
|
|
|
{
|
|
|
|
AIObject::ActiveInstance active(this);
|
|
|
|
|
|
|
|
AIObject::SetLastCommandRes(result.Succeeded());
|
|
|
|
|
|
|
|
if (result.Failed()) {
|
|
|
|
AIObject::SetLastError(AIError::StringToError(result.GetErrorMessage()));
|
|
|
|
} else {
|
|
|
|
AIObject::IncreaseDoCommandCosts(result.GetCost());
|
|
|
|
AIObject::SetLastCost(result.GetCost());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AIInstance::InsertEvent(class AIEvent *event)
|
|
|
|
{
|
|
|
|
AIObject::ActiveInstance active(this);
|
|
|
|
|
|
|
|
AIEventController::InsertEvent(event);
|
|
|
|
}
|