After Scripting

Dec 31, 2008 00:15

Six weeks ago I said I wouldn't post again until I'd finished off the scripting engine. Did that: it's wrapped up, streaming and all. A fun sub-project complete with a compact command-line tool for running scripts and a nice robust runtime interface for linking the interpreter into other applications.

For the curious, the interpreter is downloadable here, and I've put the main runtime interface headers behind an lj-cut just to give you the flavor of the thing.

To be truthful, I finished all that over a week ago. Since then I've been working on the next step: a map editor for my next game, the game that will also use that scripting engine. I'm using the working title Praetor, so you'll probably catch me referring to that. I'll post more about that thing later, but the quick summary is that it's a collectible card game played out using armies deployed on a hexagonal playing board. Cells on the playing board have various terrains, and as you progress through the game you'll face opponents on different boards--so a map editor to lay out that terrain was essential. Here's a sample of what it lets you create:



The editor has a small amount left to do, but it's not a big project and is almost wrapped up already. Importantly, a lot of its code is content that will be shared with the game itself: one third is raw game-engine code, one third is common Banshee-based map-rendering code, and one third is specific to the editor itself. Should help me get the game itself running a little faster than if I had to rewrite all that from scratch.



namespace dualscript
{
class interpreter
{
friend class runtime;
friend class object;

public:
interpreter (linker &link);
~interpreter (void);

void setImportStream (importstream *imp);
void setExportStream (exportstream *exp);

void run (void);
void runtimeError (const char *psz, ...);

runtime * newVoid (const string &name);
runtime * newInt (const string &name, int val);
runtime * newFloat (const string &name, float val);
runtime * newString (const string &name, const string &text);
runtime * newObject (const string &name, runtime *classtype, runtime *args);
runtime * newArray (const string &name);
runtime * newNamespace (string name);
runtime * newClass (const string &name, runtime *nspace, runtime::classconstructor pfn);
runtime * newGlobalMethod (const string &name, runtime *nspace, runtime::globalmethod pfn);
runtime * newMemberMethod (const string &name, runtime *classtype, runtime::membermethod pfn);

runtime * find (const string &name);
};

class runtime
{
friend class interpreter;
friend class object;

typedef runtime* (*globalmethod)(runtime *args);
typedef runtime* (*membermethod)(runtime *obj, runtime *args);
typedef void (*classconstructor)(runtime *obj, runtime *args);

public:
~runtime (void);
interpreter & getInterpreter (void);

// Queries this thing's value as an int, float or string
int getInt();
float getFloat();
string getString();

// Changes this thing's value to be an int, float or string.
void setInt (int val);
void setFloat (float val);
void setString (const string &text);

// Manipulates this thing as an object. You can assign an
// arbitrary cookie to ride shotgun on an object--a good place
// to stash a native object if you like.
void* getCookie();
void setCookie (void *cookie);

// Manipulates this object if it's an array type. Pushing just
// adds a reference to the runtime's underlying value to the
// array, so you still have to delete your runtime object to
// free your own reference to it. Likewise, when you pop an
// element from the array, or even just use getIndex() or
// front() or back() just to peek, you are granted a new runtime
// object that you must delete eventually.
void push_front (runtime *val);
void push_back (runtime *val);
size_t size (void);
runtime * require (const string &name);
runtime * getIndex (size_t ii);
runtime * front (void);
runtime * back (void);
runtime * pop_front (void);
runtime * pop_back (void);

// Finds a child. If this is a namespace, can be used to find
// a variable, class, global method or child namespace; if this
// is an object, can be used to find a member variable or method.
// The path specified can be separated by "."'s.
runtime * find (string name);

// If this is a method, calls the method with the specified args
// (which should be NULL or an array type). The input args array
// is deleted for you. All methods return something; remember to
// delete the result when you're done with it.
runtime * call (runtime *args);

// Insert the specified variable as a global or member var of the
// current object or namespace. You can still delete your reference
// to this runtime afterwards; this process adds a new reference to
// the entity.
void insert (runtime *child);
};
};

Previous post Next post
Up