mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-26 22:01:50 +00:00
c4132347eb
rewrote tab completion logic. should no longer consume so much cpu time. added tab-completion for the connect command. server lists must have been requested previously (like the connectbr command requires). fix q1bsp fencetexture+fog combo. fix wateralpha/lavaalpha/slimealpha/telealpha worldspawn fields. added a couple of extra cvars to some rulesets. fix d3d9 mipmap-size issue. fix vid_reload issue (was crashing, but its also possible that it could have been the cause of VBO corruption). made pausable default to empty, allowing for smarter defaults like pausing SP but not DM. attempt to compensate for NQ's framerate-dependant waterjumps by making QW waterjumps slightly more permissive. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5241 fc73d0e0-1445-4013-8a0c-d673dee63da5
3344 lines
94 KiB
C
3344 lines
94 KiB
C
#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);
|
|
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, 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);
|
|
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))
|
|
{
|
|
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, 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)
|
|
{
|
|
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
|