#include "quakedef.h"

#ifdef VM_LUA

#include "pr_common.h"
#include "hash.h"

#define LUA_MALLOC_TAG 0x55780128

#define luagloballist	\
	globalentity	(true, self)	\
	globalentity	(true, other)	\
	globalentity	(true, world)	\
	globalfloat		(true, time)	\
	globalfloat		(true, frametime)	\
	globalentity	(false, newmis)	\
	globalfloat		(false, force_retouch)	\
	globalstring	(true, mapname)	\
	globalfloat		(false, deathmatch)	\
	globalfloat		(false, coop)	\
	globalfloat		(false, teamplay)	\
	globalfloat		(true, serverflags)	\
	globalfloat		(false, dimension_send)	\
	globalfloat		(false, physics_mode)	\
	globalfloat		(true, total_secrets)	\
	globalfloat		(true, total_monsters)	\
	globalfloat		(true, found_secrets)	\
	globalfloat		(true, killed_monsters)	\
	globalvec		(true, v_forward)	\
	globalvec		(true, v_up)	\
	globalvec		(true, v_right)	\
	globalfloat		(true, trace_allsolid)	\
	globalfloat		(true, trace_startsolid)	\
	globalfloat		(true, trace_fraction)	\
	globalvec		(true, trace_endpos)	\
	globalvec		(true, trace_plane_normal)	\
	globalfloat		(true, trace_plane_dist)	\
	globalentity	(true, trace_ent)	\
	globalfloat		(true, trace_inopen)	\
	globalfloat		(true, trace_inwater)	\
	globalfloat		(false, trace_endcontentsf)	\
	globalint		(false, trace_endcontentsi)	\
	globalfloat		(false, trace_surfaceflagsf)	\
	globalint		(false, trace_surfaceflagsi)	\
	globalfloat		(false, cycle_wrapped)	\
	globalentity	(false, msg_entity)	\
	globalfunc		(false, main)	\
	globalfunc		(true, StartFrame)	\
	globalfunc		(true, PlayerPreThink)	\
	globalfunc		(true, PlayerPostThink)	\
	globalfunc		(true, ClientKill)	\
	globalfunc		(true, ClientConnect)	\
	globalfunc		(true, PutClientInServer)	\
	globalfunc		(true, ClientDisconnect)	\
	globalfunc		(false, SetNewParms)	\
	globalfunc		(false, SetChangeParms)	\
	globalfloat		(false, dimension_default)	\
	globalvec		(false, global_gravitydir)

//any globals or functions that the server might want access to need to be known also.
#define luaextragloballist	\
	globalstring	(true, startspot)	\
	globalfunc		(true, ClientReEnter) 

typedef struct
{
#define globalentity(required, name) int name;
#define globalint(required, name) int name;
#define globalfloat(required, name) float name;
#define globalstring(required, name) string_t name;
#define globalvec(required, name) vec3_t name;
#define globalfunc(required, name) int name;
luagloballist
luaextragloballist
#undef globalentity
#undef globalint
#undef globalfloat
#undef globalstring
#undef globalvec
#undef globalfunc

	float parm[NUM_SPAWN_PARMS];
} luaglobalvars_t;

typedef struct
{
	int type;
	qintptr_t offset;
	char *name;
	bucket_t buck;
} luafld_t;

//#define LIBLUA_STATIC

#ifdef LIBLUA_STATIC
	#ifdef _MSC_VER
		#pragma comment(lib, "liblua.lib")
	#endif
	#include <lua.h>
	//#include <lualib.h>
	#include <lauxlib.h>
