2011-09-20 19:12:24 +00:00
|
|
|
/* The Lunatic Interpreter, part of EDuke32 */
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
#include <lua.h>
|
|
|
|
#include <lualib.h>
|
|
|
|
#include <lauxlib.h>
|
|
|
|
|
|
|
|
#include "cache1d.h"
|
|
|
|
#include "osd.h"
|
|
|
|
|
2012-02-09 22:45:18 +00:00
|
|
|
#include "gameexec.h"
|
2012-06-10 18:56:10 +00:00
|
|
|
#include "gamedef.h" // EventNames[], MAXEVENTS
|
2011-09-20 19:12:24 +00:00
|
|
|
#include "lunatic.h"
|
|
|
|
|
2012-02-09 22:45:18 +00:00
|
|
|
// this serves two purposes:
|
|
|
|
// the values as booleans and the addresses as keys to the Lua registry
|
2012-06-10 18:56:10 +00:00
|
|
|
uint8_t g_elEvents[MAXEVENTS];
|
2012-02-09 22:45:18 +00:00
|
|
|
|
2012-06-10 18:56:15 +00:00
|
|
|
// same thing for actors:
|
|
|
|
uint8_t g_elActors[MAXTILES];
|
|
|
|
|
2012-07-13 18:20:35 +00:00
|
|
|
// Lua-registry key for debug.traceback
|
|
|
|
static uint8_t debug_traceback_key;
|
|
|
|
|
2012-02-09 22:45:18 +00:00
|
|
|
|
|
|
|
// forward-decls...
|
|
|
|
static int32_t SetEvent_luacf(lua_State *L);
|
2012-06-10 18:56:15 +00:00
|
|
|
static int32_t SetActor_luacf(lua_State *L);
|
2012-02-09 22:45:18 +00:00
|
|
|
|
2012-05-13 16:05:16 +00:00
|
|
|
// in lpeg.o
|
|
|
|
extern int luaopen_lpeg(lua_State *L);
|
2011-09-20 19:12:24 +00:00
|
|
|
|
2012-05-13 16:05:16 +00:00
|
|
|
|
2012-07-13 18:20:35 +00:00
|
|
|
static void check_and_register_function(lua_State *L, void *keyaddr)
|
|
|
|
{
|
|
|
|
luaL_checktype(L, -1, LUA_TFUNCTION);
|
|
|
|
|
|
|
|
lua_pushlightuserdata(L, keyaddr); // 3, push address
|
|
|
|
lua_pushvalue(L, -2); // 4, push copy of lua function
|
|
|
|
|
|
|
|
lua_settable(L, LUA_REGISTRYINDEX); // "registry[keyaddr] = <lua function>", pop 2
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-13 16:05:16 +00:00
|
|
|
// 0: success, <0: failure
|
2011-09-20 19:12:24 +00:00
|
|
|
int32_t El_CreateState(El_State *estate, const char *name)
|
|
|
|
{
|
2012-07-13 18:20:35 +00:00
|
|
|
lua_State *L;
|
|
|
|
|
2011-09-20 19:12:24 +00:00
|
|
|
estate->name = Bstrdup(name);
|
|
|
|
if (!estate->name)
|
|
|
|
return -1;
|
|
|
|
|
2012-07-13 18:20:35 +00:00
|
|
|
L = estate->L = luaL_newstate();
|
2011-09-20 19:12:24 +00:00
|
|
|
|
|
|
|
if (!estate->L)
|
|
|
|
{
|
2012-06-03 15:46:02 +00:00
|
|
|
Bfree(estate->name);
|
2011-09-20 19:12:24 +00:00
|
|
|
estate->name = NULL;
|
2011-10-11 16:53:15 +00:00
|
|
|
return -2;
|
2011-09-20 19:12:24 +00:00
|
|
|
}
|
|
|
|
|
2012-07-13 18:20:35 +00:00
|
|
|
luaL_openlibs(L); // NOTE: we set up the sandbox in defs.ilua
|
|
|
|
luaopen_lpeg(L);
|
|
|
|
lua_pop(L, lua_gettop(L)); // pop off whatever lpeg leaves on the stack
|
|
|
|
|
|
|
|
// get debug.traceback
|
|
|
|
lua_getglobal(L, "debug");
|
|
|
|
lua_getfield(L, -1, "traceback");
|
|
|
|
Bassert(lua_isfunction(L, -1));
|
|
|
|
check_and_register_function(L, &debug_traceback_key);
|
|
|
|
lua_pop(L, 2);
|
2011-09-20 19:12:24 +00:00
|
|
|
|
2012-02-09 22:45:18 +00:00
|
|
|
// create misc. global functions in the Lua state
|
2012-07-13 18:20:35 +00:00
|
|
|
lua_pushcfunction(L, SetEvent_luacf);
|
|
|
|
lua_setglobal(L, "gameevent");
|
|
|
|
lua_pushcfunction(L, SetActor_luacf);
|
|
|
|
lua_setglobal(L, "gameactor");
|
|
|
|
|
|
|
|
Bassert(lua_gettop(L)==0);
|
2012-02-09 22:45:18 +00:00
|
|
|
|
2011-09-20 19:12:24 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void El_DestroyState(El_State *estate)
|
|
|
|
{
|
|
|
|
if (!estate->L)
|
|
|
|
return;
|
|
|
|
|
2012-06-03 15:46:02 +00:00
|
|
|
Bfree(estate->name);
|
2011-09-20 19:12:24 +00:00
|
|
|
estate->name = NULL;
|
|
|
|
|
|
|
|
lua_close(estate->L);
|
|
|
|
estate->L = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -1: alloc failure
|
|
|
|
// 0: success
|
|
|
|
// 1: didn't find file
|
|
|
|
// 2: couldn't read whole file
|
|
|
|
// 3: syntax error in lua file
|
|
|
|
// 4: runtime error while executing lua file
|
|
|
|
int32_t El_RunOnce(El_State *estate, const char *fn)
|
|
|
|
{
|
|
|
|
int32_t fid, flen, i;
|
|
|
|
char *buf;
|
|
|
|
|
2012-06-10 18:56:15 +00:00
|
|
|
lua_State *const L = estate->L;
|
|
|
|
|
2011-09-20 19:12:24 +00:00
|
|
|
fid = kopen4load(fn, 0);
|
|
|
|
|
|
|
|
if (fid < 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
flen = kfilelength(fid);
|
|
|
|
if (flen == 0)
|
|
|
|
return 0; // empty script ...
|
|
|
|
|
|
|
|
buf = Bmalloc(flen+1);
|
|
|
|
if (!buf)
|
|
|
|
{
|
|
|
|
kclose(fid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = kread(fid, buf, flen);
|
|
|
|
kclose(fid);
|
|
|
|
|
|
|
|
if (i != flen)
|
|
|
|
{
|
|
|
|
Bfree(buf);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[flen] = 0;
|
|
|
|
|
|
|
|
// -- lua --
|
2012-07-13 18:20:35 +00:00
|
|
|
Bassert(lua_gettop(L)==0);
|
|
|
|
|
|
|
|
// get debug.traceback
|
|
|
|
lua_pushlightuserdata(L, &debug_traceback_key);
|
|
|
|
lua_gettable(L, LUA_REGISTRYINDEX);
|
|
|
|
Bassert(lua_isfunction(L, -1));
|
2011-09-20 19:12:24 +00:00
|
|
|
|
2012-06-10 18:56:15 +00:00
|
|
|
i = luaL_loadstring(L, buf);
|
2012-07-13 18:20:35 +00:00
|
|
|
Bassert(lua_gettop(L)==2);
|
2011-09-20 19:12:24 +00:00
|
|
|
Bfree(buf);
|
|
|
|
|
|
|
|
if (i == LUA_ERRMEM)
|
2012-07-13 18:20:35 +00:00
|
|
|
{
|
|
|
|
lua_pop(L, 2);
|
2011-09-20 19:12:24 +00:00
|
|
|
return -1;
|
2012-07-13 18:20:35 +00:00
|
|
|
}
|
2011-09-20 19:12:24 +00:00
|
|
|
|
|
|
|
if (i == LUA_ERRSYNTAX)
|
|
|
|
{
|
2012-06-10 18:56:15 +00:00
|
|
|
OSD_Printf("state \"%s\" syntax error: %s\n", estate->name,
|
|
|
|
lua_tostring(L, -1)); // get err msg
|
2012-07-13 18:20:35 +00:00
|
|
|
lua_pop(L, 2); // pop errmsg and debug.traceback
|
2011-09-20 19:12:24 +00:00
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -- call the lua chunk! --
|
|
|
|
|
2012-07-13 18:20:35 +00:00
|
|
|
i = lua_pcall(L, 0, 0, -2);
|
|
|
|
Bassert(lua_gettop(L) == 1+!!i);
|
|
|
|
Bassert(i != LUA_ERRERR); // we expect debug.traceback not to fail
|
|
|
|
|
2011-09-20 19:12:24 +00:00
|
|
|
if (i == LUA_ERRMEM) // XXX: should be more sophisticated. Clean up stack? Do GC?
|
2012-07-13 18:20:35 +00:00
|
|
|
{
|
|
|
|
lua_pop(L, 2);
|
2011-09-20 19:12:24 +00:00
|
|
|
return -1;
|
2012-07-13 18:20:35 +00:00
|
|
|
}
|
2011-09-20 19:12:24 +00:00
|
|
|
|
|
|
|
if (i == LUA_ERRRUN)
|
|
|
|
{
|
2012-06-10 18:56:15 +00:00
|
|
|
Bassert(lua_type(L, -1)==LUA_TSTRING);
|
|
|
|
OSD_Printf("state \"%s\" runtime error: %s\n", estate->name,
|
|
|
|
lua_tostring(L, -1)); // get err msg
|
2012-07-13 18:20:35 +00:00
|
|
|
lua_pop(L, 2); // pop errmsg and debug.traceback
|
2011-09-20 19:12:24 +00:00
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
|
2012-07-13 18:20:35 +00:00
|
|
|
lua_pop(L, 1);
|
2012-02-09 22:45:18 +00:00
|
|
|
|
2012-07-13 18:20:35 +00:00
|
|
|
return 0;
|
2012-06-10 18:56:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////// Lua_CFunctions //////////
|
|
|
|
|
|
|
|
// gameevent(EVENT_..., lua_function)
|
|
|
|
static int32_t SetEvent_luacf(lua_State *L)
|
|
|
|
{
|
2012-07-13 18:20:35 +00:00
|
|
|
int32_t eventidx;
|
|
|
|
|
|
|
|
if (lua_gettop(L) != 2)
|
|
|
|
luaL_error(L, "gameevent: must pass exactly two arguments");
|
|
|
|
|
|
|
|
eventidx = luaL_checkint(L, 1);
|
2012-06-10 18:56:15 +00:00
|
|
|
|
|
|
|
luaL_argcheck(L, (unsigned)eventidx < MAXEVENTS, 1, "must be an event number (0 .. MAXEVENTS-1)");
|
|
|
|
check_and_register_function(L, &g_elEvents[eventidx]);
|
2012-02-09 22:45:18 +00:00
|
|
|
g_elEvents[eventidx] = 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-10 18:56:15 +00:00
|
|
|
// gameactor(<actortile>, lua_function)
|
|
|
|
static int32_t SetActor_luacf(lua_State *L)
|
2012-02-09 22:45:18 +00:00
|
|
|
{
|
2012-07-13 18:20:35 +00:00
|
|
|
int32_t actortile;
|
|
|
|
|
|
|
|
if (lua_gettop(L) != 2)
|
|
|
|
luaL_error(L, "gameactor: must pass exactly two arguments");
|
|
|
|
|
|
|
|
actortile = luaL_checkint(L, 1);
|
2012-02-09 22:45:18 +00:00
|
|
|
|
2012-06-10 18:56:15 +00:00
|
|
|
luaL_argcheck(L, (unsigned)actortile < MAXTILES, 1, "must be an tile number (0 .. MAXTILES-1)");
|
|
|
|
check_and_register_function(L, &g_elActors[actortile]);
|
|
|
|
g_elActors[actortile] = 1;
|
2012-02-09 22:45:18 +00:00
|
|
|
|
2012-06-10 18:56:15 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
|
|
|
|
|
|
static int32_t call_registered_function3(lua_State *L, void *keyaddr,
|
|
|
|
int32_t iActor, int32_t iPlayer, int32_t lDist)
|
|
|
|
{
|
|
|
|
int32_t i;
|
2012-02-09 22:45:18 +00:00
|
|
|
|
2012-07-13 18:20:35 +00:00
|
|
|
// get the Lua function from the registry
|
|
|
|
lua_pushlightuserdata(L, keyaddr);
|
|
|
|
lua_gettable(L, LUA_REGISTRYINDEX);
|
2012-06-10 18:56:10 +00:00
|
|
|
|
|
|
|
lua_pushinteger(L, iActor);
|
|
|
|
lua_pushinteger(L, iPlayer);
|
|
|
|
lua_pushinteger(L, lDist);
|
2012-02-09 22:45:18 +00:00
|
|
|
|
|
|
|
// -- call it! --
|
|
|
|
|
2012-06-10 18:56:10 +00:00
|
|
|
i = lua_pcall(L, 3, 0, 0);
|
2012-06-10 18:56:15 +00:00
|
|
|
if (i == LUA_ERRMEM)
|
|
|
|
{
|
2012-07-13 18:20:35 +00:00
|
|
|
lua_pop(L, 1);
|
2012-06-10 18:56:15 +00:00
|
|
|
// XXX: should be more sophisticated. Clean up stack? Do GC?
|
|
|
|
}
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t El_CallEvent(El_State *estate, int32_t eventidx, int32_t iActor, int32_t iPlayer, int32_t lDist)
|
|
|
|
{
|
|
|
|
// XXX: estate must be the one where the events were registered...
|
|
|
|
// make a global?
|
|
|
|
|
|
|
|
lua_State *const L = estate->L;
|
|
|
|
|
|
|
|
int32_t i = call_registered_function3(L, &g_elEvents[eventidx], iActor, iPlayer, lDist);
|
2012-02-09 22:45:18 +00:00
|
|
|
|
|
|
|
if (i == LUA_ERRRUN)
|
|
|
|
{
|
2012-03-26 05:05:57 +00:00
|
|
|
OSD_Printf("event \"%s\" (state \"%s\") runtime error: %s\n", EventNames[eventidx].text,
|
2012-06-10 18:56:15 +00:00
|
|
|
estate->name, lua_tostring(L, -1)); // get err msg
|
|
|
|
lua_pop(L, 1);
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t El_CallActor(El_State *estate, int32_t actortile, int32_t iActor, int32_t iPlayer, int32_t lDist)
|
|
|
|
{
|
|
|
|
lua_State *const L = estate->L;
|
|
|
|
|
|
|
|
int32_t i = call_registered_function3(L, &g_elActors[actortile], iActor, iPlayer, lDist);
|
|
|
|
|
|
|
|
if (i == LUA_ERRRUN)
|
|
|
|
{
|
|
|
|
OSD_Printf("actor %d (sprite %d, state \"%s\") runtime error: %s\n", actortile, iActor,
|
|
|
|
estate->name, lua_tostring(L, -1)); // get err msg
|
2012-06-10 18:56:10 +00:00
|
|
|
lua_pop(L, 1);
|
2012-02-09 22:45:18 +00:00
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|