src/ai/ai_squirrel.cpp
author rubidium
Wed, 09 Jan 2008 18:11:12 +0000
branchnoai
changeset 9723 eee46cb39750
parent 9714 fdbdae7ea647
child 9726 f80f1d0bae90
permissions -rw-r--r--
(svn r11796) [NoAI] -Sync: with trunk r11502:11795.
/* $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[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 */
	squirrel_register_std(this->engine);
	SQAIAbstractListRegister(this->engine);
	SQAIAccountingRegister(this->engine);
	SQAIAirportRegister(this->engine);
	SQAIBaseRegister(this->engine);
	SQAICargoRegister(this->engine);
	SQAICompanyRegister(this->engine);
	SQAIControllerRegister(this->engine);
	SQAIEngineListCapacityRegister(this->engine);
	SQAIEngineListCargoTypeRegister(this->engine);
	SQAIEngineListMaxSpeedRegister(this->engine);
	SQAIEngineListPriceRegister(this->engine);
	SQAIEngineListRegister(this->engine);
	SQAIEngineListReliabilityRegister(this->engine);
	SQAIEngineRegister(this->engine);
	SQAIEventControllerRegister(this->engine);
	SQAIEventRegister(this->engine);
	SQAIEventTestRegister(this->engine);
	SQAIEventVehicleCrashRegister(this->engine);
	SQAIExecModeRegister(this->engine);
	SQAIIndustryListCargoAcceptedRegister(this->engine);
	SQAIIndustryListDistanceManhattanToTileRegister(this->engine);
	SQAIIndustryListDistanceSquareToTileRegister(this->engine);
	SQAIIndustryListLocationRegister(this->engine);
	SQAIIndustryListProductionRegister(this->engine);
	SQAIIndustryListRegister(this->engine);
	SQAIIndustryRegister(this->engine);
	SQAIListRandomizeRegister(this->engine);
	SQAIListRegister(this->engine);
	SQAIMapRegister(this->engine);
	SQAIMarineRegister(this->engine);
	SQAIOrderRegister(this->engine);
	SQAIPathFinderRegister(this->engine);
	SQAIPathFinderStupidRegister(this->engine);
	SQAIRoadRegister(this->engine);
	SQAISettingsRegister(this->engine);
	SQAISignRegister(this->engine);
	SQAIStationListCargoRatingRegister(this->engine);
	SQAIStationListCargoWaitingRegister(this->engine);
	SQAIStationListDistanceManhattanToTileRegister(this->engine);
	SQAIStationListDistanceSquareToTileRegister(this->engine);
	SQAIStationListLocationRegister(this->engine);
	SQAIStationListRegister(this->engine);
	SQAIStationRegister(this->engine);
	SQAIStationVehicleListRegister(this->engine);
	SQAITestModeRegister(this->engine);
	SQAITileListBuildableRectangleRegister(this->engine);
	SQAITileListBuildableRegister(this->engine);
	SQAITileListCargoAcceptanceRegister(this->engine);
	SQAITileListDistanceManhattanToTileRegister(this->engine);
	SQAITileListDistanceSquareToTileRegister(this->engine);
	SQAITileListHeightRegister(this->engine);
	SQAITileListNeighbourRoadCountRegister(this->engine);
	SQAITileListRegister(this->engine);
	SQAITileListRoadTileRegister(this->engine);
	SQAITileListSlopeRegister(this->engine);
	SQAITileListWaterRegister(this->engine);
	SQAITileRegister(this->engine);
	SQAITownListDistanceManhattanToTileRegister(this->engine);
	SQAITownListDistanceSquareToTileRegister(this->engine);
	SQAITownListLocationRegister(this->engine);
	SQAITownListPopulationRegister(this->engine);
	SQAITownListRandomizeRegister(this->engine);
	SQAITownListRegister(this->engine);
	SQAITownRegister(this->engine);
	SQAITransactionModeRegister(this->engine);
	SQAIVehicleListAgeLeftRegister(this->engine);
	SQAIVehicleListAgeRegister(this->engine);
	SQAIVehicleListEngineTypeRegister(this->engine);
	SQAIVehicleListLocationRegister(this->engine);
	SQAIVehicleListMaxAgeRegister(this->engine);
	SQAIVehicleListProfitLastYearRegister(this->engine);
	SQAIVehicleListProfitThisYearRegister(this->engine);
	SQAIVehicleListRegister(this->engine);
	SQAIVehicleListUnitNumberRegister(this->engine);
	SQAIVehicleListVehicleTypeRegister(this->engine);
	SQAIVehicleRegister(this->engine);
	SQAIVehicleStationListRegister(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::Start()
{
	this->engine->CallMethod(this->SQ_instance, "Start");
}

/* virtual */ void AIControllerSquirrel::Stop()
{
	this->engine->CallMethod(this->SQ_instance, "Stop");
}