#else
typedef struct lua_State lua_State;
typedef void *(QDECL *lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
typedef const char *(QDECL *lua_Reader)(lua_State *L, void *data, size_t *size);
typedef int (QDECL *lua_CFunction) (lua_State *L);
typedef double lua_Number;
typedef long long lua_Integer;


#define lua_pcall(L,n,r,f)	lua_pcallk(L, (n), (r), (f), 0, NULL)
#define lua_call(L,n,r)		lua_callk(L, (n), (r), 0, NULL)
#define lua_pop(L,n)		lua_settop(L, -(n)-1)
#define lua_pushstring(L,s)	lua_pushfstring(L,"%s",s)

//#define lua_remove(L,idx)	(lua_rotate(L, (idx), -1), lua_pop(L, 1))
#define lua_replace(L,idx)	(lua_copy(L, -1, (idx)), lua_pop(L, 1))
#define lua_pushglobaltable(L)  (lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS))
#define luaL_getmetatable(L,n)	(lua_getfield(L, LUA_REGISTRYINDEX, (n)))

#define LUA_TNONE			(-1)
#define LUA_TNIL			0
#define LUA_TBOOLEAN		1
#define LUA_TLIGHTUSERDATA	2
#define LUA_TNUMBER			3
#define LUA_TSTRING			4
#define LUA_TTABLE			5
#define LUA_TFUNCTION		6
#define LUA_TUSERDATA		7
#define LUA_TTHREAD			8
#define LUA_NUMTAGS			9

#define LUA_RIDX_GLOBALS	2

#define LUA_REGISTRYINDEX (-1000000 - 1000)

#define lua_newstate		lua.newstate
#define lua_atpanic			lua.atpanic
#define lua_close			lua.close
#define lua_load			lua.load
#define lua_pcallk			lua.pcallk
#define lua_callk			lua.callk
#define lua_getfield		lua.getfield
#define lua_setfield		lua.setfield
#define lua_gettable		lua.gettable
#define lua_settable		lua.settable
#define lua_getglobal		lua.getglobal
#define lua_setglobal		lua.setglobal	
#define lua_error			lua.error
#define lua_type			lua.type
#define lua_typename		lua.typename
#define lua_rawget			lua.rawget
#define lua_rawgeti			lua.rawgeti
#define lua_rawgetp			lua.rawgetp
#define lua_rawset			lua.rawset
#define lua_createtable		lua.createtable
#define lua_setmetatable	lua.setmetatable
#define lua_newuserdata		lua.newuserdata

#define lua_copy			lua.copy
#define lua_gettop			lua.gettop
#define lua_settop			lua.settop
#define lua_pushboolean		lua.pushboolean
#define lua_pushnil			lua.pushnil
#define lua_pushnumber		lua.pushnumber
#define lua_pushinteger		lua.pushinteger
#define lua_pushvalue		lua.pushvalue
#define lua_pushcclosure	lua.pushcclosure
#define lua_pushfstring		lua.pushfstring
#define lua_pushliteral		lua_pushstring
#define lua_pushlightuserdata	lua.pushlightuserdata
#define lua_tolstring		lua.tolstring
#define lua_toboolean		lua.toboolean
#define lua_tonumberx		lua.tonumberx
#define lua_tointegerx		lua.tointegerx
#define lua_topointer		lua.topointer
#define lua_touserdata		lua.touserdata
#define lua_touserdata		lua.touserdata
#define lua_next			lua.next
//#define lua_remove			lua.remove

#define luaL_callmeta		lua.Lcallmeta
#define luaL_newmetatable	lua.Lnewmetatable

#define lua_newtable(L)		lua_createtable(L,0,0)
#define lua_pushcfunction(L,f) lua_pushcclosure(L,f,0)
#define lua_isnil(L,i)		(lua_type(L,i)==LUA_TNIL)
#define lua_tostring(L,i)	lua_tolstring(L,i,NULL)
#define lua_tonumber(L,i)	lua_tonumberx(L,i,NULL)
#define lua_tointeger(L,i)	lua_tointegerx(L,i,NULL)
#endif

//I'm using this struct for all the global stuff.
static struct
{
	lua_State *ctx;
	char readbuf[1024];
	pubprogfuncs_t progfuncs;
	progexterns_t progfuncsparms;
	edict_t **edicttable;
	unsigned int maxedicts;

	luaglobalvars_t globals;	//internal global structure
	hashtable_t globalfields;	//name->luafld_t
	luafld_t globflds[1024];	//fld->offset+type

	hashtable_t entityfields;	//name->luafld_t
	luafld_t entflds[1024];		//fld->offset+type
	size_t numflds;

#ifndef LIBLUA_STATIC
	qboolean triedlib;
	dllhandle_t *lib;

	lua_State *		(QDECL *newstate)		(lua_Alloc f, void *ud);
	lua_CFunction	(QDECL *atpanic)		(lua_State *L, lua_CFunction panicf);
	void			(QDECL *close)			(lua_State *L);
	int				(QDECL *load)			(lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode);
	int				(QDECL *pcallk)			(lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k);
	void			(QDECL *callk)			(lua_State *L, int nargs, int nresults,				 int ctx, lua_CFunction k);
	void			(QDECL *getfield)		(lua_State *L, int idx, const char *k);
	void			(QDECL *setfield)		(lua_State *L, int idx, const char *k);
	void			(QDECL *gettable)		(lua_State *L, int idx);
	void			(QDECL *settable)		(lua_State *L, int idx);
	void			(QDECL *getglobal)		(lua_State *L, const char *var);
	void			(QDECL *setglobal)		(lua_State *L, const char *var);
	int				(QDECL *error)			(lua_State *L);
	int				(QDECL *type)			(lua_State *L, int idx);
	const char     *(QDECL *typename)		(lua_State *L, int tp);
	int				(QDECL *rawget)			(lua_State *L, int idx);
	int				(QDECL *rawgeti)		(lua_State *L, int idx, lua_Integer n);
	int				(QDECL *rawgetp)		(lua_State *L, int idx, const void *p);
	void			(QDECL *rawset)			(lua_State *L, int idx);
	void			(QDECL *createtable)	(lua_State *L, int narr, int nrec);
	int				(QDECL *setmetatable)	(lua_State *L, int objindex);
	void		   *(QDECL *newuserdata)	(lua_State *L, size_t usize);

	void			(QDECL *copy)			(lua_State *L, int fromidx, int toidx);	//added in 5.3
//	void			(QDECL *replace)		(lua_State *L, int idx);				//removed in 5.3
	int				(QDECL *gettop)			(lua_State *L);
	int				(QDECL *settop)			(lua_State *L, int idx);
	void			(QDECL *pushboolean)	(lua_State *L, int b);
	void			(QDECL *pushnil)		(lua_State *L);
	void			(QDECL *pushnumber)		(lua_State *L, lua_Number n);
	void			(QDECL *pushinteger)	(lua_State *L, lua_Integer n);
	void			(QDECL *pushvalue)		(lua_State *L, int idx);
	void			(QDECL *pushcclosure)	(lua_State *L, lua_CFunction fn, int n);
	const char *	(QDECL *pushfstring)	(lua_State *L, const char *fmt, ...);
	void			(QDECL *pushlightuserdata)	(lua_State *L, void *p);
	const char *	(QDECL *tolstring)		(lua_State *L, int idx, size_t *len);
	int             (QDECL *toboolean)		(lua_State *L, int idx);
	lua_Number      (QDECL *tonumberx)		(lua_State *L, int idx, int *isnum);
	lua_Integer     (QDECL *tointegerx)		(lua_State *L, int idx, int *isnum);
	const void     *(QDECL *topointer)		(lua_State *L, int idx);
	void		   *(QDECL *touserdata)		(lua_State *L, int idx);
	int				(QDECL *next)			(lua_State *L, int idx);
//	void			(QDECL *remove)			(lua_State *L, int idx);

	int				(QDECL *Lcallmeta)		(lua_State *L, int obj, const char *e);
	int				(QDECL *Lnewmetatable)	(lua_State *L, const char *tname);
#endif
} lua;

static qboolean init_lua(void)
{
#ifndef LIBLUA_STATIC
	if (!lua.triedlib)
	{
		dllfunction_t luafuncs[] =
		{
			{(void*)&lua.newstate,		"lua_newstate"},
			{(void*)&lua.atpanic,		"lua_atpanic"},
			{(void*)&lua.close,			"lua_close"},
			{(void*)&lua.load,			"lua_load"},
			{(void*)&lua.pcallk,		"lua_pcallk"},
			{(void*)&lua.callk,			"lua_callk"},
			{(void*)&lua.getfield,		"lua_getfield"},
			{(void*)&lua.setfield,		"lua_setfield"},
			{(void*)&lua.gettable,		"lua_gettable"},
			{(void*)&lua.settable,		"lua_settable"},
			{(void*)&lua.getglobal,		"lua_getglobal"},
			{(void*)&lua.setglobal,		"lua_setglobal"},
			{(void*)&lua.error,			"lua_error"},
			{(void*)&lua.type,			"lua_type"},
			{(void*)&lua.typename,		"lua_typename"},
			{(void*)&lua.rawget,		"lua_rawget"},
			{(void*)&lua.rawgeti,		"lua_rawgeti"},
			{(void*)&lua.rawgetp,		"lua_rawgetp"},
			{(void*)&lua.rawset,		"lua_rawset"},
			{(void*)&lua.createtable,	"lua_createtable"},
			{(void*)&lua.setmetatable,	"lua_setmetatable"},
			{(void*)&lua.newuserdata,	"lua_newuserdata"},
	
			{(void*)&lua.copy,			"lua_copy"},
//			{(void*)&lua.replace,		"lua_replace"},
			{(void*)&lua.gettop,		"lua_gettop"},
			{(void*)&lua.settop,		"lua_settop"},
			{(void*)&lua.pushboolean,	"lua_pushboolean"},
			{(void*)&lua.pushnil,		"lua_pushnil"},
			{(void*)&lua.pushnumber,	"lua_pushnumber"},
			{(void*)&lua.pushinteger,	"lua_pushinteger"},
			{(void*)&lua.pushvalue,		"lua_pushvalue"},
			{(void*)&lua.pushcclosure,	"lua_pushcclosure"},
			{(void*)&lua.pushfstring,	"lua_pushfstring"},
			{(void*)&lua.pushlightuserdata,	"lua_pushlightuserdata"},
			{(void*)&lua.tolstring,		"lua_tolstring"},
			{(void*)&lua.toboolean,		"lua_toboolean"},
			{(void*)&lua.tonumberx,		"lua_tonumberx"},
			{(void*)&lua.tointegerx,	"lua_tointegerx"},
			{(void*)&lua.topointer,		"lua_topointer"},
			{(void*)&lua.touserdata,	"lua_touserdata"},
			{(void*)&lua.next,			"lua_next"},
//			{(void*)&lua.remove,		"lua_remove"},

			{(void*)&lua.Lcallmeta,		"luaL_callmeta"},
			{(void*)&lua.Lnewmetatable,	"luaL_newmetatable"},

			{NULL, NULL}
		};
		lua.triedlib = true;
		lua.lib = Sys_LoadLibrary("lua53", luafuncs);
	}
	if (!lua.lib)
		return false;
#endif
	return true;
}
char *QDECL Lua_AddString(pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup);

static void lua_pushedict(lua_State *L, struct edict_s *ent)
{
	lua_rawgetp(L, LUA_REGISTRYINDEX, ent);
}

/*
static void lua_debugstack(lua_State *L)
{
	int idx;
	int top = lua_gettop(L);
	for (idx = 1; idx <= top; idx++)
	{
		if (luaL_callmeta(L, idx, "__tostring"))
		{
			Con_Printf("Stack%i: %s\n", idx, lua_tolstring(L, -1, NULL));
			lua_pop(L, 1);
		}
		else
		{
			int t = lua_type(L, idx);
			switch (t)
			{
			case LUA_TNUMBER:
				Con_Printf("Stack%i: %g\n", idx, lua_tonumber(L, idx));
				break;
			case LUA_TSTRING:
				Con_Printf("Stack%i: %s\n", idx, lua_tolstring(L, idx, NULL));
				break;
			case LUA_TBOOLEAN:
				Con_Printf("Stack%i: %s\n", idx, (lua_toboolean(L, idx) ? "true" : "false"));
				break;
			case LUA_TNIL:
				Con_Printf("Stack%i: %s\n", idx, "nil");
				break;
			case LUA_TTABLE:
				//special check for things that look like vectors.
				lua_getfield(L, idx, "x");
				lua_getfield(L, idx, "y");
				lua_getfield(L, idx, "z");
				if (lua_type(L, -3) == LUA_TNUMBER && lua_type(L, -2) == LUA_TNUMBER && lua_type(L, -1) == LUA_TNUMBER)
				{
					Con_Printf("Stack%i: '%f %f %f'\n", idx, lua_tonumberx(L, -3, NULL), lua_tonumberx(L, -2, NULL), lua_tonumberx(L, -1, NULL));
					lua_pop(L, 3);
					break;
				}
				lua_pop(L, 3);
			default:
				Con_Printf("Stack%i: %s: %p\n", idx, lua_typename(L, lua_type(L, idx)), lua_topointer(L, idx));
				break;
			}
		}
	}
}
*/

static void *my_lua_alloc (void *ud, void *ptr, size_t osize, size_t nsize)
{
	if (nsize == 0)
	{
		free(ptr);
		return NULL;
	}
	else
		return realloc(ptr, nsize);
}
const char * my_lua_Reader(lua_State *L, void *data, size_t *size)
{
	vfsfile_t *f = data;
	*size = VFS_READ(f, lua.readbuf, sizeof(lua.readbuf));
	return lua.readbuf;
}

//replace lua's standard 'print' function to use the console instead of stdout. intended to use the same linebreak rules.
static int bi_lua_print(lua_State *L)
{
	//the problem is that we can only really accept strings here.
	//so lets just use the tostring function to make sure things are actually readable as strings.
	int args = lua_gettop(L);
	int i;
	const char *s;
	lua_getglobal(L, "tostring");
	//args now start at 1
	for(i = 1; i <= args; i++)
	{
		lua_pushvalue(L, -1);
		lua_pushvalue(L, i);
		lua_pcall(L, 1, 1, 0);	//pops args+func
		s = lua_tolstring(L, -1, NULL);
		if(s == NULL)
			s = "?";

		if(i > 1) Con_Printf("\t");
		Con_Printf("%s", s);
		lua_pop(L, 1);			//pop our lstring
	};
	lua_pop(L, 1);			//pop the cached tostring.
	Con_Printf("\n");
	return 0;
};
//more like quakec's print
static int bi_lua_conprint(lua_State *L)
{
	//the problem is that we can only really accept strings here.
	//so lets just use the tostring function to make sure things are actually readable as strings.
	int args = lua_gettop(L);
	int i;
	const char *s;

	lua_getglobal(L, "tostring");
	//args start at stack index 1
	for(i = 1; i <= args; i++)
	{
		lua_pushvalue(L, -1);	//dupe the tostring
		lua_pushvalue(L, i);	//dupe the argument
		lua_pcall(L, 1, 1, 0);	//pops args+func, pushes the string result
		s = lua_tolstring(L, -1, NULL);
		if(s == NULL)
			s = "?";

		Con_Printf("%s", s);
		lua_pop(L, 1);			//pop our lstring
	};
	lua_pop(L, 1);			//pop the cached tostring.
	return 0;
};
static int bi_lua_dprint(lua_State *L)
{
	if (!developer.ival)
		return 0;
	return bi_lua_conprint(L);
}

//taken from lua's baselib.c, with dependancies reduced a little.
static int bi_lua_tostring(lua_State *L)
{
//	if (lua_type(L, 1) == LUA_TNONE)
//		luaL_argerror(L, narg, "value expected");
	if (luaL_callmeta(L, 1, "__tostring"))
		return 1;
	switch (lua_type(L, 1))
	{
	case LUA_TNUMBER:
		lua_pushfstring(L, lua_tolstring(L, 1, NULL));
		break;
	case LUA_TSTRING:
		lua_pushvalue(L, 1);
		break;
	case LUA_TBOOLEAN:
		lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false"));
		break;
	case LUA_TNIL:
		lua_pushstring(L, "nil");
		break;
	case LUA_TTABLE:
		//special check for things that look like vectors.
		lua_getfield(L, 1, "x");
		lua_getfield(L, 1, "y");
		lua_getfield(L, 1, "z");
		if (lua_type(L, -3) == LUA_TNUMBER && lua_type(L, -2) == LUA_TNUMBER && lua_type(L, -1) == LUA_TNUMBER)
		{
			lua_pushfstring(L, "'%g %g %g'", lua_tonumberx(L, -3, NULL), lua_tonumberx(L, -2, NULL), lua_tonumberx(L, -1, NULL));
			return 1;
		}
		//fallthrough
	default:
		lua_pushfstring(L, "%s: %p", lua_typename(L, lua_type(L, 1)), lua_topointer(L, 1));
		break;
	}
	return 1;
}
#define bi_lua_vtos bi_lua_tostring
#define bi_lua_ftos bi_lua_tostring

//taken from lua's baselib.c, with dependancies reduced a little.
static int bi_lua_tonumber(lua_State *L)
{
//	if (lua_type(L, 1) == LUA_TNONE)
//		luaL_argerror(L, narg, "value expected");
	if (luaL_callmeta(L, 1, "__tonumber"))
		return 1;
	switch (lua_type(L, 1))
	{
	case LUA_TSTRING:
		lua_pushnumber(L, atof(lua_tostring(L, 1)));
		break;
	case LUA_TNUMBER:
		lua_pushvalue(L, 1);
		break;
	case LUA_TBOOLEAN:
		lua_pushnumber(L, lua_toboolean(L, 1));
		break;
	case LUA_TNIL:
		lua_pushnumber(L, 0);
		break;
	case LUA_TTABLE:
		//special check for things that look like vectors.
		lua_getfield(L, 1, "x");
		lua_getfield(L, 1, "y");
		lua_getfield(L, 1, "z");
		if (lua_type(L, -3) == LUA_TNUMBER && lua_type(L, -2) == LUA_TNUMBER && lua_type(L, -1) == LUA_TNUMBER)
		{
			vec3_t v = {lua_tonumberx(L, -3, NULL), lua_tonumberx(L, -2, NULL), lua_tonumberx(L, -1, NULL)};
			lua_pushnumber(L, DotProduct(v,v));
			return 1;
		}
		lua_getfield(L, 1, "entnum");
		if (lua_type(L, -1) == LUA_TNUMBER)
			return 1;
		//fallthrough
	default:
		lua_pushfstring(L, "%s: %p", lua_typename(L, lua_type(L, 1)), lua_topointer(L, 1));
		break;
	}
	return 1;
}
#define bi_lua_stof bi_lua_tonumber

static int my_lua_panic(lua_State *L)
{
	const char *s = lua_tolstring(L, -1, NULL);
	Sys_Error("lua error: %s", s);
}

static int my_lua_entity_eq(lua_State *L)
{
	//table1=1
	//table2=2
	unsigned int entnum1, entnum2;
	lua_getfield(L, 1, "entnum");
	entnum1 = lua_tointegerx(L, -1, NULL);
	lua_getfield(L, 2, "entnum");
	entnum2 = lua_tointegerx(L, -1, NULL);
	lua_pop(L, 2);

	lua_pushboolean(L, entnum1 == entnum2);
	return 1;
}

static int my_lua_entity_tostring(lua_State *L)
{
	//table=1
	unsigned int entnum;
	lua_getfield(L, 1, "entnum");
	entnum = lua_tointegerx(L, -1, NULL);
	lua_pop(L, 1);

	lua_pushstring(L, va("entity: %u", entnum));
	return 1;
}

static int my_lua_vec3_tostring(lua_State *L)
{
	//table=1
	lua_Number x,y,z;
	lua_getfield(L, 1, "x");
	x = lua_tonumber(L, -1);
	lua_getfield(L, 1, "y");
	y = lua_tonumber(L, -1);
	lua_getfield(L, 1, "z");
	z = lua_tonumber(L, -1);
	lua_pop(L, 3);

	lua_pushstring(L, va("'%g %g %g'", x, y, z));
	return 1;
}

static qboolean lua_readvector(lua_State *L, int idx, float *result)
{
	switch(lua_type(L, idx))
	{
	case LUA_TSTRING:
		{
			//we parse strings primnarily for easy .ent(or bsp) loading support.
			const char *str = lua_tolstring(L, idx, NULL);
			str = COM_Parse(str);
			result[0] = atof(com_token);
			str = COM_Parse(str);
			result[1] = atof(com_token);
			str = COM_Parse(str);
			result[2] = atof(com_token);
		}
		return true;
	case LUA_TTABLE:
		lua_getfield(L, idx, "x");
		result[0] = lua_tonumberx(L, -1, NULL);
		lua_getfield(L, idx, "y");
		result[1] = lua_tonumberx(L, -1, NULL);
		lua_getfield(L, idx, "z");
		result[2] = lua_tonumberx(L, -1, NULL);
		lua_pop(L, 3);
		return true;
	case LUA_TNUMBER:
		result[0] = 
		result[1] = 
		result[2] = lua_tonumber(L, idx);
		break;
	case LUA_TNIL:
		result[0] = 0;
		result[1] = 0;
		result[2] = 0;
		break;
	default:
		Con_Printf("Expected vector, got %s\n", lua_typename(L, lua_type(L, idx)));
		result[0] = 0;
		result[1] = 0;
		result[2] = 0;
		break;
	}
	return false;
}
static void lua_pushvector(lua_State *L, vec_t x, vec_t y, vec_t z)
{
	lua_newtable(L);
	//FIXME: should provide a metatable with a __tostring
	lua_pushnumber(L, x);
	lua_setfield (L, -2, "x");
	lua_pushnumber(L, y);
	lua_setfield (L, -2, "y");
	lua_pushnumber(L, z);
	lua_setfield (L, -2, "z");

	luaL_getmetatable(L, "vec3_t");
	lua_setmetatable(L, -2); 
}

static int my_lua_vec3_eq(lua_State *L)
{
	vec3_t a, b;
	lua_readvector(L, 1, a);
	lua_readvector(L, 2, b);
	lua_pushboolean(L, a[0]==b[0] && a[1]==b[1] && a[2]==b[2]);
	return 1;
}
static int my_lua_vec3_sub(lua_State *L)
{
	vec3_t a, b;
	lua_readvector(L, 1, a);
	lua_readvector(L, 2, b);
	lua_pushvector(L, a[0]-b[0], a[1]-b[1], a[2]-b[2]);
	return 1;
}
static int my_lua_vec3_mul(lua_State *L)
{
	vec3_t a, b;
	if (lua_readvector(L, 1, a) + lua_readvector(L, 2, b) == 2)
		lua_pushnumber(L, DotProduct(a,b));	//vec*vec is a dotproduct
	else
		lua_pushvector(L, a[0]*b[0], a[1]*b[1], a[2]*b[2]);	//one arg is a scaler. woot.
	return 1;
}
static int my_lua_vec3_add(lua_State *L)
{
	vec3_t a, b;
	lua_readvector(L, 1, a);
	lua_readvector(L, 2, b);
	lua_pushvector(L, a[0]+b[0], a[1]+b[1], a[2]+b[2]);
	return 1;
}
static int my_lua_vec3_len(lua_State *L)
{
	vec3_t a;
	lua_readvector(L, 1, a);
	lua_pushnumber(L, VectorLength(a));
	return 1;
}

static int my_lua_entity_set(lua_State *L)	//__newindex
{
//	Con_Printf("lua_entity_set: ");
//	my_lua_print(L);
	//table=1
	//key=2
	//value=3

	if (lua_type(L, 2) == LUA_TSTRING)
	{
		const char *s = lua_tolstring(L, 2, NULL);
		luafld_t *fld = Hash_GetInsensitive(&lua.entityfields, s);
		eval_t *eval;
		unsigned int entnum;
		if (fld && fld->offset >= 0)
		{
			lua_getfield(L, 1, "entnum");
			entnum = lua_tointegerx(L, -1, NULL);
			lua_pop(L, 1);
			if (entnum < lua.maxedicts && lua.edicttable[entnum] && !ED_ISFREE(lua.edicttable[entnum]))
			{
				eval = (eval_t*)((char*)lua.edicttable[entnum]->v + fld->offset);
				switch(fld->type)
				{
				case ev_float:
					eval->_float = lua_tonumberx(L, 3, NULL);
					return 0;
				case ev_vector:
					lua_readvector(L, 3, eval->_vector);
					return 0;
				case ev_integer:
					eval->_int = lua_tointegerx(L, 3, NULL);
					return 0;
				case ev_function:
					if (lua_type(L, 3) == LUA_TNIL)
						eval->function = 0;	//so the engine can distinguish between nil and not.
					else
						eval->function = fld->offset | ((entnum+1)<<10);
					lua_pushlightuserdata(L, (void *)(qintptr_t)fld->offset);	//execute only knows a function id, so we need to translate the store to match.
					lua_replace(L, 2);
					lua_rawset(L, 1);
					return 0;
				case ev_string:
					if (lua_type(L, 3) == LUA_TNIL)
						eval->string = 0;	//so the engine can distinguish between nil and not.
					else
						eval->string = fld->offset | ((entnum+1)<<10);
					lua_pushlightuserdata(L, (void *)(qintptr_t)fld->offset);	//execute only knows a string id, so we need to translate the store to match.
					lua_replace(L, 2);
					lua_rawset(L, 1);
					return 0;
				case ev_entity:
					//read the table's entnum field so we know which one its meant to be.
					lua_getfield(L, 3, "entnum");
					eval->edict = lua_tointegerx(L, -1, NULL);
					return 0;
				}
			}
		}
	}

	lua_rawset(L, 1);
	return 0;
}
static int my_lua_entity_get(lua_State *L)	//__index
{
//	Con_Printf("lua_entity_get: ");
//	my_lua_print(L);
	//table=1
	//key=2

	if (lua_type(L, 2) == LUA_TSTRING)
	{
		const char *s = lua_tolstring(L, 2, NULL);
		luafld_t *fld = Hash_GetInsensitive(&lua.entityfields, s);
		eval_t *eval;
		int entnum;
		if (fld)
		{
			if (fld->offset < 0)
			{	//negative offsets are not real fields, but are just there to ensure that no nils appear
				lua_rawget(L, 1);
				if (lua_isnil(L, -1))
				{
					lua_pop(L, 1);
					switch(fld->type)
					{
					case ev_float:
						lua_pushnumber(L, 0);
						return 1;
					case ev_integer:
						lua_pushinteger(L, 0);
						return 1;
					case ev_vector:
						lua_pushvector(L, 0, 0, 0);
						break;
					case ev_function:
					default:	//no idea
						lua_pushnil(L);
						break;
					case ev_string:
						lua_pushliteral(L, "");
						break;
					case ev_entity:
						lua_pushedict(L, lua.edicttable[0]);
						break;
					}
				}
				return 1;
			}
			lua_getfield(L, 1, "entnum");
			entnum = lua_tointegerx(L, -1, NULL);
			lua_pop(L, 1);
			if (entnum < lua.maxedicts && lua.edicttable[entnum])// && !lua.edicttable[entnum]->isfree)
			{
				eval = (eval_t*)((char*)lua.edicttable[entnum]->v + fld->offset);
				switch(fld->type)
				{
				case ev_float:
					lua_pushnumber(L, eval->_float);
					return 1;
				case ev_integer:
					lua_pushinteger(L, eval->_int);
					return 1;
				case ev_vector:
					lua_pushvector(L, eval->_vector[0], eval->_vector[1], eval->_vector[2]);
					return 1;
				case ev_function:
				case ev_string:
					lua_pushlightuserdata(L, (void *)(qintptr_t)(eval->function & 1023));	//execute only knows a function id, so we need to translate the store to match.
					lua_replace(L, 2);
					lua_rawget(L, 1);
					return 1;
				case ev_entity:
					//return the table for the entity via the lua registry.
					lua_pushlightuserdata(lua.ctx, lua.edicttable[eval->edict]);
					lua_gettable(lua.ctx, LUA_REGISTRYINDEX);
					return 1;
				}
			}
		}
	}

	//make sure it exists so we don't get called constantly if code loops through stuff that wasn't set.
//	lua_pushstring(L, "nil");
	lua_rawget(L, 1);
	return 1;
}
static int my_lua_global_set(lua_State *L)	//__newindex
{
//	Con_Printf("my_lua_global_set: ");
//	my_lua_print(L);
	//table=1
	//key=2
	//value=3

	if (lua_type(L, 2) == LUA_TSTRING)
	{
		const char *s = lua_tolstring(L, 2, NULL);
		luafld_t *fld = Hash_GetInsensitive(&lua.globalfields, s);
		eval_t *eval;
		if (fld)
		{
			eval = (eval_t*)((char*)&lua.globals + fld->offset);
			switch(fld->type)
			{
			case ev_float:
				eval->_float = lua_tonumberx(L, 3, NULL);
				return 0;
			case ev_vector:
				lua_getfield(L, 3, "x");
				eval->_vector[0] = lua_tonumberx(L, -1, NULL);
				lua_getfield(L, 3, "y");
				eval->_vector[1] = lua_tonumberx(L, -1, NULL);
				lua_getfield(L, 3, "z");
				eval->_vector[2] = lua_tonumberx(L, -1, NULL);
				return 0;
			case ev_integer:
				eval->_int = lua_tointegerx(L, 3, NULL);
				return 0;
			case ev_function:
				if (lua_type(L, 3) == LUA_TNIL)
					eval->function = 0;	//so the engine can distinguish between nil and not.
				else
					eval->function = fld->offset;
				lua_pushlightuserdata(L, (void *)fld->offset);	//execute only knows a function id, so we need to translate the store to match.
				lua_replace(L, 2);
				lua_rawset(L, 1);
				return 0;
			case ev_string:
				if (lua_type(L, 3) == LUA_TNIL)
					eval->string = 0;	//so the engine can distinguish between nil and not.
				else
					eval->string = fld->offset;
				lua_pushlightuserdata(L, (void *)fld->offset);	//execute only knows a string id, so we need to translate the store to match.
				lua_replace(L, 2);
				lua_rawset(L, 1);
				return 0;
			case ev_entity:
				//read the table's entnum field so we know which one its meant to be.
				lua_getfield(L, 3, "entnum");
				eval->edict = lua_tointegerx(L, -1, NULL);
				return 0;
			}
		}
	}

	lua_rawset(L, 1);
	return 0;
}
static int my_lua_global_get(lua_State *L)	//__index
{
//	Con_Printf("my_lua_global_get: ");
//	my_lua_print(L);
	//table=1
	//key=2

	if (lua_type(L, 2) == LUA_TSTRING)
	{
		const char *s = lua_tolstring(L, 2, NULL);
		luafld_t *fld = Hash_GetInsensitive(&lua.globalfields, s);
		eval_t *eval;
		if (fld)
		{
			eval = (eval_t*)((char*)&lua.globals + fld->offset);
			switch(fld->type)
			{
			case ev_float:
				lua_pushnumber(L, eval->_float);
				return 1;
			case ev_integer:
				lua_pushinteger(L, eval->_int);
				return 1;
			case ev_vector:
				lua_pushvector(L, eval->_vector[0], eval->_vector[1], eval->_vector[2]);
				return 1;
			case ev_function:
				lua_pushlightuserdata(L, (void *)(qintptr_t)eval->function);	//execute only knows a function id, so we need to translate the store to match.
				lua_replace(L, 2);
				lua_rawget(L, 1);
				return 1;
			case ev_entity:
				lua_pushedict(L, lua.edicttable[eval->edict]);
				return 1;
			}
		}
	}

	//make sure it exists so we don't get called constantly if code loops through stuff that wasn't set.
//	lua_pushstring(L, "nil");
	lua_rawget(L, 1);
	return 1;
}

static int bi_lua_setmodel(lua_State *L)
{
	int entnum;
	edict_t *e;
	lua_getfield(L, 1, "entnum");
	entnum = lua_tointegerx(L, -1, NULL);
	e = (entnum>=lua.maxedicts)?NULL:lua.edicttable[entnum];
	PF_setmodel_Internal(&lua.progfuncs, e, lua_tolstring(L, 2, NULL));
	return 0;
}
static int bi_lua_precache_model(lua_State *L)
{
	PF_precache_model_Internal(&lua.progfuncs, lua_tolstring(L, 1, NULL), false);
	return 0;
}
#define bi_lua_precache_model2 bi_lua_precache_model
static int bi_lua_precache_sound(lua_State *L)
{
	PF_precache_sound_Internal(&lua.progfuncs, lua_tolstring(L, 1, NULL));
	return 0;
}
#define bi_lua_precache_sound2 bi_lua_precache_sound
static int bi_lua_precache_file(lua_State *L)
{
	return 0;
}
static int bi_lua_lightstyle(lua_State *L)
{
	vec3_t rgb;
	if (lua_gettop(L) >= 3)
		lua_readvector(L, 3, rgb);
	else
		VectorSet(rgb, 1, 1, 1);
	PF_applylightstyle(lua_tointegerx(L, 1, NULL), lua_tolstring(L, 2, NULL), rgb);
	return 0;
}
static int bi_lua_spawn(lua_State *L)
{
	edict_t *e = lua.progfuncs.EntAlloc(&lua.progfuncs, false, 0);
	if (e)
		lua_pushedict(L, e);
	else
		lua_pushnil(L);
	return 1;
}
static int bi_lua_remove(lua_State *L)
{
	int entnum;
	edict_t *e;
	lua_getfield(L, 1, "entnum");
	entnum = lua_tointegerx(L, -1, NULL);
	e = (entnum>=lua.maxedicts)?NULL:lua.edicttable[entnum];
	if (e)
		lua.progfuncs.EntFree(&lua.progfuncs, e);
	return 0;
}
static int bi_lua_setorigin(lua_State *L)
{
	edict_t *e;
	lua_getfield(L, 1, "entnum");
	e = EDICT_NUM_UB((&lua.progfuncs), lua_tointegerx(L, -1, NULL));
	lua_readvector(L, 2, e->v->origin);
	World_LinkEdict (&sv.world, (wedict_t*)e, false);
	return 0;
}
static int bi_lua_setsize(lua_State *L)
{
	edict_t *e;
	lua_getfield(L, 1, "entnum");
	e = EDICT_NUM_UB((&lua.progfuncs), lua_tointegerx(L, -1, NULL));
	lua_readvector(L, 2, e->v->mins);
	lua_readvector(L, 3, e->v->maxs);
	VectorSubtract (e->v->maxs, e->v->mins, e->v->size);
	World_LinkEdict (&sv.world, (wedict_t*)e, false);
	return 0;
}

static int bi_lua_localcmd(lua_State *L)
{
	const char	*str = lua_tolstring(lua.ctx, 1, NULL);
	Cbuf_AddText (str, RESTRICT_INSECURE);
	return 0;
}
static int bi_lua_changelevel(lua_State *L)
{
	const char	*s, *spot;

// make sure we don't issue two changelevels (unless the last one failed)
	if (sv.mapchangelocked)
		return 0;
	sv.mapchangelocked = true;

	if (lua_type(L, 2) == LUA_TSTRING)	//and not nil or none
	{
		s = lua_tolstring(lua.ctx, 1, NULL);
		spot = lua_tolstring(lua.ctx, 2, NULL);
		Cbuf_AddText (va("\nchangelevel %s %s\n",s, spot), RESTRICT_LOCAL);
	}
	else
	{
		s = lua_tolstring(lua.ctx, 1, NULL);
		Cbuf_AddText (va("\nchangelevel %s\n",s), RESTRICT_LOCAL);
	}
	return 0;
}

static int bi_lua_stuffcmd(lua_State *L)
{
	int entnum;
	const char *str;
	lua_getfield(L, 1, "entnum");
	entnum = lua_tointegerx(L, -1, NULL);
	str = lua_tolstring(L, 2, NULL);

	PF_stuffcmd_Internal(entnum, str, 0);
	return 0;
}
static int bi_lua_centerprint(lua_State *L)
{
	int entnum;
	const char *str;
	lua_getfield(L, 1, "entnum");
	entnum = lua_tointegerx(L, -1, NULL);
	str = lua_tolstring(L, 2, NULL);
	if (!str)
		str = "";
	PF_centerprint_Internal(entnum, false, str);
	return 0;
}
static int bi_lua_getinfokey(lua_State *L)
{
	int entnum;
	const char *key;
	lua_getfield(L, 1, "entnum");
	entnum = lua_tointegerx(L, -1, NULL);
	key = lua_tolstring(L, 2, NULL);

	key = PF_infokey_Internal(entnum, key);
	lua_pushstring(L, key);
	return 1;
}
#define bi_lua_infokey bi_lua_getinfokey
static int bi_lua_setinfokey(lua_State *L)
{
	int entnum;
	const char *key;
	const char *value;
	int result;
	lua_getfield(L, 1, "entnum");
	entnum = lua_tointegerx(L, -1, NULL);
	key = lua_tolstring(L, 2, NULL);
	value = lua_tolstring(L, 3, NULL);

	result = PF_ForceInfoKey_Internal(entnum, key, value, strlen(value));
	lua_pushinteger(L, result);
	return 1;
}
static int bi_lua_ambientsound(lua_State *L)
{
	vec3_t pos;
	const char *samp = lua_tolstring(L, 2, NULL);
	float vol = lua_tonumberx(L, 3, NULL);
	float attenuation = lua_tonumberx(L, 4, NULL);
	lua_readvector(L, 1, pos);

	PF_ambientsound_Internal(pos, samp, vol, attenuation);
	return 0;
}
static int bi_lua_sound(lua_State *L)
{
	int entnum;
	float channel = lua_tonumberx(L, 2, NULL);
	const char *samp = lua_tolstring(L, 3, NULL);
	float volume = lua_tonumberx(L, 4, NULL);
	float attenuation = lua_tonumberx(L, 5, NULL);

	lua_getfield(L, 1, "entnum");
	entnum = lua_tointegerx(L, -1, NULL);

	//note: channel & 256 == reliable

	SVQ1_StartSound (NULL, (wedict_t*)EDICT_NUM_UB((&lua.progfuncs), entnum), channel, samp, volume*255, attenuation, 0, 0, 0);
	return 0;
}

static int bi_lua_pointcontents(lua_State *L)
{
	vec3_t pos;
	lua_readvector(L, 1, pos);
	lua_pushinteger(L, sv.world.worldmodel->funcs.PointContents(sv.world.worldmodel, NULL, pos));
	return 1;
}

static int bi_lua_setspawnparms(lua_State *L)
{
	globalvars_t pr_globals;

	lua_getfield(L, 1, "entnum");
	pr_globals.param[0].i = lua_tointegerx(L, -1, NULL);
	PF_setspawnparms(&lua.progfuncs, &pr_globals);
	return 0;
}
static int bi_lua_makestatic(lua_State *L)
{
	globalvars_t pr_globals;

	lua_getfield(L, 1, "entnum");
	pr_globals.param[0].i = lua_tointegerx(L, -1, NULL);
	PF_makestatic(&lua.progfuncs, &pr_globals);
	return 0;
}

static int bi_lua_droptofloor(lua_State *L)
{
	extern cvar_t pr_droptofloorunits;
	wedict_t	*ent;
	vec3_t		end;
	vec3_t		start;
	trace_t		trace;
	const float *gravitydir;
	static const vec3_t standardgravity = {0,0,-1};
	pubprogfuncs_t *prinst = &lua.progfuncs;
	world_t *world = prinst->parms->user;

	ent = PROG_TO_WEDICT((&lua.progfuncs), pr_global_struct->self);

	if (ent->xv->gravitydir[2] || ent->xv->gravitydir[1] || ent->xv->gravitydir[0])
		gravitydir = ent->xv->gravitydir;
	else
		gravitydir = standardgravity;

	VectorCopy (ent->v->origin, end);
	if (pr_droptofloorunits.value > 0)
		VectorMA(end, pr_droptofloorunits.value, gravitydir, end);
	else
		VectorMA(end, 256, gravitydir, end);

	VectorCopy (ent->v->origin, start);
	trace = World_Move (world, start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);

	if (trace.fraction == 1 || trace.allsolid)
		lua_pushboolean(L, false);
	else
	{
		VectorCopy (trace.endpos, ent->v->origin);
		World_LinkEdict (world, ent, false);
		ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
		ent->v->groundentity = EDICT_TO_PROG(prinst, trace.ent);
		lua_pushboolean(L, true);
	}
	return 1;
}

static int bi_lua_checkbottom(lua_State *L)
{
	qboolean okay;
	int entnum;
	vec3_t up = {0,0,1};
	lua_getfield(L, 1, "entnum");
	entnum = lua_tointegerx(L, -1, NULL);
	okay = World_CheckBottom(&sv.world, (wedict_t*)EDICT_NUM_UB((&lua.progfuncs), entnum), up);
	lua_pushboolean(L, okay);
	return 1;
}

static int bi_lua_bprint_qw(lua_State *L)
{
	int level = lua_tointegerx(L, 1, NULL);
	const char *str = lua_tolstring(L, 2, NULL);
	SV_BroadcastPrintf (level, "%s", str);
	return 0;
}
static int bi_lua_bprint_nq(lua_State *L)
{
	int level = PRINT_HIGH;
	const char *str = lua_tolstring(L, 1, NULL);
	SV_BroadcastPrintf (level, "%s", str);
	return 0;
}
static int bi_lua_sprint_qw(lua_State *L)
{
	int entnum;
	int level = lua_tointegerx(L, 2, NULL);
	const char *str = lua_tolstring(L, 3, NULL);

	lua_getfield(L, 1, "entnum");
	entnum = lua_tointegerx(L, -1, NULL);

	if (entnum < 1 || entnum > sv.allocated_client_slots)
	{
		Con_TPrintf ("tried to sprint to a non-client\n");
		return 0;
	}
	SV_ClientPrintf (&svs.clients[entnum-1], level, "%s", str);
	return 0;
}
static int bi_lua_sprint_nq(lua_State *L)
{
	int entnum;
	int level = PRINT_HIGH;
	const char *str = lua_tolstring(L, 2, NULL);

	lua_getfield(L, 1, "entnum");
	entnum = lua_tointegerx(L, -1, NULL);

	if (entnum < 1 || entnum > sv.allocated_client_slots)
	{
		Con_TPrintf ("tried to sprint to a non-client\n");
		return 0;
	}
	SV_ClientPrintf (&svs.clients[entnum-1], level, "%s", str);
	return 0;
}

static int bi_lua_cvar_set(lua_State *L)
{
	const char *name = lua_tolstring(L, 1, NULL);
	const char *str = lua_tolstring(L, 2, NULL);
	cvar_t *var = Cvar_FindVar(name);
	if (var)
		Cvar_Set(var, str);
	return 0;
}
static int bi_lua_cvar_get(lua_State *L)
{
	const char *name = lua_tolstring(L, 1, NULL);
	cvar_t *var = Cvar_FindVar(name);
	if (var)
		lua_pushstring(L, var->string);
	else
		lua_pushnil(L);
	return 1;
}
static int bi_lua_cvar(lua_State *L)	//useless get[float]
{
	const char *name = lua_tolstring(L, 1, NULL);
	cvar_t *var = Cvar_FindVar(name);
	if (var)
		lua_pushnumber(L, var->value);
	else
		lua_pushnil(L);
	return 1;
}

static int pushset_trace_globals(lua_State *L, trace_t *trace)
{
	pr_global_struct->trace_allsolid = trace->allsolid;
	pr_global_struct->trace_startsolid = trace->startsolid;
	pr_global_struct->trace_fraction = trace->fraction;
	pr_global_struct->trace_inwater = trace->inwater;
	pr_global_struct->trace_inopen = trace->inopen;
	pr_global_struct->trace_surfaceflagsf = trace->surface?trace->surface->flags:0;
	pr_global_struct->trace_surfaceflagsi = trace->surface?trace->surface->flags:0;
//	if (pr_global_struct->trace_surfacename)
//		prinst->SetStringField(prinst, NULL, &pr_global_struct->trace_surfacename, tr->surface?tr->surface->name:"", true);
	pr_global_struct->trace_endcontentsf = trace->contents;
	pr_global_struct->trace_endcontentsi = trace->contents;
//	if (trace.fraction != 1)
//		VectorMA (trace->endpos, 4, trace->plane.normal, P_VEC(trace_endpos));
//	else
		VectorCopy (trace->endpos, P_VEC(trace_endpos));
	VectorCopy (trace->plane.normal, P_VEC(trace_plane_normal));
	pr_global_struct->trace_plane_dist =  trace->plane.dist;
	pr_global_struct->trace_ent = trace->ent?((wedict_t*)trace->ent)->entnum:0;


	lua_newtable(L);
		lua_pushnumber(L, trace->fraction);
		lua_setfield(L, -2, "fraction");
		lua_pushvector(L, trace->endpos[0], trace->endpos[1], trace->endpos[2]);
		lua_setfield(L, -2, "endpos");
		if (trace->ent)
			lua_pushedict(L, trace->ent);
		else
			lua_pushedict(L, lua.edicttable[0]);
		lua_setfield(L, -2, "ent");

		lua_pushboolean(L, trace->allsolid);
		lua_setfield(L, -2, "allsolid");
		lua_pushboolean(L, trace->startsolid);
		lua_setfield(L, -2, "startsolid");
		lua_pushboolean(L, trace->inwater);
		lua_setfield(L, -2, "inwater");
		lua_pushboolean(L, trace->inopen);
		lua_setfield(L, -2, "inopen");

		lua_newtable(L);
			lua_pushnumber(L, trace->plane.dist);
			lua_setfield(L, -2, "dist");
			lua_pushvector(L, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2]);
			lua_setfield(L, -2, "normal");
		lua_setfield(L, -2, "plane");
	return 1;
}

