diff -r b90c0d1a36b7 -r ef0c109c5661 src/ai/squirrel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ai/squirrel.cpp Thu Mar 15 22:28:14 2007 +0000 @@ -0,0 +1,216 @@ +/* $Id$ */ + +/** @file squirrel.cpp allows loading squirrel scripts to control an AI */ + +#include "../../stdafx.h" +#include "../../debug.h" +#include "../../openttd.h" +#include "../../string.h" +#include "../../fios.h" +#include +#include + +#ifdef _UNICODE +/* Disable unicode for squirrel to allow compilation with MINGW + * and simplify coding for WIN32 (squirrel headers miss a lot of "string" functions) + */ +#undef _UNICODE +#endif +#include +#include "../../squirrel.hpp" +#include "../../squirrel_helper.hpp" +#include "../../squirrel_class.hpp" +#include "../core/ai_factory.hpp" +#include "../core/ai_controller.hpp" +#include "squirrel.hpp" + +/* Convert all AI related classes to Squirrel data */ +#define DEFINE_SQUIRREL_CLASS +#include "../core/ai_base.hpp" +#include "../core/ai_cargo.hpp" +#include "../core/ai_controller.hpp" +#include "../core/ai_company.hpp" +#include "../core/ai_industry.hpp" +#include "../core/ai_map.hpp" +#include "../core/ai_town.hpp" + +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[256]; + char script_name[256]; + 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 */ + strcpy(script_name, dirname); + strcat(script_name, PATHSEP); + strcat(script_name, d_name); + strcat(script_name, PATHSEP); + strcat(script_name, "main.nut"); + /* 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 */ + SQAIBaseRegister(this->engine); + SQAICargoRegister(this->engine); + SQAICompanyRegister(this->engine); + SQAIControllerRegister(this->engine); + SQAIIndustryRegister(this->engine); + SQAIMapRegister(this->engine); + SQAITownRegister(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); + this->engine->CallMethod(this->SQ_instance, "constructor"); +} + +AIControllerSquirrel::~AIControllerSquirrel() +{ + delete this->engine; +} + +/* virtual */ void AIControllerSquirrel::GameLoop() +{ + this->engine->CallMethod(this->SQ_instance, "GameLoop"); +}