/* $Id$ */
/** @file squirrel.cpp allows loading squirrel scripts to control an AI */
#include "squirrel.hpp"
#include <sqstdio.h>
#include <sqstdaux.h>
#include <stdarg.h>
#include "../../debug.h"
/**
* Here we handle errors that come from squirrel.
*/
static void Squirrel_CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *source, SQInteger line, SQInteger column)
{
#ifdef _SQ64
printf("Error %s:%ld/%ld: %s\n", source, line, column, desc);
#else
printf("Error %s:%d/%d: %s\n", source, line, column, desc);
#endif
}
/**
* Here we handle run-time errors that come from squirrel.
*/
static SQInteger Squirrel_RunError(HSQUIRRELVM vm)
{
const SQChar *sErr = 0;
if (sq_gettop(vm) >= 1) {
if (SQ_SUCCEEDED(sq_getstring(vm, -1, &sErr))) {
printf("%s\n", sErr);
} else {
printf("Unknown error\n");
}
}
return 0;
}
/**
* Here we handle 'print' command within squirrel script.
*/
static void Squirrel_PrintFunc(HSQUIRRELVM vm, const SQChar *s, ...)
{
va_list arglist;
va_start(arglist, s);
vprintf(s, arglist);
va_end(arglist);
printf("\n");
}
SQInteger Squirrel::SQDestructor(SQUserPointer p, SQInteger size)
{
return 1;
}
SQInteger Squirrel::SQConstructor(HSQUIRRELVM vm)
{
Squirrel *self = NULL;
HSQOBJECT object;
SQUserPointer SQ_instance;
/* Get the instance pointer */
sq_getclass(vm, 1);
sq_getstackobj(vm, 1, &object);
SQ_instance = object._unVal.pUserPointer;
/* Find the 'this' pointer and store it inside the class */
self = (Squirrel *)sq_getforeignptr(vm);
if (self == NULL) {
DEBUG(ai, 0, "[squirrel] Please only make one instance of AIController!");
return SQ_ERROR;
}
sq_setinstanceup(vm, 1, self);
sq_setreleasehook(vm, 1, &Squirrel::SQDestructor);
/* Keep track of the internal Squirrel instance */
self->SQ_instance = SQ_instance;
/* Make sure we only register one AI in this script */
sq_setforeignptr(vm, NULL);
return 0;
}
SQInteger Squirrel::SQGetTick(HSQUIRRELVM vm)
{
Squirrel *self;
/* Find the 'self' pointer */
if (SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer *)&self, 0))) {
DEBUG(ai, 0, "[squirrel] Failed to find the instance-pointer");
return SQ_ERROR;
}
/* Return the value */
sq_pushinteger(vm, self->GetTick());
return 1;
}
inline void Squirrel::SQAddMethod(const char *name, SQFUNCTION proc, uint nparam, const char *params)
{
sq_pushstring(this->vm, name, -1);
sq_newclosure(this->vm, proc, 0);
sq_setparamscheck(this->vm, nparam, params);
sq_setnativeclosurename(this->vm, -1, name);
sq_createslot(this->vm, -3);
}
void Squirrel::RegisterClasses()
{
SQInteger top = sq_gettop(this->vm);
/* Store 'this' so we can find it back later on */
sq_setforeignptr(this->vm, this);
/* Create AIController class (the 'main' of it all) */
sq_pushroottable(this->vm);
sq_pushstring(this->vm, "AIController", -1);
sq_newclass(this->vm, SQFalse);
/* Create a constructor, so we can assign the right 'this' */
this->SQAddMethod("constructor", &Squirrel::SQConstructor, 1, "x");
/* Assign other functions as AIController won't be done via the normal way */
this->SQAddMethod("GetTick", &Squirrel::SQGetTick, 1, "x");
/* Finally really create the class */
sq_createslot(this->vm, -3);
/* Reset top */
sq_settop(this->vm, top);
}
Squirrel::Squirrel(const char *script_dir)
{
char filename[1024];
this->vm = sq_open(1024);
/* Handle compile-errors ourself, so we can display it nicely */
sq_setcompilererrorhandler(this->vm, Squirrel_CompileError);
sq_notifyallexceptions(this->vm, SQTrue);
/* Set a good print-function */
sq_setprintfunc(this->vm, Squirrel_PrintFunc);
/* Handle runtime-errors ourself, so we can display it nicely */
sq_newclosure(this->vm, &Squirrel_RunError, 0);
sq_seterrorhandler(this->vm);
/* TODO -- This is ugly! */
strcpy(filename, "ai/");
strcat(filename, script_dir);
strcat(filename, PATHSEP);
strcat(filename, "main.sq");
DEBUG(ai, 1, "[squirrel] Starting AI with script in '%s'", filename);
sq_pushroottable(this->vm);
this->RegisterClasses();
/* Load and run the script */
if (!SQ_SUCCEEDED(sqstd_dofile(this->vm, filename, SQFalse, SQTrue))) {
DEBUG(ai, 0, "[squirrel] Failed to compile '%s'", filename);
return;
}
}
Squirrel::~Squirrel()
{
/* Clean up the stuff */
sq_pop(this->vm, 1);
sq_close(this->vm);
}
/* virtual */ void Squirrel::GameLoop()
{
/* Store the current top */
int top = sq_gettop(this->vm);
/* Go back to root-table */
sq_pushroottable(this->vm);
/* Find the function-name inside the script */
sq_pushstring(this->vm, "GameLoop", -1);
if (!SQ_SUCCEEDED(sq_get(this->vm, -2))) {
DEBUG(ai, 0, "[squirrel] Failed to run GameLoop");
sq_settop(this->vm, top);
return;
}
/* Call the function */
sq_pushroottable(this->vm);
sq_call(this->vm, 1, 0, 0);
/* Reset the top */
sq_settop(this->vm, top);
}