static int bi_lua_traceline(lua_State *L)
{
	vec3_t v1, v2;
	trace_t	trace;
	int		nomonsters;
	wedict_t	*ent;

	lua_readvector(L, 1, v1);
	lua_readvector(L, 2, v2);
	nomonsters = lua_tointegerx(L, 3, NULL);
	lua_getfield(L, 4, "entnum");
	ent = (wedict_t*)EDICT_NUM_UB((&lua.progfuncs), lua_tointegerx(L, -1, NULL));

	trace = World_Move (&sv.world, v1, vec3_origin, vec3_origin, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent);

	return pushset_trace_globals(L, &trace);
}

static int bi_lua_tracebox(lua_State *L)
{
	vec3_t v1, v2, mins, maxs;
	trace_t	trace;
	int		nomonsters;
	wedict_t	*ent;

	lua_readvector(L, 1, v1);
	lua_readvector(L, 2, v2);
	lua_readvector(L, 3, mins);
	lua_readvector(L, 4, maxs);
	nomonsters = lua_tointegerx(L, 5, NULL);
	lua_getfield(L, 6, "entnum");
	ent = (wedict_t*)EDICT_NUM_UB((&lua.progfuncs), lua_tointegerx(L, -1, NULL));

	trace = World_Move (&sv.world, v1, mins, maxs, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent);

	return pushset_trace_globals(L, &trace);
}

