--Lua file for a Sprite's scripting function init(state) repeat state.vx = math.random(-4, 4) state.vy = math.random(-4, 4) until (state.vx*state.vx + state.vy*state.vy > 9) state.angle = math.random(0, 360) return state end function update (state) state.x = state.x + state.vx state.y = state.y + state.vy if (state.x < -50) then state.x = 850 end if (state.x > 850) then state.x = -50 end if (state.y < -50) then state.y = 650 end if (state.y > 650) then state.y = -50 end state.angle = math.mod(state.angle + 3, 360) return state end function react (state, collision) if (collision.code == 1) then state.dead = 1 state.x = -500 state.y = -500 end return state end //Scripting-related C++ code //Initialize a bunch of Lua stuff. void Sprite::setupLua(void) { L = lua_open(); luaopen_base(L); luaopen_table(L); luaopen_io(L); luaopen_string(L); luaopen_math(L); luaopen_debug(L); lua_pushcfunction(L, Sprite::spawnSprite); lua_setglobal(L, "spawnSprite"); lua_pushcfunction(L, Sprite::getSprite); lua_setglobal(L, "getSprite"); lua_pushcfunction(L, Sprite::call); lua_setglobal(L, "call"); lua_pushcfunction(L, Sprite::createText); lua_setglobal(L, "createText"); int error = luaL_loadfile(L, scriptFile.c_str()) || (lua_pcall(L, 0, 0, 0)); if (error) { fprintf(stderr, "%s\n", lua_tostring(L, -1)); lua_pop(L, 1); /* pop error message from the stack */ exit(111); } //Make certain the stack is clean at the start lua_settop(L, 0); lua_getglobal(L, "init"); pushState(); callLua(1, 1, "init"); popState("init"); } //Update the sprite. Calls an external Lua function. void Sprite::update(void) { lua_getglobal(L, "update"); pushState(); callLua(1, 1, "update"); popState("update"); } //React to a collision based on the provided collision data. void Sprite::react(collisionData data) { lua_getglobal(L, "react"); pushState(); //Push the collision data lua_newtable(L); setfield(L, "normx", data.surfaceNormal.x); setfield(L, "normy", data.surfaceNormal.y); setfield(L, "delta", data.delta); setfield(L, "offx", data.offset.x); setfield(L, "offy", data.offset.y); setfield(L, "newvx", data.vel.x); setfield(L, "newvy", data.vel.y); setfield(L, "altvx", data.altvel.x); setfield(L, "altvy", data.altvel.y); setfield(L, "code", data.code); callLua(2, 1, "react"); popState("react"); } //Assuming that a table has been pushed onto the stack, set a field of that //table. void Sprite::setfield(lua_State* L, const char* field, double value) { lua_pushstring(L, field); lua_pushnumber(L, value); lua_settable(L, -3); } //Get a field out of the table on the Lua stack. double Sprite::getfield(lua_State* L, const char* field) { double result; lua_pushstring(L, field); lua_gettable(L, -2); result = (double) lua_tonumber(L, -1); lua_pop(L, 1); return result; } //Push the current sprite state onto the Lua stack, as a table. void Sprite::pushState(void) { lua_newtable(L); setfield(L, "dead", 0); setfield(L, "angle", angle); setfield(L, "scale", scale); setfield(L, "vx", vel.x); setfield(L, "vy", vel.y); setfield(L, "x", boundingBox.getCenter().x); setfield(L, "y", boundingBox.getCenter().y); setfield(L, "anim", currentAnimation); setfield(L, "advanceFrames", 0); } //Pop the current sprite state off of the Lua stack. void Sprite::popState(const char* fun) { if (!lua_istable(L, -1)) { cerr << "Error: script " << scriptFile << ", function " << fun << " did not return a valid sprite state\n"; exit(169); } //Short-circuit: if the sprite is dying, then we don't care about the rest. dead = (bool) getfield(L, "dead"); if (dead) { world->killSprite(this); return; } vel.x = getfield(L, "vx"); vel.y = getfield(L, "vy"); //(x, y) is actually the location of the sprite's center, not its upper //corner. Coord newLoc; newLoc.x = getfield(L, "x"); newLoc.y = getfield(L, "y"); boundingBox.moveCenterTo(newLoc); angle = getfield(L, "angle"); scale = getfield(L, "scale"); currentAnimation = (int) getfield(L, "anim"); int anim = (int) getfield(L, "advanceFrames"); animations[currentAnimation]->advanceTime(anim); //Clean off the stack lua_settop(L, 0); } //Tell the world to spawn a new sprite based on information passed from the Lua //script. int Sprite::spawnSprite(lua_State* L) { int id = (int) luaL_checknumber(L, 1); int layer = (int) luaL_checknumber(L, 2); int uniqueId = (int) luaL_checknumber(L, 3); double x = (double) luaL_checknumber(L, 4); double y = (double) luaL_checknumber(L, 5); int newId = world->spawnSprite(id, layer, uniqueId, x, y); lua_pushnumber(L, newId); return 1; } //Return the information on a sprite with a given unique ID int Sprite::getSprite(lua_State* L) { int id = (int) luaL_checknumber(L, 1); Sprite* alt = world->getSprite(id); lua_newtable(L); Coord v = alt->getVel(); Sprite::setfield(L, "vx", v.x); Sprite::setfield(L, "vy", v.y); v = alt->getBox().getCenter(); Sprite::setfield(L, "x", v.x); Sprite::setfield(L, "y", v.y); return 1; } //Allow a sprite to call another sprite's Lua function int Sprite::call(lua_State* L) { string function = luaL_checkstring(L, 1); int id = (int) luaL_checknumber(L, 2); double arg = (double) luaL_checknumber(L, 3); Sprite* target = world->getSprite(id); target->invokeLuaFunction(function, arg); return 0; } //Display some text int Sprite::createText(lua_State* L) { string fontName = luaL_checkstring(L, 1); int x = (int) luaL_checknumber(L, 2); int y = (int) luaL_checknumber(L, 3); string content = luaL_checkstring(L, 4); world->createText(fontName, x, y, content); return 0; } //Invoke a specified Lua function on ourselves void Sprite::invokeLuaFunction(string function, double arg) { lua_getglobal(L, function.c_str()); pushState(); lua_pushnumber(L, arg); callLua(2, 1, function.c_str()); popState(function.c_str()); } //Call a Lua function and handle any error output. void Sprite::callLua(int args, int ret, const char* function) { if (lua_pcall(L, args, ret, 0)) { fprintf(stderr, "Error calling function \"%s\" in file %s:\n", function, scriptFile.c_str()); fprintf(stderr, "%s\n", lua_tostring(L, -1)); lua_pop(L, 1); exit(1); } }