(svn r12237) [NoAI] -Fix: don't force the existance of a 'constructor' (tnx Progman)
/* $Id$ */
/** @file squirrel.cpp allows loading squirrel scripts to control an AI */
#include "../stdafx.h"
#include "../debug.h"
#include "../openttd.h"
#include "../string_func.h"
#include "../fileio.h"
#include "../fios.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <squirrel.h>
#include "../squirrel.hpp"
#include "../squirrel_helper.hpp"
#include "../squirrel_class.hpp"
#include "../squirrel_std.hpp"
#include "api/ai_controller.hpp"
#include "ai_factory.hpp"
#include "ai_squirrel.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_cargo.hpp.sq"
#include "api/ai_company.hpp.sq"
#include "api/ai_controller.hpp.sq"
#include "api/ai_engine.hpp.sq"
#include "api/ai_enginelist.hpp.sq"
#include "api/ai_enginelist_valuator.hpp.sq"
#include "api/ai_event.hpp.sq"
#include "api/ai_event_types.hpp.sq"
#include "api/ai_execmode.hpp.sq"
#include "api/ai_industry.hpp.sq"
#include "api/ai_industrylist.hpp.sq"
#include "api/ai_industrylist_valuator.hpp.sq"
#include "api/ai_list.hpp.sq"
#include "api/ai_list_valuator.hpp.sq"
#include "api/ai_map.hpp.sq"
#include "api/ai_marine.hpp.sq"
#include "api/ai_order.hpp.sq"
#include "api/ai_pathfinder.hpp.sq"
#include "api/ai_pathfinder_stupid.hpp.sq"
#include "api/ai_road.hpp.sq"
#include "api/ai_settings.hpp.sq"
#include "api/ai_sign.hpp.sq"
#include "api/ai_station.hpp.sq"
#include "api/ai_stationlist.hpp.sq"
#include "api/ai_stationlist_valuator.hpp.sq"
#include "api/ai_testmode.hpp.sq"
#include "api/ai_tile.hpp.sq"
#include "api/ai_tilelist.hpp.sq"
#include "api/ai_tilelist_valuator.hpp.sq"
#include "api/ai_town.hpp.sq"
#include "api/ai_townlist.hpp.sq"
#include "api/ai_townlist_valuator.hpp.sq"
#include "api/ai_transactionmode.hpp.sq"
#include "api/ai_vehicle.hpp.sq"
#include "api/ai_vehiclelist.hpp.sq"
#include "api/ai_vehiclelist_valuator.hpp.sq"
static FSquirrel iFSquirrel; ///< Tell the AI-core that we have an AI with which we like to play.
/**
* Our tiny wrapper between C++ and Squirrel.
*/
class AIFactorySquirrel: public AIFactoryBase {
public:
Squirrel *engine;
HSQOBJECT SQ_instance;
char *script_name;
~AIFactorySquirrel();
/* virtual */ const char *GetAuthor() { return this->engine->CallStringMethod (this->SQ_instance, "GetAuthor"); }
/* virtual */ const char *GetName() { return this->engine->CallStringMethod (this->SQ_instance, "GetName"); }
/* virtual */ const char *GetDescription() { return this->engine->CallStringMethod (this->SQ_instance, "GetDescription"); }
/* virtual */ int GetVersion() { return this->engine->CallIntegerMethod(this->SQ_instance, "GetVersion"); }
/* virtual */ const char *GetDate() { return this->engine->CallStringMethod (this->SQ_instance, "GetDate"); }
/* virtual */ AIController *CreateInstance();
/**
* Register Squirrel script to the Factory.
*/
void RegisterSquirrel() { this->RegisterFactory(this->GetName()); }
/**
* Check if a method exists in the script.
*/
void CheckMethods(AIFactorySquirrel *fbase, SQInteger *res, const char *name);
};
AIFactorySquirrel::~AIFactorySquirrel()
{
free(this->script_name);
}
void AIFactorySquirrel::CheckMethods(AIFactorySquirrel *fbase, SQInteger *res, const char *name)
{
if (!fbase->engine->MethodExists(fbase->SQ_instance, name)) {
char error[1024];
snprintf(error, sizeof(error), "Missing method '%s' for FactoryClass", name);
fbase->engine->ThrowError(error);
*res = SQ_ERROR;
}
}
SQInteger FSquirrel::FactoryConstructor(HSQUIRRELVM vm)
{
AIFactorySquirrel *fbase = new AIFactorySquirrel();
SQInteger res = 0;
Squirrel::GetInstance(vm, &fbase->SQ_instance);
fbase->engine = ((FSquirrel *)Squirrel::GetGlobalPointer(vm))->engine;
/* Check if all needed fields are there */
fbase->CheckMethods(fbase, &res, "GetAuthor");
fbase->CheckMethods(fbase, &res, "GetName");
fbase->CheckMethods(fbase, &res, "GetDescription");
fbase->CheckMethods(fbase, &res, "GetVersion");
fbase->CheckMethods(fbase, &res, "GetDate");
fbase->CheckMethods(fbase, &res, "CreateInstance");
/* Abort if one method was missing */
if (res != 0) return res;
fbase->RegisterSquirrel();
fbase->script_name = strdup(((FSquirrel *)Squirrel::GetGlobalPointer(vm))->current_script);
return 0;
}
extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
extern bool FiosIsHiddenFile(const struct dirent *ent);
void FSquirrel::ScanDir(const char *dirname)
{
struct stat sb;
struct dirent *dirent;
DIR *dir;
char d_name[MAX_PATH];
char script_name[MAX_PATH];
dir = ttd_opendir(dirname);
/* Dir not found, so do nothing */
if (dir == NULL) return;
/* Walk all dirs trying to find a dir in which 'main.nut' exists */
while ((dirent = readdir(dir)) != NULL) {
ttd_strlcpy(d_name, FS2OTTD(dirent->d_name), sizeof(d_name));
/* Found file must be directory, but not '.' or '..' */
if (FiosIsValidFile("ai/", dirent, &sb) && (sb.st_mode & S_IFDIR) &&
(!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
/* Create the full-length script-name */
ttd_strlcpy(script_name, dirname, sizeof(script_name));
ttd_strlcat(script_name, PATHSEP, sizeof(script_name));
ttd_strlcat(script_name, d_name, sizeof(script_name));
ttd_strlcat(script_name, PATHSEP, sizeof(script_name));
ttd_strlcat(script_name, "main.nut", sizeof(script_name));
/* If it exists, load it up */
if (FileExists(script_name)) {
DEBUG(ai, 6, "[squirrel] Loading script '%s' for AI handling", script_name);
this->current_script = script_name;
this->engine->LoadScript(this->current_script);
}
}
}
closedir(dir);
}
void FSquirrel::Initializer()
{
this->engine = new Squirrel();
/* Create the AIFactory class, and bind the constructor */
this->engine->AddClassBegin("AIFactory");
this->engine->AddMethod("constructor", &FSquirrel::FactoryConstructor, 1, "x");
this->engine->AddClassEnd();
/* Set a dummy AIController, so script can load */
this->engine->AddClassBegin("AIController");
this->engine->AddClassEnd();
/* Mark this class as global pointer */
this->engine->SetGlobalPointer(this);
/* Scan the AI dir for scripts */
this->ScanDir("ai");
}
FSquirrel::~FSquirrel()
{
delete this->engine;
}
AIController *AIFactorySquirrel::CreateInstance()
{
const char *class_name = this->engine->CallStringMethod(this->SQ_instance, "CreateInstance");
AIControllerSquirrel *controller = new AIControllerSquirrel(this->script_name, class_name);
return controller;
}
void AIControllerSquirrel::RegisterClasses()
{
/* Ignore AIFactory if we are really starting an AI */
this->engine->AddClassBegin("AIFactory");
this->engine->AddClassEnd();
/* Register all classes */
squirrel_register_std(this->engine);
SQAIAbstractList_Register(this->engine);
SQAIAccounting_Register(this->engine);
SQAIAirport_Register(this->engine);
SQAIBase_Register(this->engine);
SQAICargo_Register(this->engine);
SQAICompany_Register(this->engine);
SQAIController_Register(this->engine);
SQAIEngine_Register(this->engine);
SQAIEngineList_Register(this->engine);
SQAIEngineList_vCapacity_Register(this->engine);
SQAIEngineList_vCargoType_Register(this->engine);
SQAIEngineList_vMaxSpeed_Register(this->engine);
SQAIEngineList_vPrice_Register(this->engine);
SQAIEngineList_vReliability_Register(this->engine);
SQAIEvent_Register(this->engine);
SQAIEventController_Register(this->engine);
SQAIEventTest_Register(this->engine);
SQAIEventVehicleCrash_Register(this->engine);
SQAIExecMode_Register(this->engine);
SQAIIndustry_Register(this->engine);
SQAIIndustryList_Register(this->engine);
SQAIIndustryList_vCargoAccepted_Register(this->engine);
SQAIIndustryList_vDistanceManhattanToTile_Register(this->engine);
SQAIIndustryList_vDistanceSquareToTile_Register(this->engine);
SQAIIndustryList_vLocation_Register(this->engine);
SQAIIndustryList_vProduction_Register(this->engine);
SQAIList_Register(this->engine);
SQAIList_vRandomize_Register(this->engine);
SQAIMap_Register(this->engine);
SQAIMarine_Register(this->engine);
SQAIOrder_Register(this->engine);
SQAIPathFinder_Register(this->engine);
SQAIPathFinderStupid_Register(this->engine);
SQAIRoad_Register(this->engine);
SQAISettings_Register(this->engine);
SQAISign_Register(this->engine);
SQAIStation_Register(this->engine);
SQAIStationList_Register(this->engine);
SQAIStationList_Vehicle_Register(this->engine);
SQAIStationList_vCargoRating_Register(this->engine);
SQAIStationList_vCargoWaiting_Register(this->engine);
SQAIStationList_vDistanceManhattanToTile_Register(this->engine);
SQAIStationList_vDistanceSquareToTile_Register(this->engine);
SQAIStationList_vLocation_Register(this->engine);
SQAITestMode_Register(this->engine);
SQAITile_Register(this->engine);
SQAITileList_Register(this->engine);
SQAITileList_vBuildable_Register(this->engine);
SQAITileList_vBuildableRectangle_Register(this->engine);
SQAITileList_vCargoAcceptance_Register(this->engine);
SQAITileList_vDistanceManhattanToTile_Register(this->engine);
SQAITileList_vDistanceSquareToTile_Register(this->engine);
SQAITileList_vHeight_Register(this->engine);
SQAITileList_vNeighbourRoadCount_Register(this->engine);
SQAITileList_vRoadTile_Register(this->engine);
SQAITileList_vSlope_Register(this->engine);
SQAITileList_vWater_Register(this->engine);
SQAITown_Register(this->engine);
SQAITownList_Register(this->engine);
SQAITownList_vDistanceManhattanToTile_Register(this->engine);
SQAITownList_vDistanceSquareToTile_Register(this->engine);
SQAITownList_vLocation_Register(this->engine);
SQAITownList_vPopulation_Register(this->engine);
SQAITownList_vRandomize_Register(this->engine);
SQAITransactionMode_Register(this->engine);
SQAIVehicle_Register(this->engine);
SQAIVehicleList_Register(this->engine);
SQAIVehicleList_Station_Register(this->engine);
SQAIVehicleList_vAge_Register(this->engine);
SQAIVehicleList_vAgeLeft_Register(this->engine);
SQAIVehicleList_vEngineType_Register(this->engine);
SQAIVehicleList_vLocation_Register(this->engine);
SQAIVehicleList_vMaxAge_Register(this->engine);
SQAIVehicleList_vProfitLastYear_Register(this->engine);
SQAIVehicleList_vProfitThisYear_Register(this->engine);
SQAIVehicleList_vUnitNumber_Register(this->engine);
SQAIVehicleList_vVehicleType_Register(this->engine);
this->engine->SetGlobalPointer(this->engine);
}
AIControllerSquirrel::AIControllerSquirrel(const char *script, const char *class_name)
{
this->engine = new Squirrel();
this->RegisterClasses();
this->engine->LoadScript(script);
/* Create the main-class */
this->engine->CreateClassInstance(class_name, this, &this->SQ_instance);
if (this->engine->MethodExists(this->SQ_instance, "constructor"))
this->engine->CallMethod(this->SQ_instance, "constructor");
}
AIControllerSquirrel::~AIControllerSquirrel()
{
delete this->engine;
}
/* virtual */ void AIControllerSquirrel::Start()
{
this->engine->CallMethod(this->SQ_instance, "Start");
}
/* virtual */ void AIControllerSquirrel::Stop()
{
this->engine->CallMethod(this->SQ_instance, "Stop");
}