static int bi_lua_walkmove(lua_State *L)
{
	pubprogfuncs_t *prinst = &lua.progfuncs;
	world_t *world = prinst->parms->user;
	wedict_t	*ent;
	float	yaw, dist;
	vec3_t	move;
	int 	oldself;
	vec3_t	axis[3];

	ent = PROG_TO_WEDICT(prinst, *world->g.self);
	yaw = lua_tonumberx(L, 1, NULL);
	dist = lua_tonumberx(L, 2, NULL);

	if ( !( (int)ent->v->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
	{
		lua_pushboolean(L, false);
		return 1;
	}

	World_GetEntGravityAxis(ent, axis);

	yaw = yaw*M_PI*2 / 360;

	VectorScale(axis[0], cos(yaw)*dist, move);
	VectorMA(move, sin(yaw)*dist, axis[1], move);

// save program state, because World_movestep may call other progs
	oldself = *world->g.self;

	lua_pushboolean(L, World_movestep(world, ent, move, axis, true, false, NULL));

// restore program state
	*world->g.self = oldself;

	return 1;
}
static int bi_lua_movetogoal(lua_State *L)
{
	pubprogfuncs_t *prinst = &lua.progfuncs;
	world_t *world = prinst->parms->user;
	wedict_t	*ent;
	float dist;
	ent = PROG_TO_WEDICT(prinst, *world->g.self);
	dist = lua_tonumberx(L, 1, NULL);
	World_MoveToGoal (world, ent, dist);
	return 0;
}

static int bi_lua_nextent(lua_State *L)
{
	world_t *world = &sv.world;
	int		i;
	wedict_t	*ent;

	lua_getfield(L, 1, "entnum");
	i = lua_tointegerx(L, -1, NULL);

	while (1)
	{
		i++;
		if (i == world->num_edicts)
		{
			ent = world->edicts;
			break;
		}
		ent = (wedict_t *)lua.edicttable[i];
		if (!ED_ISFREE(ent))
		{
			break;
		}
	}
	lua_pushedict(L, (struct edict_s *)ent);
	return 1;
}

static int bi_lua_nextclient(lua_State *L)
{
	world_t *world = &sv.world;
	int		i;
	wedict_t	*ent;

	lua_getfield(L, 1, "entnum");
	i = lua_tointegerx(L, -1, NULL);

	while (1)
	{
		i++;
		if (i == sv.allocated_client_slots)
		{
			ent = world->edicts;
			break;
		}
		ent = WEDICT_NUM_UB(world->progs, i);
		if (!ED_ISFREE(ent))
		{
			break;
		}
	}
	lua_pushlightuserdata(L, ent);
	lua_gettable(L, LUA_REGISTRYINDEX);
	return 1;
}

static int bi_lua_checkclient(lua_State *L)
{
	pubprogfuncs_t *prinst = &lua.progfuncs;
	wedict_t *ent;
	ent = WEDICT_NUM_PB(prinst, PF_checkclient_Internal(prinst));
	lua_pushlightuserdata(L, ent);
	lua_gettable(L, LUA_REGISTRYINDEX);
	return 1;
}

static int bi_lua_random(lua_State *L)
{
	lua_pushnumber(L, (rand ()&0x7fff) / ((float)0x8000));
	return 1;
}

static int bi_lua_makevectors(lua_State *L)
{
	vec3_t angles;
	//this is annoying as fuck in lua, what with it writing globals and stuff.
	//perhaps we should support f,u,l=makevectors(ang)... meh, cba.
	lua_readvector(L, 1, angles);
	AngleVectors (angles, pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up);

#if 1
	//globals suck, and lua allows multi-return.
	lua_pushvector(L, (pr_global_struct->v_forward)[0], (pr_global_struct->v_forward)[1], (pr_global_struct->v_forward)[2]);
	lua_pushvector(L, (pr_global_struct->v_right)[0], (pr_global_struct->v_right)[1], (pr_global_struct->v_right)[2]);
	lua_pushvector(L, (pr_global_struct->v_up)[0], (pr_global_struct->v_up)[1], (pr_global_struct->v_up)[2]);
	return 3;
#else
	return 0;
#endif
}
static int bi_lua_normalize(lua_State *L)
{
	vec3_t v;
	lua_readvector(L, 1, v);

	VectorNormalize(v);

	lua_pushvector(L, v[0], v[1], v[2]);
	return 1;
}
static int bi_lua_vectoangles(lua_State *L)
{
	vec3_t forward;
	vec3_t up;
	float *uv = NULL;
	vec3_t ret;
	lua_readvector(L, 1, forward);
	if (lua_type(L, 2) != LUA_TNONE)
	{
		lua_readvector(L, 1, up);
		uv = up;
	}
	VectorAngles(forward, uv, ret, true);

	lua_pushvector(L, ret[0], ret[1], ret[2]);
	return 1;
}
static int bi_lua_vectoyaw(lua_State *L)
{
	vec3_t forward;
	vec3_t up;
	float *uv = NULL;
	vec3_t ret;
	lua_readvector(L, 1, forward);
	if (lua_type(L, 2) != LUA_TNONE)
	{
		lua_readvector(L, 1, up);
		uv = up;
	}
	VectorAngles(forward, uv, ret, true);

	lua_pushnumber(L, ret[1]);
	return 1;
}

void QCBUILTIN PF_aim (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
static int bi_lua_aim(lua_State *L)
{	//aim builtin is for keyboard users, if they still exist.
	globalvars_t pr_globals;
	lua_getfield(L, 1, "entnum");
	pr_globals.param[0].i = lua_tointeger(L, -1);
	pr_globals.param[1].f = lua_tonumber(L, 2);	//speed
	PF_aim(&lua.progfuncs, &pr_globals);
	lua_pushvector(L, pr_globals.ret.vec[0], pr_globals.ret.vec[1], pr_globals.ret.vec[2]);
	return 1;
}

static int bi_lua_tokenize(lua_State *L)
{
	const char *instring = lua_tolstring(L, 1, NULL);
	int argc = 0;
	lua_newtable(L);
	while(NULL != (instring = COM_Parse(instring)))
	{
		//lua is traditionally 1-based
		//for i=1,t.argc do
		lua_pushinteger(L, ++argc);
		lua_pushstring(L, com_token);
		lua_settable(L, -3);

		if (argc == 1)
		{
			while (*instring == ' ' || *instring == '\t')
				instring++;
			lua_pushstring(L, instring);
			lua_setfield(L, -2, "args");	//args is all-but-the-first
		}
	}
	lua_pushinteger(L, argc);
	lua_setfield(L, -2, "argc");	//argc is the count.
	return 1;
}

static int bi_lua_findradiuschain(lua_State *L)
{
	extern cvar_t sv_gameplayfix_blowupfallenzombies;
	world_t *world = &sv.world;
	edict_t	*ent, *chain;
	float	rad;
	vec3_t	org;
	vec3_t	eorg;
	int		i, j;

	chain = (edict_t *)world->edicts;

	lua_readvector(L, 1, org);
	rad = lua_tonumberx(L, 2, NULL);
	rad = rad*rad;

	for (i=1 ; i<world->num_edicts ; i++)
	{
		ent = EDICT_NUM_PB(world->progs, i);
		if (ED_ISFREE(ent))
			continue;
		if (ent->v->solid == SOLID_NOT && (progstype != PROG_QW || !((int)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.value)
			continue;
		for (j=0 ; j<3 ; j++)
			eorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5);
		if (DotProduct(eorg,eorg) > rad)
			continue;

		ent->v->chain = EDICT_TO_PROG(world->progs, chain);
		chain = ent;
	}

	lua_pushlightuserdata(L, chain);
	lua_gettable(L, LUA_REGISTRYINDEX);
	return 1;
}
static int bi_lua_findradiustable(lua_State *L)
{
	extern cvar_t sv_gameplayfix_blowupfallenzombies;
	world_t *world = &sv.world;
	edict_t	*ent, *chain;
	float	rad;
	vec3_t	org;
	vec3_t	eorg;
	int		i, j;
	int results = 1;	//lua arrays are 1-based

	chain = (edict_t *)world->edicts;

	lua_readvector(L, 1, org);
	rad = lua_tonumberx(L, 2, NULL);
	rad = rad*rad;

	lua_newtable(L);	//our return value.

	for (i=1 ; i<world->num_edicts ; i++)
	{
		ent = EDICT_NUM_PB(world->progs, i);
		if (ED_ISFREE(ent))
			continue;
		if (ent->v->solid == SOLID_NOT && (progstype != PROG_QW || !((int)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.value)
			continue;
		for (j=0 ; j<3 ; j++)
			eorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5);
		if (DotProduct(eorg,eorg) > rad)
			continue;

		lua_pushinteger(L, ++results);
		lua_pushlightuserdata(L, ent);
		lua_gettable(L, LUA_REGISTRYINDEX);
		lua_settable(L, -3);
	}

	lua_pushlightuserdata(L, chain);
	lua_gettable(L, LUA_REGISTRYINDEX);
	return 1;
}
#define bi_lua_findradius bi_lua_findradiuschain


static int bi_lua_find(lua_State *L)
{
	world_t *world = &sv.world;
	edict_t	*ent;
	size_t	i;
	const char *s;
	const char *match;

	lua_getfield(L, 1, "entnum");
	i = lua_tointegerx(L, -1, NULL)+1;
	match = lua_tostring(L, 3);

	for ( ; i<world->num_edicts ; i++)
	{
		ent = EDICT_NUM_PB(world->progs, i);
		if (ED_ISFREE(ent))
			continue;
		lua_pushedict(L, ent);
		lua_pushvalue(L, 2);
		lua_gettable(L, -2);
		s = lua_tostring(L, -1);
		if (!s)
			s = "";
		if (!strcmp(s, match))	//should probably do a lua comparison, but nils suck
		{
			lua_pop(L, 2);
			lua_pushedict(L, ent);
			return 1;
		}
		lua_pop(L, 2);
	}

	lua_pushedict(L, EDICT_NUM_PB(world->progs, 0));
	return 1;
}

static int bi_lua_multicast(lua_State *L)
{
	int dest;
	vec3_t org;

	dest = lua_tointegerx(L, 1, NULL);
	lua_readvector(L, 2, org);

	NPP_Flush();
	SV_Multicast (org, dest);

	return 0;
}
extern sizebuf_t csqcmsgbuffer;
static int bi_lua_writechar(lua_State *L)
{
	globalvars_t pr_globals;
	pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST);
	pr_globals.param[1].f = lua_tonumberx(L, 1, NULL);
	PF_WriteChar(&lua.progfuncs, &pr_globals);
	return 0;
}
static int bi_lua_writebyte(lua_State *L)
{
	globalvars_t pr_globals;
	pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST);
	pr_globals.param[1].f = lua_tonumberx(L, 1, NULL);
	PF_WriteByte(&lua.progfuncs, &pr_globals);
	return 0;
}
static int bi_lua_writeshort(lua_State *L)
{
	globalvars_t pr_globals;
	pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST);
	pr_globals.param[1].f = lua_tonumberx(L, 1, NULL);
	PF_WriteShort(&lua.progfuncs, &pr_globals);
	return 0;
}
static int bi_lua_writelong(lua_State *L)
{
	globalvars_t pr_globals;
	pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST);
	pr_globals.param[1].f = lua_tonumberx(L, 1, NULL);
	PF_WriteLong(&lua.progfuncs, &pr_globals);
	return 0;
}
static int bi_lua_writeangle(lua_State *L)
{
	globalvars_t pr_globals;
	pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST);
	pr_globals.param[1].f = lua_tonumberx(L, 1, NULL);
	PF_WriteAngle(&lua.progfuncs, &pr_globals);
	return 0;
}
static int bi_lua_writecoord(lua_State *L)
{
	globalvars_t pr_globals;
	pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST);
	pr_globals.param[1].f = lua_tonumberx(L, 1, NULL);
	PF_WriteCoord(&lua.progfuncs, &pr_globals);
	return 0;
}
static int bi_lua_writestring(lua_State *L)
{
	PF_WriteString_Internal((csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST),lua_tolstring(L, 1, NULL));
	return 0;
}
static int bi_lua_writeentity(lua_State *L)
{
	globalvars_t pr_globals;
	lua_getfield(L, 1, "entnum");
	pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST);
	pr_globals.param[1].i = lua_tointegerx(L, -1, NULL);
	PF_WriteEntity(&lua.progfuncs, &pr_globals);
	return 0;
}

