src/ai/ai_squirrel.cpp
author truebrain
Sun, 24 Feb 2008 22:13:24 +0000
branchnoai
changeset 9754 ec106fe84b72
parent 9753 7209db94ad12
child 9757 5cdc14959fb6
permissions -rw-r--r--
(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");
}