static int bi_lua_WriteChar(lua_State *L)
{
	globalvars_t pr_globals;
	pr_globals.param[0].f = lua_tonumberx(L, 1, NULL);
	pr_globals.param[1].f = lua_tonumberx(L, 2, NULL);
	PF_WriteChar(&lua.progfuncs, &pr_globals);
	return 0;
}
static int bi_lua_WriteByte(lua_State *L)
{
	globalvars_t pr_globals;
	pr_globals.param[0].f = lua_tonumberx(L, 1, NULL);
	pr_globals.param[1].f = lua_tonumberx(L, 2, NULL);
	PF_WriteByte(&lua.progfuncs, &pr_globals);
	return 0;
}
static int bi_lua_WriteShort(lua_State *L)
{
	globalvars_t pr_globals;
	pr_globals.param[0].f = lua_tonumberx(L, 1, NULL);
	pr_globals.param[1].f = lua_tonumberx(L, 2, NULL);
	PF_WriteShort(&lua.progfuncs, &pr_globals);
	return 0;
}
static int bi_lua_WriteLong(lua_State *L)
{
	globalvars_t pr_globals;
	pr_globals.param[0].f = lua_tonumberx(L, 1, NULL);
	pr_globals.param[1].f = lua_tonumberx(L, 2, NULL);
	PF_WriteLong(&lua.progfuncs, &pr_globals);
	return 0;
}
static int bi_lua_WriteAngle(lua_State *L)
{
	globalvars_t pr_globals;
	pr_globals.param[0].f = lua_tonumberx(L, 1, NULL);
	pr_globals.param[1].f = lua_tonumberx(L, 2, NULL);
	PF_WriteAngle(&lua.progfuncs, &pr_globals);
	return 0;
}
static int bi_lua_WriteCoord(lua_State *L)
{
	globalvars_t pr_globals;
	pr_globals.param[0].f = lua_tonumberx(L, 1, NULL);
	pr_globals.param[1].f = lua_tonumberx(L, 2, NULL);
	PF_WriteCoord(&lua.progfuncs, &pr_globals);
	return 0;
}
static int bi_lua_WriteString(lua_State *L)
{
	PF_WriteString_Internal(lua_tonumberx(L, 1, NULL),lua_tolstring(L, 2, NULL));
	return 0;
}
static int bi_lua_WriteEntity(lua_State *L)
{
	globalvars_t pr_globals;
	lua_getfield(L, 2, "entnum");
	pr_globals.param[0].f = lua_tonumberx(L, 1, NULL);
	pr_globals.param[1].i = lua_tointegerx(L, -1, NULL);
	PF_WriteEntity(&lua.progfuncs, &pr_globals);
	return 0;
}

void QCBUILTIN PF_particle (pubprogfuncs_t *prinst, globalvars_t *pr_globals);
static int bi_lua_particle(lua_State *L)
{
	globalvars_t pr_globals;
	lua_readvector(L, 1, pr_globals.param[0].vec);
	lua_readvector(L, 2, pr_globals.param[1].vec);
	pr_globals.param[2].f = lua_tonumberx(L, 3, NULL);
	pr_globals.param[3].f = lua_tonumberx(L, 4, NULL);
	PF_particle(&lua.progfuncs, &pr_globals);
	return 0;
}
static int bi_lua_ChangeYaw(lua_State *L)
{
	PF_changeyaw(&lua.progfuncs, NULL);
	return 0;
}


static int bi_lua_bitnot(lua_State *L)
{
	lua_pushinteger(L, ~lua_tointegerx(L, 1, NULL));
	return 1;
}
static int bi_lua_bitclear(lua_State *L)
{
	lua_pushinteger(L, lua_tointegerx(L, 1, NULL)&~lua_tointegerx(L, 2, NULL));
	return 1;
}
static int bi_lua_bitset(lua_State *L)
{
	lua_pushnumber(L, lua_tointegerx(L, 1, NULL)|lua_tointegerx(L, 2, NULL));
	return 1;
}
#define bi_lua_bitor bi_lua_bitset
static int bi_lua_bitand(lua_State *L)
{
	lua_pushnumber(L, lua_tointegerx(L, 1, NULL)&lua_tointegerx(L, 2, NULL));
	return 1;
}
static int bi_lua_bitxor(lua_State *L)
{
	lua_pushnumber(L, lua_tointegerx(L, 1, NULL)^lua_tointegerx(L, 2, NULL));
	return 1;
}

static int bi_lua_sin(lua_State *L)
{
	lua_pushnumber(L, sin(lua_tonumberx(L, 1, NULL)));
	return 1;
}
static int bi_lua_cos(lua_State *L)
{
	lua_pushnumber(L, cos(lua_tonumberx(L, 1, NULL)));
	return 1;
}
static int bi_lua_tan(lua_State *L)
{
	lua_pushnumber(L, tan(lua_tonumberx(L, 1, NULL)));
	return 1;
}
static int bi_lua_atan2(lua_State *L)
{
	lua_pushnumber(L, atan2(lua_tonumberx(L, 1, NULL), lua_tonumberx(L, 2, NULL)));
	return 1;
}
static int bi_lua_sqrt(lua_State *L)
{
	lua_pushnumber(L, sin(lua_tonumberx(L, 1, NULL)));
	return 1;
}
static int bi_lua_pow(lua_State *L)
{
	lua_pushnumber(L, pow(lua_tonumberx(L, 1, NULL), lua_tonumberx(L, 2, NULL)));
	return 1;
}
static int bi_lua_floor(lua_State *L)
{
	lua_pushnumber(L, floor(lua_tonumberx(L, 1, NULL)));
	return 1;
}
static int bi_lua_rint(lua_State *L)
{	//C rounds towards 0, so bias away from 0 by 0.5 and we'll get the right rounded value.
	lua_Number f = lua_tonumberx(L, 1, NULL);
	if (f < 0)
		lua_pushinteger(L, f - 0.5);
	else
		lua_pushinteger(L, f + 0.5);
	return 1;
}
static int bi_lua_ceil(lua_State *L)
{
	lua_pushnumber(L, ceil(lua_tonumberx(L, 1, NULL)));
	return 1;
}
static int bi_lua_fabs(lua_State *L)
{
	lua_pushnumber(L, fabs(lua_tonumberx(L, 1, NULL)));
	return 1;
}
static int bi_lua_acos(lua_State *L)
{
	lua_pushnumber(L, acos(lua_tonumberx(L, 1, NULL)));
	return 1;
}
static int bi_lua_asin(lua_State *L)
{
	lua_pushnumber(L, asin(lua_tonumberx(L, 1, NULL)));
	return 1;
}
static int bi_lua_atan(lua_State *L)
{
	lua_pushnumber(L, atan(lua_tonumberx(L, 1, NULL)));
	return 1;
}

typedef struct
{
	lua_State *L;
	int idx;
} luafsenum_t;
static int QDECL lua_file_enumerate(const char *fname, qofs_t fsize, time_t mtime, void *param, searchpathfuncs_t *spath)
{
	luafsenum_t *e = param;
	lua_pushinteger(e->L, e->idx++);
	lua_pushfstring(e->L, "%s", fname);
	lua_settable(e->L, -3);
	return true;
}
static int bi_lua_getfilelist(lua_State *L)
{
	luafsenum_t e;
	const char *path = lua_tolstring(L, 1, NULL);
	e.L = L;
	e.idx = 1;	//lua arrays are 1-based.

	lua_newtable(L);	//our return value.
	COM_EnumerateFiles(path, lua_file_enumerate, &e);
	return 1;
}

static int bi_lua_fclose(lua_State *L)
{
	//both fclose and __gc.
	//should we use a different function so that we can warn on dupe fcloses without bugging out on fclose+gc?
	//meh, cba
	vfsfile_t **f = lua_touserdata(L, 1);
	if (f && *f != NULL)
	{
		VFS_CLOSE(*f);
		*f = NULL;
	}
	return 0;
}
static int bi_lua_fopen(lua_State *L)
{
	vfsfile_t *f;
	const char *fname = lua_tolstring(L, 1, NULL);
	qboolean read = true;
	vfsfile_t **ud;
	if (read)
		f = FS_OpenVFS(fname, "rb", FS_GAME);
	else
		f = FS_OpenVFS(fname, "wb", FS_GAMEONLY);
	if (!f)
	{
		lua_pushnil(L);
		return 1;
	}
	ud = lua_newuserdata(L, sizeof(vfsfile_t*));
	*ud = f;
	
	lua_newtable(L);
	lua_pushcclosure(L, bi_lua_fclose, 0);
	lua_setfield(L, -2, "__gc");
	lua_setmetatable(L, -2);
	return 1;
}
static int bi_lua_fgets(lua_State *L)
{
	vfsfile_t **f = lua_touserdata(L, 1);
	char line[8192];
	char *r = NULL;
	if (f && *f)
		r = VFS_GETS(*f, line, sizeof(line));

	if (r)
		lua_pushfstring(L, "%s", r);
	else
		lua_pushnil(L);
	return 1;
}
static int bi_lua_fputs(lua_State *L)
{
	vfsfile_t **f = lua_touserdata(L, 1);
	size_t l;
	const char *str = lua_tolstring(L, 2, &l);
	if (f && *f != NULL)
		VFS_WRITE(*f, str, l);
	return 0;
}

static int bi_lua_loadlua(lua_State *L)
{
	const char *fname = lua_tolstring(L, 1, NULL);
	vfsfile_t *sourcefile = FS_OpenVFS(fname, "rb", FS_GAME);
	if (!sourcefile)
	{
		Con_Printf("Error trying to load %s\n", fname);
		lua_pushnil(L);
	}
	else if (0 != lua_load(L, my_lua_Reader, sourcefile, va("@%s", fname), "bt"))	//load the file, embed it within a function and push it
	{
		Con_Printf("Error trying to parse %s: %s\n", fname, lua_tolstring(L, -1, NULL));
		lua_pushnil(L);
	}
	VFS_CLOSE(sourcefile);
	return 1;
}

static int bi_lua_require(lua_State *L)
{
	const char *fname = lua_tolstring(L, 1, NULL);
	vfsfile_t *sourcefile;
	const char *usename;
	if ((sourcefile = FS_OpenVFS(usename=fname, "rb", FS_GAME)))
		;
	else if ((sourcefile = FS_OpenVFS(usename=va("%s.lua", fname), "rb", FS_GAME)))
		;
	else
	{
		Con_Printf("Error trying to load %s\n", fname);
		return 1;
	}

	if (0 != lua_load(lua.ctx, my_lua_Reader, sourcefile, va("@%s", usename), "bt"))	//load the file, embed it within a function and push it
	{
		//failed - it pushed an error code instead
		Con_Printf("Error trying to parse %s: %s\n", fname, lua_tolstring(lua.ctx, -1, NULL));
		lua_pop(lua.ctx, 1);
	}
	else
	{
		if (lua_pcall(lua.ctx, 0, 0, 0) != 0)	//now call it, so its actually run.
		{
			const char *s = lua_tolstring(lua.ctx, -1, NULL);
			Con_Printf(CON_WARNING "%s\n", s);
			lua_pop(lua.ctx, 1);
		}
	}
	VFS_CLOSE(sourcefile);
	return 0;
}

static int bi_lua_vec3(lua_State *L)
{
	float x = lua_tonumberx(L, 1, NULL);
	float y = lua_tonumberx(L, 2, NULL);
	float z = lua_tonumberx(L, 3, NULL);

	lua_pushvector(L, x, y, z);
	return 1;
}
static int bi_lua_field(lua_State *L)
{
	const char *fname = lua_tostring(L, 1);
	const char *ftype = lua_tostring(L, 2);
	int t;
	size_t u;

	if (!strcmp(ftype, "string"))
		t = ev_string;
	else if (!strcmp(ftype, "float"))
		t = ev_float;
	else if (!strcmp(ftype, "integer"))
		t = ev_integer;
	else if (!strcmp(ftype, "vector"))
		t = ev_vector;
	else if (!strcmp(ftype, "function"))
		t = ev_function;
	else if (!strcmp(ftype, "entity"))
		t = ev_entity;
	else
	{
		Con_Printf("Unknown field type\n");
		return 0;
	}

	if (lua.numflds == countof(lua.entflds))
	{
		Con_Printf("Too many ent fields\n");
		return 0;
	}

	//no dupes please.
	for (u = 0; u < lua.numflds; u++)
	{
		if (!strcmp(lua.entflds[u].name, fname))
			return 0;
	}

	lua.entflds[lua.numflds].offset = -1;	//if we don't know about it yet, then its an artificial field, and present just so that self.whatever returns something other than nil.
	lua.entflds[lua.numflds].name = Lua_AddString(NULL, fname, 0, false);
	lua.entflds[lua.numflds].type = t;
	Hash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck);
	lua.numflds++;

	return 0;
}
static int bi_lua_type(lua_State *L)
{
	const char *tn;
	int t = lua_type(L, 1);
//	luaL_argcheck(L, t != LUA_TNONE, 1, "value expected");
	tn = lua_typename(L, t);
	lua_pushstring(L, tn);
	return 1;
}

static int bi_lua_logfrag(lua_State *L)
{
	//stub... noone cares
	return 0;
}

/*static int bi_lua_entities(lua_State *L)
{
	//stub... noone cares
	return 0;
}*/

#ifdef LIBLUA_STATIC
static int pairsmeta (lua_State *L, const char *method, int iszero,
                      lua_CFunction iter) {
  luaL_checkany(L, 1);
  if (luaL_getmetafield(L, 1, method) == LUA_TNIL) {  /* no metamethod? */
    lua_pushcfunction(L, iter);  /* will return generator, */
    lua_pushvalue(L, 1);  /* state, */
    if (iszero) lua_pushinteger(L, 0);  /* and initial value */
    else lua_pushnil(L);
  }
  else {
    lua_pushvalue(L, 1);  /* argument 'self' to metamethod */
    lua_call(L, 1, 3);  /* get 3 values from metamethod */
  }
  return 3;
}
static int luaB_next (lua_State *L) {
  luaL_checktype(L, 1, LUA_TTABLE);
  lua_settop(L, 2);  /* create a 2nd argument if there isn't one */
  if (lua_next(L, 1))
    return 2;
  else {
    lua_pushnil(L);
    return 1;
  }
}
static int bi_lua_pairs (lua_State *L) {
  return pairsmeta(L, "__pairs", 0, luaB_next);
}
#endif

static int bi_lua_objerror (lua_State *L)
{
	Con_Printf("\n");
	lua_pushedict(L, lua.edicttable[lua.globals.self]);

	lua_pushnil(L);
	while (lua_next(L, -2))
	{	//FIXME: this doesn't find vector fields
		if (lua_type(L, -2) == LUA_TSTRING)
			Con_Printf("%21s:", lua_tostring(L, -2));
		else if (lua_type(L, -2) == LUA_TLIGHTUSERDATA)
		{
			int i;
			const void *ud = lua_topointer(L, -2);
			for (i = 0; i < lua.numflds; i++)
				if (lua.entflds[i].offset == (qintptr_t)ud)
					break;

			if (i == lua.numflds)
				Con_Printf("%21s:", lua_typename(L, lua_type(L, -2)));
			else
				Con_Printf("%21s:", lua.entflds[i].name);
		}
		else
			Con_Printf("%21s:", lua_typename(L, lua_type(L, -2)));
		if (lua_type(L, -1) == LUA_TSTRING)
			Con_Printf(" \"%s\"\n", lua_tostring(L, -1));
		else if (lua_type(L, -1) == LUA_TNUMBER)
			Con_Printf(" \"%g\"\n", lua_tonumber(L, -1));
#ifdef LIBLUA_STATIC
		else if (lua_type(L, -1) == LUA_TFUNCTION)
		{
			lua_Debug ar = {0};
			lua_pushvalue(L, -1);
			lua_getinfo(L, ">nS", &ar);
			Con_Printf(" %s %s:%i\n", ar.name, ar.source, ar.linedefined);
		}
#endif
		else
			Con_Printf(" %s\n", lua_typename(L, lua_type(L, -1)));
		lua_pop(L, 1);
	}

	lua_pop(L, 1);

	Con_Printf("objerror: %s\n", lua_tostring(L, 1));
	return 0;
}
static int bi_lua_error (lua_State *L)
{
	SV_Error("Error: %s\n", lua_tostring(L, 1));
	return 0;
}
static int bi_lua_qtrue (lua_State *L)
{
	//the truth is what you make it...

	const char *s;

	switch(lua_type(L, 1))
	{
	case LUA_TSTRING:
		s = lua_tostring(L, 1);
		lua_pushboolean(L, *s != 0);	//empty string is considered false. WARNING: vanilla QC considered empty-but-not-null strings to be true
		break;

	default:
//	case LUA_TUSERDATA:
//	case LUA_TTHREAD:
//	case LUA_TLIGHTUSERDATA:
//	case LUA_TNONE:
//	case LUA_TNIL:
		lua_pushboolean(L, false);
		break;
	case LUA_TBOOLEAN:
		lua_pushvalue(L, 1);
		break;

	case LUA_TNUMBER:
		lua_pushboolean(L, lua_tonumber(L, 1) != 0);
		break;

	case LUA_TFUNCTION:	//functions are always considered true. otherwise they're nil and not functions.
		lua_pushboolean(L, true);
		break;
	case LUA_TTABLE:
		//might be a vector or an entity.
		lua_getfield(L, 1, "entnum");
		if (!lua_isnil(L, -1))
		{	//okay, so its a table with a valid entnum field. must be an entity.
			lua_pushboolean(L, 0!=lua_tointegerx(L, -1, NULL));	//true if its not 0/world
		}
		else
		{	//assume that its a vector.
			//note that this means that any table without x|y|z fields will be considered false.
			vec3_t v;
			lua_readvector(L, 1, v);
			lua_pushboolean(L, v[0] || v[1] || v[2]);
		}
		break;
	}
	return 1;
}

#define registerfunc(n) lua_pushcclosure(L, bi_lua_##n, 0); lua_setglobal(L, #n);
#define registerfuncn(n) registerfunc(n)	//new crap
#define registerfuncd(n) registerfunc(n)	//deprecated crap
static void my_lua_registerbuiltins(lua_State *L)
{
	lua_atpanic (L, my_lua_panic);

	lua_pushglobaltable(L);
	lua_setfield(L, -1, "_G");

	//standard lua library replacement
	//this avoids the risk of including any way to access os.execute etc, or other file access.
	registerfuncn(tostring);	//standardish
	registerfuncn(tonumber);	//standardish
	registerfuncn(type);		//standardish
	registerfuncn(print);		//'standard' lua print, except prints to console. WARNING: this adds an implicit \n. Use conprint for the quake-style version.
	registerfuncn(require);		//'standard'ish, except uses quake's filesystem instead of reading random system paths.
#ifdef LIBLUA_STATIC
	registerfuncn(pairs);
#endif
	lua_pushnil(L); lua_setglobal(L, "dofile");	//violates our sandbox
	lua_pushnil(L); lua_setglobal(L, "loadfile"); //violates our sandbox

	lua_newtable(L);
		lua_pushcclosure(L, bi_lua_fabs, 0); lua_setfield(L, -2, "abs");
		lua_pushcclosure(L, bi_lua_rint, 0); lua_setfield(L, -2, "rint");
		lua_pushcclosure(L, bi_lua_ceil, 0); lua_setfield(L, -2, "ceil");
		lua_pushcclosure(L, bi_lua_floor, 0); lua_setfield(L, -2, "floor");
		lua_pushcclosure(L, bi_lua_sin, 0); lua_setfield(L, -2, "sin");
		lua_pushcclosure(L, bi_lua_cos, 0); lua_setfield(L, -2, "cos");
		lua_pushcclosure(L, bi_lua_tan, 0); lua_setfield(L, -2, "tan");
		lua_pushcclosure(L, bi_lua_asin, 0); lua_setfield(L, -2, "asin");
		lua_pushcclosure(L, bi_lua_acos, 0); lua_setfield(L, -2, "acos");
		lua_pushcclosure(L, bi_lua_atan, 0); lua_setfield(L, -2, "atan");
		lua_pushcclosure(L, bi_lua_atan2, 0); lua_setfield(L, -2, "atan2");
		lua_pushcclosure(L, bi_lua_sqrt, 0); lua_setfield(L, -2, "sqrt");
		lua_pushcclosure(L, bi_lua_pow, 0); lua_setfield(L, -2, "pow");
		lua_pushnumber  (L, M_PI);			lua_setfield(L, -2, "pi");
	lua_setglobal(L, "math");

//	registerfuncd(entities);

	registerfuncd(loadlua);	//should probably use 'require' instead.
	registerfuncn(vec3);
	registerfuncn(field);
#undef qtrue
	registerfuncn(qtrue);	//for auto-converted code that tests for truth amongst a myriad of different custom types...

	registerfunc(setmodel);
	registerfunc(precache_model);
	registerfuncd(precache_model2);	//pointless alternative name
	registerfunc(precache_sound);
	registerfuncd(precache_sound2);	//pointless alternative name
	registerfuncd(precache_file);	//empty pointless function...
	registerfunc(lightstyle);
	registerfunc(spawn);
	registerfunc(remove);
	registerfunc(nextent);
	registerfunc(nextclient);
	registerfunc(makestatic);
	registerfunc(setorigin);
	registerfunc(setsize);

	registerfuncn(conprint);	//dprint, without the developer

	registerfunc(bprint_qw);
	registerfunc(sprint_qw);
	if (progstype == PROG_QW)
	{
		lua_pushcclosure(L, bi_lua_conprint, 0); lua_setglobal(L, "dprint");	//ignores developer
		lua_pushcclosure(L, bi_lua_bprint_qw, 0); lua_setglobal(L, "bprint");	//an extra level arg
		lua_pushcclosure(L, bi_lua_sprint_qw, 0); lua_setglobal(L, "sprint");	//an extra level arg
	}
	else
	{
		lua_pushcclosure(L, bi_lua_dprint, 0); lua_setglobal(L, "dprint");	//responds to developer
		lua_pushcclosure(L, bi_lua_bprint_nq, 0); lua_setglobal(L, "bprint");	//no level arg
		lua_pushcclosure(L, bi_lua_sprint_nq, 0); lua_setglobal(L, "sprint");	//no level arg
	}
	registerfunc(centerprint);
	registerfunc(ambientsound);
	registerfunc(sound);
	registerfunc(random);
	registerfunc(checkclient);
	registerfunc(stuffcmd);
	registerfunc(localcmd);
	registerfuncd(cvar);			//gets a float value, never a string.
	registerfuncn(cvar_get);
	registerfunc(cvar_set);
	registerfuncd(findradius);		//qc legacy compat. should probably warn when its called or sommit.
	registerfuncn(findradiuschain);	//renamed from qc. because qc's behaviour is annoying enough that we want to discourage its use.
	registerfuncn(findradiustable);	//findradius, but returns an array/table instead.

	registerfunc(objerror);
	registerfunc(error);
//	registerfuncd(break);	//won't implement

	registerfunc(traceline);
	registerfuncn(tracebox);
	registerfunc(walkmove);
	registerfunc(movetogoal);
	registerfunc(droptofloor);
	registerfunc(checkbottom);
	registerfunc(pointcontents);
	registerfuncd(ChangeYaw);

	registerfunc(setspawnparms);
	registerfunc(changelevel);
	registerfunc(logfrag);
	registerfuncd(infokey);
	registerfuncn(getinfokey);
	registerfuncn(setinfokey);
	registerfunc(multicast);
	registerfuncn(writebyte);
	registerfuncn(writechar);
	registerfuncn(writeshort);
	registerfuncn(writelong);
	registerfuncn(writeangle);
	registerfuncn(writecoord);
	registerfuncn(writestring);
	registerfuncn(writeentity);
	registerfuncd(WriteByte);
	registerfuncd(WriteChar);
	registerfuncd(WriteShort);
	registerfuncd(WriteLong);
	registerfuncd(WriteAngle);
	registerfuncd(WriteCoord);
	registerfuncd(WriteString);
	registerfuncd(WriteEntity);
	registerfuncd(particle);
	registerfuncn(bitnot);
	registerfuncn(bitclear);
	registerfuncn(bitset);
	registerfuncn(bitor);
	registerfuncn(bitand);
	registerfuncn(bitxor);
	registerfuncn(sin);
	registerfuncn(cos);
	registerfuncn(acos);
	registerfuncn(asin);
	registerfuncn(atan);
	registerfuncn(atan2);
	registerfuncn(sqrt);
	registerfuncn(pow);
	registerfunc(floor);
	registerfunc(ceil);
	registerfunc(rint);
	registerfunc(fabs);
	registerfuncn(fopen);
	registerfuncn(fclose);
	registerfuncn(fgets);
	registerfuncn(fputs);
	registerfuncn(getfilelist);
	registerfuncd(find);

	//registerfunc(strftime);
	registerfunc(tokenize);
	registerfunc(makevectors);
	registerfunc(normalize);
	registerfunc(vectoangles);
	registerfunc(vectoyaw);
	registerfuncd(aim);	//original implementation nudges v_forward up or down so that keyboard players can still play.
	registerfunc(vtos);
	registerfunc(ftos);
	registerfunc(stof);

	//registerfunc(PRECACHE_VWEP_MODEL);
	//registerfunc(SETPAUSE);


	//set a metatable on the globals table
	//this means that we can just directly use self.foo instead of blob.self.foo
	lua_pushglobaltable(L);
	if (luaL_newmetatable(L, "globals"))
	{
		lua_pushcclosure(L, my_lua_global_set, 0);	//for the luls.
		lua_setfield (L, -2, "__newindex");

		lua_pushcclosure(L, my_lua_global_get, 0);	//for the luls.
		lua_setfield (L, -2, "__index");
	}
	lua_setmetatable(L, -2);
	lua_pop(L, 1);

	if (luaL_newmetatable(L, "vec3_t"))
	{
//		lua_pushcclosure(L, my_lua_vec3_set, 0);	//known writes should change the internal info so the engine can use the information.
//		lua_setfield (L, -2, "__newindex");

//		lua_pushcclosure(L, my_lua_vec3_get, 0);	//we need to de-translate the engine's fields too.
//		lua_setfield (L, -2, "__index");

		lua_pushcclosure(L, my_lua_vec3_tostring, 0);	//cos its prettier than seeing 'table 0x5425729' all over the place
		lua_setfield (L, -2, "__tostring");

		lua_pushcclosure(L, my_lua_vec3_eq, 0);	//for comparisons, you know?
		lua_setfield (L, -2, "__eq");

		lua_pushcclosure(L, my_lua_vec3_add, 0);	//for comparisons, you know?
		lua_setfield (L, -2, "__add");

		lua_pushcclosure(L, my_lua_vec3_sub, 0);	//for comparisons, you know?
		lua_setfield (L, -2, "__sub");

		lua_pushcclosure(L, my_lua_vec3_mul, 0);	//for comparisons, you know?
		lua_setfield (L, -2, "__mul");

		lua_pushcclosure(L, my_lua_vec3_len, 0);	//for comparisons, you know?
		lua_setfield (L, -2, "__len");

		lua_pop(L, 1);
	}
}






static edict_t *QDECL Lua_EdictNum(pubprogfuncs_t *pf, unsigned int num)
{
	int newcount;
	if (num >= lua.maxedicts)
	{
		newcount = num + 64;
		lua.edicttable = realloc(lua.edicttable, newcount*sizeof(*lua.edicttable));
		while(lua.maxedicts < newcount)
			lua.edicttable[lua.maxedicts++] = NULL;

		pf->edicttable_length = lua.maxedicts;
		pf->edicttable = lua.edicttable;
	}
	return lua.edicttable[num];
}
static unsigned int QDECL Lua_NumForEdict(pubprogfuncs_t *pf, edict_t *e)
{
	return e->entnum;
}
static int QDECL Lua_EdictToProgs(pubprogfuncs_t *pf, edict_t *e)
{
	return e->entnum;
}
static edict_t *QDECL Lua_ProgsToEdict(pubprogfuncs_t *pf, int num)
{
	return Lua_EdictNum(pf, num);
}
void Lua_EntClear (pubprogfuncs_t *pf, edict_t *e)
{
	int num = e->entnum;
	memset (e->v, 0, sv.world.edict_size);
	e->ereftype = ER_ENTITY;
	e->entnum = num;
}
edict_t *Lua_CreateEdict(unsigned int num)
{
	edict_t *e;
	e = lua.edicttable[num] = Z_Malloc(sizeof(edict_t) + sv.world.edict_size);
	e->v = (stdentvars_t*)(e+1);
#ifdef VM_Q1
	e->xv = (extentvars_t*)(e->v + 1);
#endif
	e->entnum = num;
	return e;
}
static void QDECL Lua_EntRemove(pubprogfuncs_t *pf, edict_t *e)
{
	lua_State *L = lua.ctx;

	if (!ED_CanFree(e))
		return;
	e->ereftype = ER_FREE;
	e->freetime = sv.time;

	//clear out the lua version of the entity, so that it can be garbage collected.
	//should probably clear out its entnum field too, just in case.
	lua_pushlightuserdata(L, e);
	lua_pushnil(L);
	lua_settable(L, LUA_REGISTRYINDEX);
}
static edict_t *Lua_DoRespawn(pubprogfuncs_t *pf, edict_t *e, int num)
{
	lua_State *L = lua.ctx;
	if (!e)
		e = Lua_CreateEdict(num);
	else
		Lua_EntClear (pf, e);

	ED_Spawned((struct edict_s *) e, false);

	//create a new table for the entity, give it a suitable metatable, and store it into the registry (avoiding GC and allowing us to actually hold on to it).
	lua_pushlightuserdata(L, lua.edicttable[num]);
	lua_newtable(L);
	if (luaL_newmetatable(L, "entity"))
	{
		lua_pushcclosure(L, my_lua_entity_set, 0);	//known writes should change the internal info so the engine can use the information.
		lua_setfield (L, -2, "__newindex");

		lua_pushcclosure(L, my_lua_entity_get, 0);	//we need to de-translate the engine's fields too.
		lua_setfield (L, -2, "__index");

		lua_pushcclosure(L, my_lua_entity_tostring, 0);	//cos its prettier than seeing 'table 0x5425729' all over the place
		lua_setfield (L, -2, "__tostring");

		lua_pushcclosure(L, my_lua_entity_eq, 0);	//for comparisons, you know?
		lua_setfield (L, -2, "__eq");
	}
	lua_setmetatable(L, -2);
	lua_pushinteger(L, num);
	lua_setfield (L, -2, "entnum");	//so we know which entity it is.
	lua_settable(L, LUA_REGISTRYINDEX);
	return e;
}
static edict_t *QDECL Lua_EntAlloc(pubprogfuncs_t *pf, pbool isobject, size_t extrasize)
{
	int i;
	edict_t *e;
	for ( i=0 ; i<sv.world.num_edicts ; i++)
	{
		e = (edict_t*)EDICT_NUM_PB(pf, i);
		// the first couple seconds of server time can involve a lot of
		// freeing and allocating, so relax the replacement policy
		if (!e || (ED_ISFREE(e) && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) ))
		{
			e = Lua_DoRespawn(pf, e, i);
			return (struct edict_s *)e;
		}
	}

	if (i >= sv.world.max_edicts-1)	//try again, but use timed out ents.
	{
		for ( i=0 ; i<sv.world.num_edicts ; i++)
		{
			e = (edict_t*)EDICT_NUM_PB(pf, i);
			// the first couple seconds of server time can involve a lot of
			// freeing and allocating, so relax the replacement policy
			if (!e || ED_ISFREE(e))
			{
				e = Lua_DoRespawn(pf, e, i);
				return (struct edict_s *)e;
			}
		}

		if (i >= sv.world.max_edicts-1)
		{
			Sys_Error ("ED_Alloc: no free edicts");
		}
	}

	sv.world.num_edicts++;
	e = Lua_EdictNum(pf, i);

	e = Lua_DoRespawn(pf, e, i);

	return (struct edict_s *)e;
}

/*static int QDECL Lua_LoadEnts(pubprogfuncs_t *pf, const char *mapstring, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend))
{
	lua_State *L = lua.ctx;
	int i = 0;
	lua_getglobal(L, "LoadEnts");
	lua_newtable(L);
	while(NULL != (mapstring = COM_Parse(mapstring)))
	{
		lua_pushinteger(L, i++);
		lua_pushstring(L, com_token);
		lua_settable(L, -3);
	}
//	lua_pushinteger(L, spawnflags);

	if (lua_pcall(L, 2, 0, 0) != 0)
	{
		const char *s = lua_tolstring(L, -1, NULL);
		Con_Printf(CON_WARNING "%s\n", s);
		lua_pop(L, 1);
	}

	return sv.world.edict_size;
}*/

static const char *Lua_ParseEdict (pubprogfuncs_t *progfuncs, const char *data, struct edict_s *ent)
{
	lua_State *L = lua.ctx;

//	fdef_t		*key;
	pbool	init;
	char		keyname[256];
	int			n;
	int			nest = 1;
	char		token[8192];
	luafld_t *fld;

//	eval_t		*val;

	init = false;

// go through all the dictionary pairs
	while (1)
	{
	// parse key
		data = COM_ParseOut(data, token, sizeof(token));
		if (token[0] == '}')
		{
			if (--nest)
				continue;
			break;
		}
		if (token[0] == '{' && !token[1])
			nest++;
		if (!data)
		{
			Con_Printf ("Lua_ParseEdict: EOF without closing brace\n");
			return NULL;
		}
		if (nest > 1)
			continue;

		strncpy (keyname, token, sizeof(keyname)-1);
		keyname[sizeof(keyname)-1] = 0;

		// another hack to fix heynames with trailing spaces
		n = strlen(keyname);
		while (n && keyname[n-1] == ' ')
		{
			keyname[n-1] = 0;
			n--;
		}

	// parse value
		data = COM_ParseOut(data, token, sizeof(token));
		if (!data)
		{
			Con_Printf ("Lua_ParseEdict: EOF without closing brace\n");
			return NULL;
		}

		if (token[0] == '}')
		{
			Con_Printf ("Lua_ParseEdict: closing brace without data\n");
			return NULL;
		}

		init = true;

// keynames with a leading underscore are used for utility comments,
// and are immediately discarded by quake
		if (keyname[0] == '_')
			continue;



		if (!strcmp(keyname, "angle"))	//Quake anglehack - we've got to leave it in cos it doesn't work for quake otherwise, and this is a QuakeC lib!
		{
			Q_snprintfz (keyname, sizeof(keyname), "angles");	//change it from yaw to 3d angle
			Q_snprintfz (token, sizeof(token), "0 %f 0", atof(token));	//change it from yaw to 3d angle
			goto cont;
		}
/*
		key = ED_FindField (progfuncs, keyname);
		if (!key)
		{
			if (!strcmp(keyname, "light"))	//Quake lighthack - allows a field name and a classname to go by the same thing in the level editor
				if ((key = ED_FindField (progfuncs, "light_lev")))
					goto cont;
			if (externs->badfield && externs->badfield(&progfuncs->funcs, (struct edict_s*)ent, keyname, qcc_token))
				continue;
			Con_DPrintf ("'%s' is not a field\n", keyname);
			continue;
		}
*/
cont:
		fld = Hash_GetInsensitive(&lua.entityfields, keyname);
		if (fld && fld->type == ev_float)
			lua_pushnumber(L, atof(token));
		else if (fld && fld->type == ev_integer)
			lua_pushinteger(L, atoi(token));
		else if (fld && fld->type == ev_vector)
		{
			char *e;
			float x,y,z;
			x = strtod(token, &e);
			if (*e == ' ')
				e++;
			y = strtod(e, &e);
			if (*e == ' ')
				e++;
			z = strtod(e, &e);
			if (*e == ' ')
				e++;
			lua_pushvector(L, x, y, z);
		}
		else if (fld && fld->type == ev_function)
			lua_getglobal(L, token);	//functions are nameless, except for how they're invoked. so this is only for evil mods...
		else if (fld && fld->type == ev_entity)
		{
			int num = atoi(token);
			lua_pushedict(L, EDICT_NUM_UB((&lua.progfuncs), num));
		}
		else
			lua_pushstring(L, token);
		lua_setfield(L, -2, keyname);

		/*if (!ED_ParseEpair (progfuncs, (char*)ent->fields - progfuncs->funcs.stringtable, key->ofs, key->type, qcc_token))
		{
			continue;
//			Sys_Error ("ED_ParseEdict: parse error on entities");
		}*/
	}

	if (!init)
		ent->ereftype = ER_FREE;

	return data;
}

static int QDECL Lua_LoadEnts(pubprogfuncs_t *pf, const char *mapstring, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend), pbool (PDECL *unhandledcallback)(pubprogfuncs_t *,void *,const char **))
{
	lua_State *L = lua.ctx;
	struct edict_s *ed = NULL;
	const char *datastart = mapstring;

	lua_pushglobaltable(L);
	while (1)
	{
		datastart = mapstring;

		mapstring = COM_Parse(mapstring);
		if (!strcmp(com_token, "{"))
		{
			if (!ed)	//first entity
				ed = EDICT_NUM_PB(pf, 0);
			else
				ed = ED_Alloc(pf, false, 0);
			ed->ereftype = ER_ENTITY;
			if (pf->parms->entspawn)
				pf->parms->entspawn(ed, true);

			lua_pushedict(L, ed);
			mapstring = Lua_ParseEdict(pf, mapstring, ed);

			if (1)
			{	//we can't call the callback, as it would be unable to represent the function references.
				int spawnflags, killonspawnflags = *(int*)ctx;//lame. really lame
				lua_getfield(L, -1, "spawnflags");	//push -1["classname"]...
				spawnflags = lua_tointeger(L, -1);
				lua_pop(L, 1);
				if (spawnflags & killonspawnflags)
					lua.progfuncs.EntFree(&lua.progfuncs, ed);
				else
				{
					lua_getfield(L, -1, "classname");	//push -1["classname"]...
					//-3:globaltable, -2:enttable, -1:classname string
					lua_gettable(L, -3);	//pop the classname and look it up inside the global table (to find the function in question)
					lua.globals.self = ed->entnum;
					if (lua_pcall(L, 0, 0, 0) != 0)
					{
						const char *s = lua_tolstring(L, -1, NULL);
						lua_getfield(L, -2, "classname");	//push -1["classname"]...
						Con_Printf(CON_WARNING "spawn func %s: %s\n", lua_tolstring(L, -1, NULL), s);
						lua_pop(L, 2);
					}
				}
			}
			else
			{
				lua_pop(L, 1);
				callback(pf, ed, ctx, datastart, mapstring);
			}
			lua_pop(L, 1);	//pop ent table
		}
		else
			break;	//unexpected token...
	}
	lua_pop(L, 1);
	return sv.world.edict_size;
}

static eval_t *QDECL Lua_GetEdictFieldValue(pubprogfuncs_t *pf, edict_t *e, const char *fieldname, etype_t type, evalc_t *cache)
{
	eval_t *val;
	luafld_t *fld;
	fld = Hash_GetInsensitive(&lua.entityfields, fieldname);
	if (fld)
	{
		val = (eval_t*)((char*)e->v + fld->offset);
		return val;
	}
	return NULL;
}

static eval_t	*QDECL Lua_FindGlobal		(pubprogfuncs_t *prinst, const char *name, progsnum_t num, etype_t *type)
{
	eval_t *val;
	luafld_t *fld;
	fld = Hash_GetInsensitive(&lua.globalfields, name);
	if (fld)
	{
		val = (eval_t*)((char*)&lua.globals + fld->offset);
		return val;
	}

	Con_Printf("Lua_FindGlobal: %s\n", name);
	return NULL;
}
static func_t QDECL Lua_FindFunction		(pubprogfuncs_t *prinst, const char *name, progsnum_t num)
{
	eval_t *val;
	luafld_t *fld;
	fld = Hash_GetInsensitive(&lua.globalfields, name);
	if (fld)
	{
		val = (eval_t*)((char*)&lua.globals + fld->offset);
		return val->function;
	}

	Con_Printf("Lua_FindFunction: %s\n", name);
	return 0;
}

static globalvars_t *QDECL Lua_Globals(pubprogfuncs_t *prinst, int prnum)
{
//	Con_Printf("Lua_Globals: called\n");
	return NULL;
}

char *QDECL Lua_AddString(pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup)
{
	char *ptr;
	int len = strlen(val)+1;
	if (len < minlength)
		len = minlength;
	ptr = Z_TagMalloc(len, LUA_MALLOC_TAG);
	strcpy(ptr, val);
	return ptr;
}
static string_t QDECL Lua_StringToProgs(pubprogfuncs_t *prinst, const char *str)
{
	Con_Printf("Lua_StringToProgs called instead of Lua_SetStringField\n");
	return 0;
}

//passing NULL for ed means its setting a global.
static void QDECL Lua_SetStringField(pubprogfuncs_t *prinst, edict_t *ed, string_t *fld, const char *str, pbool str_is_static)
{
	lua_State *L = lua.ctx;
	string_t val;
	string_t base;
	if (ed)
	{
		base = (ed->entnum+1)<<10;
		val = (char*)fld-(char*)ed->v;

		lua_pushedict(lua.ctx, lua.edicttable[ed->entnum]);
	}
	else
	{
		base = 0;
		val = (char*)fld-(char*)&lua.globals;

		//push the globals list
		lua_pushglobaltable(L);
	}
	*fld = base | val;	//set the engine's value

	//set the stuff so that lua can read it properly.
	lua_pushlightuserdata(L, (void *)(qintptr_t)val);
	lua_pushstring(L, str);
	lua_rawset(L, -3);

	//and pop the table
	lua_pop(L, 1);
}

static const char *ASMCALL QDECL Lua_StringToNative(pubprogfuncs_t *prinst, string_t str)
{
	const char *ret = "";
	unsigned int entnum = str >> 10;
	if (str)
	{
		str &= 1023;
		if (!entnum)
		{
			//okay, its the global table.
			lua_pushglobaltable(lua.ctx);
		}
		else
		{
			entnum-=1;
			if (entnum >= lua.maxedicts)
				return ret;	//erk...
			//get the entity's table
			lua_pushlightuserdata(lua.ctx, lua.edicttable[entnum]);
			lua_gettable(lua.ctx, LUA_REGISTRYINDEX);
		}

		//read the function from the table
		lua_pushlightuserdata(lua.ctx, (void *)(qintptr_t)str);
		lua_rawget(lua.ctx, -2);
		ret = lua_tolstring(lua.ctx, -1, NULL);
		lua_pop(lua.ctx, 2);	//pop the table+string.
		//popping the string is 'safe' on the understanding that the string reference is still held by its containing table, so don't store the string anywhere.
	}

	return ret;
}

static void Lua_Event_Touch(world_t *w, wedict_t *s, wedict_t *o, trace_t *trace)
{
	int oself = pr_global_struct->self;
	int oother = pr_global_struct->other;

	pr_global_struct->self = EDICT_TO_PROG(w->progs, s);
	pr_global_struct->other = EDICT_TO_PROG(w->progs, o);
	pr_global_struct->time = w->physicstime;

#if 1
	PR_ExecuteProgram (w->progs, s->v->touch);
#else
	lua_pushedict(lua.ctx, s);
	//lua_pushliteral(lua.ctx, "touch");
	lua_pushlightuserdata(lua.ctx, (void*)((char*)&s->v->touch-(char*)s->v));
	lua_rawget(lua.ctx, -2);
	lua_replace(lua.ctx, -2);
	if (lua_pcall(lua.ctx, 0, 0, 0) != 0)
	{
		const char *e = lua_tolstring(lua.ctx, -1, NULL);
		lua_pushedict(lua.ctx, (struct edict_s*)s);
		lua_getfield(lua.ctx, -1, "classname");
		Con_Printf(CON_WARNING "%s touch: %s\n", lua_tostring(lua.ctx, -1), e);
		lua_pop(lua.ctx, 3);	//error, enttable, classname
	}
#endif

	pr_global_struct->self = oself;
	pr_global_struct->other = oother;
}

static void Lua_Event_Think(world_t *w, wedict_t *s)
{
	pr_global_struct->self = EDICT_TO_PROG(w->progs, s);
	pr_global_struct->other = EDICT_TO_PROG(w->progs, w->edicts);

#if 0
	PR_ExecuteProgram (w->progs, s->v->think);
#else
	lua_pushedict(lua.ctx, (struct edict_s*)s);
//	lua_pushliteral(lua.ctx, "think");
	lua_pushlightuserdata(lua.ctx, (void*)((char*)&s->v->think-(char*)s->v));
	lua_rawget(lua.ctx, -2);
	lua_replace(lua.ctx, -2);
	if (lua_pcall(lua.ctx, 0, 0, 0) != 0)
	{
		const char *e = lua_tolstring(lua.ctx, -1, NULL);
		lua_pushedict(lua.ctx, (struct edict_s*)s);
		lua_getfield(lua.ctx, -1, "classname");
		Con_Printf(CON_WARNING "%s think: %s\n", lua_tostring(lua.ctx, -1), e);
		lua_pop(lua.ctx, 3);	//error, enttable, classname
	}
#endif
}

static qboolean Lua_Event_ContentsTransition(world_t *w, wedict_t *ent, int oldwatertype, int newwatertype)
{
	return false;	//always do legacy behaviour, because I cba implementing anything else.
}

static void Lua_SetupGlobals(world_t *world)
{
	int flds;
	int bucks;
	comentvars_t	*v = NULL;
	extentvars_t	*xv = (extentvars_t*)(v+1);

	memset(&lua.globals, 0, sizeof(lua.globals));
	lua.globals.physics_mode = 2;
	lua.globals.dimension_send = 255;
	lua.globals.dimension_default = 255;
	lua.globals.global_gravitydir[2] = -1;

	flds = 0;
	bucks = 64;
	Hash_InitTable(&lua.globalfields, bucks, Z_Malloc(Hash_BytesForBuckets(bucks)));

//WARNING: global is not remapped yet...
//This code is written evilly, but works well enough
#define doglobal(n, t)	\
		pr_global_ptrs->n = &lua.globals.n;	\
		lua.globflds[flds].offset = (char*)&lua.globals.n - (char*)&lua.globals;	\
		lua.globflds[flds].name = #n;		\
		lua.globflds[flds].type = t;		\
		Hash_AddInsensitive(&lua.globalfields, lua.globflds[flds].name, &lua.globflds[flds], &lua.globflds[flds].buck);	\
		flds++;
#define doglobal_v(o, f, t)	\
		lua.globflds[flds].offset = (char*)&lua.globals.o - (char*)&lua.globals;	\
		lua.globflds[flds].name = #f;		\
		lua.globflds[flds].type = t;		\
		Hash_AddInsensitive(&lua.globalfields, lua.globflds[flds].name, &lua.globflds[flds], &lua.globflds[flds].buck);	\
		flds++;
#define globalentity(required, name) doglobal(name, ev_entity)
#define globalint(required, name) doglobal(name, ev_integer)
#define globalfloat(required, name) doglobal(name, ev_float)
#define globalstring(required, name) doglobal(name, ev_string)
#define globalvec(required, name) doglobal(name, ev_vector) doglobal_v(name[0], name##_x, ev_float) doglobal_v(name[1], name##_y, ev_float) doglobal_v(name[2], name##_z, ev_float)
#define globalfunc(required, name) doglobal(name, ev_function)
	luagloballist
#undef doglobal
#define doglobal(n, t) doglobal_v(n,n,t)
	luaextragloballist

#define parm(n)\
		pr_global_ptrs->spawnparamglobals[n] = &lua.globals.parm[n];	\
		lua.globflds[flds].offset = (char*)&lua.globals.parm[n] - (char*)&lua.globals;	\
		lua.globflds[flds].name = "parm"#n;		\
		lua.globflds[flds].type = ev_float;		\
		Hash_AddInsensitive(&lua.globalfields, lua.globflds[flds].name, &lua.globflds[flds], &lua.globflds[flds].buck);	\
		flds++;
	parm( 0);parm( 1);parm( 2);parm( 3);parm( 4);parm( 5);parm( 6);parm( 7);
	parm( 8);parm( 9);parm(10);parm(11);parm(12);parm(13);parm(14);parm(15);
#undef parm

	lua.numflds = 0;
	bucks = 256;
	Hash_InitTable(&lua.entityfields, bucks, Z_Malloc(Hash_BytesForBuckets(bucks)));


#define doefield(n, t)	\
		lua.entflds[lua.numflds].offset = (char*)&v->n - (char*)v;	\
		lua.entflds[lua.numflds].name = #n;		\
		lua.entflds[lua.numflds].type = t;		\
		Hash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck);	\
		lua.numflds++;
#define doefield_v(o, f, t)	\
		lua.entflds[lua.numflds].offset = (char*)&v->o - (char*)v;	\
		lua.entflds[lua.numflds].name = #f;		\
		lua.entflds[lua.numflds].type = t;		\
		Hash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck);	\
		lua.numflds++;
#define comfieldentity(name,desc) doefield(name, ev_entity)
#define comfieldint(name,desc) doefield(name, ev_integer)
#define comfieldfloat(name,desc) doefield(name, ev_float)
#define comfieldstring(name,desc) doefield(name, ev_string)
#define comfieldvector(name,desc) doefield(name, ev_vector) doefield_v(name[0], name##_x, ev_float) doefield_v(name[1], name##_y, ev_float) doefield_v(name[2], name##_z, ev_float)
#define comfieldfunction(name,typestr,desc) doefield(name, ev_function)
	comqcfields
#undef doefield
#undef doefield_v
#define doefield(n, t)	\
		lua.entflds[lua.numflds].offset = (char*)&xv->n - (char*)v;	\
		lua.entflds[lua.numflds].name = #n;		\
		lua.entflds[lua.numflds].type = t;		\
		Hash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck);	\
		lua.numflds++;
#define doefield_v(o, f, t)	\
		lua.entflds[lua.numflds].offset = (char*)&xv->o - (char*)v;	\
		lua.entflds[lua.numflds].name = #f;		\
		lua.entflds[lua.numflds].type = t;		\
		Hash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck);	\
		lua.numflds++;
	comextqcfields
	svextqcfields

	PR_SV_FillWorldGlobals(world);
}

void QDECL Lua_ExecuteProgram(pubprogfuncs_t *funcs, func_t func)
{
	unsigned int entnum = func >> 10;
	func &= 1023;
	if (!entnum)
	{
		//okay, its the global table.
		lua_pushglobaltable(lua.ctx);
	}
	else
	{
		entnum-=1;
		if (entnum >= lua.maxedicts)
			return;	//erk...
		//get the entity's table
		lua_pushlightuserdata(lua.ctx, lua.edicttable[entnum]);
		lua_gettable(lua.ctx, LUA_REGISTRYINDEX);
	}

	//read the function from the table
	lua_pushlightuserdata(lua.ctx, (void *)(qintptr_t)func);
	lua_rawget(lua.ctx, -2);

	//and now invoke it.
	if (lua_pcall(lua.ctx, 0, 0, 0) != 0)
	{
		const char *s = lua_tolstring(lua.ctx, -1, NULL);
		Con_Printf(CON_WARNING "%s\n", s);
		lua_pop(lua.ctx, 1);
	}
}

void PDECL Lua_CloseProgs(pubprogfuncs_t *inst)
{
	lua_close(lua.ctx);
	free(lua.edicttable);
	lua.edicttable = NULL;
	lua.maxedicts = 0;

	memset(&lua, 0, sizeof(lua));

	Z_FreeTags(LUA_MALLOC_TAG);
}

static void QDECL Lua_Get_FrameState(world_t *w, wedict_t *ent, framestate_t *fstate)
{
	memset(fstate, 0, sizeof(*fstate));
	fstate->g[FS_REG].frame[0] = ent->v->frame;
	fstate->g[FS_REG].frametime[0] = ent->xv->frame1time;
	fstate->g[FS_REG].lerpweight[0] = 1;
	fstate->g[FS_REG].endbone = 0x7fffffff;

	fstate->g[FST_BASE].frame[0] = ent->xv->baseframe;
	fstate->g[FST_BASE].frametime[0] = ent->xv->/*base*/frame1time;
	fstate->g[FST_BASE].lerpweight[0] = 1;
	fstate->g[FST_BASE].endbone = ent->xv->basebone;

#if defined(SKELETALOBJECTS) || defined(RAGDOLL)
	if (ent->xv->skeletonindex)
		skel_lookup(w, ent->xv->skeletonindex, fstate);
#endif
}

qboolean PR_LoadLua(void)
{
	world_t *world = &sv.world;
	pubprogfuncs_t *pf;
	vfsfile_t *sourcefile = NULL;
	if ((sourcefile = FS_OpenVFS("qwprogs.lua", "rb", FS_GAME)))
		progstype = PROG_QW;
	else if ((sourcefile = FS_OpenVFS("progs.lua", "rb", FS_GAME)))
		progstype = PROG_NQ;
	else
		return false;

	if (!init_lua())
	{
		VFS_CLOSE(sourcefile);
		Con_Printf("WARNING: Found progs.lua, but could load load lua library\n");
		return false;
	}

	pf = svprogfuncs = &lua.progfuncs;

	pf->CloseProgs = Lua_CloseProgs;
	pf->AddString = Lua_AddString;
	pf->EdictNum = Lua_EdictNum;
	pf->NumForEdict = Lua_NumForEdict;
	pf->EdictToProgs = Lua_EdictToProgs;
	pf->ProgsToEdict = Lua_ProgsToEdict;
	pf->EntAlloc = Lua_EntAlloc;
	pf->EntFree = Lua_EntRemove;
	pf->EntClear = Lua_EntClear;
	pf->FindGlobal = Lua_FindGlobal;
	pf->load_ents = Lua_LoadEnts;
	pf->globals = Lua_Globals;
	pf->GetEdictFieldValue = Lua_GetEdictFieldValue;
	pf->SetStringField = Lua_SetStringField;
	pf->StringToProgs = Lua_StringToProgs;
	pf->StringToNative = Lua_StringToNative;
	pf->ExecuteProgram = Lua_ExecuteProgram;
	pf->FindFunction = Lua_FindFunction;

	world->Event_Touch = Lua_Event_Touch;
	world->Event_Think = Lua_Event_Think;
	world->Event_Sound = SVQ1_StartSound;
	world->Event_ContentsTransition = Lua_Event_ContentsTransition;
	world->Get_CModel = SVPR_GetCModel;
	world->Get_FrameState = Lua_Get_FrameState;

	world->progs = pf;
	world->progs->parms = &lua.progfuncsparms;
	world->progs->parms->user = world;
	world->progs->parms->Printf = PR_Printf;
	world->progs->parms->DPrintf = PR_DPrintf;
	world->usesolidcorpse = true;

	Lua_SetupGlobals(world);

	svs.numprogs = 0;	//Why is this svs?
#ifdef VM_Q1
	world->edict_size = sizeof(stdentvars_t) + sizeof(extentvars_t);
#else
	world->edict_size = sizeof(stdentvars_t);
#endif

	//force items2 instead of serverflags
	sv.haveitems2 = true;

	//initalise basic lua context
	lua.ctx = lua_newstate(my_lua_alloc, NULL);					//create our lua state
//	luaL_openlibs(lua.ctx); 
	my_lua_registerbuiltins(lua.ctx);

	//spawn the world, woo.
	world->edicts = (wedict_t*)pf->EntAlloc(pf,false,0);

	//load the gamecode now. it should be safe for it to call various builtins.
	if (0 != lua_load(lua.ctx, my_lua_Reader, sourcefile, "progs.lua", "bt"))	//load the file, embed it within a function and push it
	{
		Con_Printf("Error trying to parse %s: %s\n", "progs.lua", lua_tolstring(lua.ctx, -1, NULL));
		lua_pop(lua.ctx, 1);
	}
	else
	{
		if (lua_pcall(lua.ctx, 0, 0, 0) != 0)
		{
			const char *s = lua_tolstring(lua.ctx, -1, NULL);
			Con_Printf(CON_WARNING "%s\n", s);
			lua_pop(lua.ctx, 1);
		}
	}
	VFS_CLOSE(sourcefile);

	return true;
}
#endif	//VM_LUA
