#include"luaapi.h" #include"resman.h" #include #include #include #include"game.h" #include"k3mix.h" #include"k3water.h" #include"net_server.h" #include"net_client.h" #include"gl.h" #include"k4.h" #include"net.h" #include"k3menu.h" #include /* * This is by far the least clean or well-maintained source in the * entire project. Touch it as little as possible to minimize loss * of sanity. * */ static lua_State *L; #define seti(src, dst) \ lua_getfield(L, -1, src);\ li = lua_tointegerx(L, -1, &i);\ if(i) {\ dst = li;\ }\ lua_pop(L, 1); #define setf(src, dst) \ lua_getfield(L, -1, src);\ dst = lua_tonumber(L, -1);\ lua_pop(L, 1); #define setv3(src, dst) \ lua_getfield(L, -1, src);\ if(lua_istable(L, -1)) {\ lua_geti(L, -1, 1);\ lua_geti(L, -2, 2);\ lua_geti(L, -3, 3);\ dst[0] = lua_tonumber(L, -3);\ dst[1] = lua_tonumber(L, -2);\ dst[2] = lua_tonumber(L, -1);\ lua_pop(L, 3);\ }\ lua_pop(L, 1); #define setv4(src, dst) \ lua_getfield(L, -1, src);\ if(lua_istable(L, -1)) {\ lua_geti(L, -1, 1);\ lua_geti(L, -2, 2);\ lua_geti(L, -3, 3);\ lua_geti(L, -3, 4);\ dst[0] = lua_tonumber(L, -4);\ dst[1] = lua_tonumber(L, -3);\ dst[2] = lua_tonumber(L, -2);\ dst[3] = lua_tonumber(L, -1);\ lua_pop(L, 4);\ }\ lua_pop(L, 1); #define setstrstatic(src, dst, maxlen) \ lua_getfield(L, -1, src);\ if(lua_isstring(L, -1)) {\ size_t len;\ const char *s = lua_tolstring(L, -1, &len);\ strncpy(dst, s, (maxlen) - 1);\ }\ lua_pop(L, 1); static int serialize(struct bstr *b) { int top = lua_gettop(L); if(lua_type(L, -1) == LUA_TNIL) { b_wu8(b, 0); } else if(lua_type(L, -1) == LUA_TNUMBER) { if(lua_isinteger(L, -1)) { b_wu8(b, 1); b_wu64(b, lua_tointeger(L, -1)); } else { b_wu8(b, 2); b_wf64(b, lua_tonumber(L, -1)); } } else if(lua_type(L, -1) == LUA_TSTRING) { b_wu8(b, 3); size_t len; const uint8_t *data = lua_tolstring(L, -1, &len); b_wu32(b, len); b_wraw(b, data, len); } else if(lua_type(L, -1) == LUA_TBOOLEAN) { b_wu8(b, lua_toboolean(L, -1) ? 5 : 4); } else if(lua_type(L, -1) == LUA_TTABLE) { b_wu8(b, 6); lua_pushnil(L); while(lua_next(L, -2)) { lua_pushvalue(L, -2); if(!serialize(b)) goto error; lua_pop(L, 1); if(!serialize(b)) goto error; lua_pop(L, 1); } b_wu8(b, 0); } else goto error; return 1; error: lua_settop(L, top); return 0; } static int deserialize(struct bstr *b) { uint8_t type = b_ru8(b); int top = lua_gettop(L); if(type == 0) { lua_pushnil(L); } else if(type == 1) { lua_pushinteger(L, b_ru64(b)); } else if(type == 2) { lua_pushnumber(L, b_rf64(b)); } else if(type == 3) { size_t len = b_ru32(b); lua_pushlstring(L, b_rraw(b, len), len); } else if(type == 4) { lua_pushboolean(L, 0); } else if(type == 5) { lua_pushboolean(L, 1); } else if(type == 6) { lua_newtable(L); while(1) { deserialize(b); if(lua_type(L, -1) == LUA_TNIL) { lua_pop(L, 1); break; } deserialize(b); lua_rawset(L, -3); } } else goto error; return 1; error: lua_settop(L, top); return 0; } static int game_addentity(lua_State *L) { lua_Integer li; int i; uint16_t id; lua_getfield(L, 1, "id"); if(lua_isnil(L, -1)) { id = game_nextid(); } else { id = lua_tointeger(L, -1); } lua_pop(L, 1); game_claimentid(id); lua_getfield(L, 1, "physics"); if(lua_istable(L, -1)) { struct CPhysics c; memset(&c, 0, sizeof(c)); c.entity = id; seti("trigger", c.trigger); c.dynamics = CPHYSICS_STATIC; lua_getfield(L, -1, "dynamics"); if(lua_tostring(L, -1)) { if(!strcmp("dynamic", lua_tostring(L, -1))) { c.dynamics = CPHYSICS_DYNAMIC; } else if(!strcmp("static", lua_tostring(L, -1))) { c.dynamics = CPHYSICS_STATIC; } else if(!strcmp("kinematic", lua_tostring(L, -1))) { c.dynamics = CPHYSICS_KINEMATIC; } else { k3Log(k3_WARN, "Invalid \"dynamics\" field %s", lua_tostring(L, -1)); } } lua_pop(L, 1); lua_getfield(L, -1, "ghost"); if(lua_toboolean(L, -1)) { c.dynamics |= CPHYSICS_GHOST; } lua_pop(L, 1); // Default c.type = CPHYSICS_SPHERE; c.sphere.radius = 0.25; bool found_shape = false; lua_getfield(L, -1, "capsule"); if(lua_type(L, -1) == LUA_TTABLE) { found_shape = true; c.type = CPHYSICS_CAPSULE; setf("radius", c.capsule.radius); setf("length", c.capsule.length); } lua_pop(L, 1); lua_getfield(L, -1, "box"); if(lua_type(L, -1) == LUA_TTABLE) { found_shape = true; c.type = CPHYSICS_BOX; setf("w", c.box.w); setf("h", c.box.h); setf("l", c.box.l); } lua_pop(L, 1); lua_getfield(L, -1, "sphere"); if(lua_type(L, -1) == LUA_TTABLE) { found_shape = true; c.type = CPHYSICS_SPHERE; setf("radius", c.sphere.radius); } lua_pop(L, 1); lua_getfield(L, -1, "trimesh"); if(lua_type(L, -1) != LUA_TNIL) { found_shape = true; c.type = CPHYSICS_TRIMESH; if(lua_type(L, -1) == LUA_TSTRING) { strncpy(c.trimesh.name, lua_tostring(L, -1), sizeof(c.trimesh.name) - 1); } else { struct TrimeshData *tmd = *(struct TrimeshData**) luaL_checkudata(L, -1, "k3physics"); struct ResManRes *res = resman_rev(tmd); if(res) { res->refs++; } c.trimesh.cache = tmd; } } lua_pop(L, 1); if(!found_shape) { k3Log(k3_INFO, "Entity with invalid shape. Falling back to sphere."); } setf("speed", c.speed); lua_getfield(L, -1, "mass"); c.mass = lua_isnil(L, -1) ? 1 : lua_tonumber(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "friction"); c.friction = lua_isnil(L, -1) ? 1.414 : lua_tonumber(L, -1); lua_pop(L, 1); setv3("pos", c.start); c.collide = CATEGORY_STATIC | CATEGORY_ENTITY; lua_getfield(L, -1, "collide"); if(!lua_isnil(L, -1)) { c.collide = lua_tointeger(L, -1); } lua_pop(L, 1); lua_getfield(L, -1, "rot"); if(lua_istable(L, -1)) { lua_geti(L, -1, 1); lua_geti(L, -2, 2); lua_geti(L, -3, 3); lua_geti(L, -4, 4); c.rot[0] = lua_tonumber(L, -4); c.rot[1] = lua_tonumber(L, -3); c.rot[2] = lua_tonumber(L, -2); c.rot[3] = lua_tonumber(L, -1); lua_pop(L, 4); } else { c.rot[0] = 0; c.rot[1] = 0; c.rot[2] = 0; c.rot[3] = 1; } lua_pop(L, 1); game_addcomponent(physics, &c); } lua_pop(L, 1); lua_getfield(L, 1, "render"); if(lua_istable(L, -1)) { struct CRender c; memset(&c, 0, sizeof(c)); c.entity = id; setv4("pos", c.pos); setv4("rot", c.rot); setstrstatic("mdl", c.mdl, sizeof(c.mdl)); //c.animator.base = NULL; game_addcomponent(render, &c); } lua_pop(L, 1); lua_getfield(L, 1, "movement"); if(lua_istable(L, -1)) { struct CMovement c; memset(&c, 0, sizeof(c)); c.entity = id; setv3("dir", c.dir); setf("pointing", c.pointing); seti("jump", c.jump); seti("canjump", c.canJump); c.holding = ENT_ID_INVALID; game_addcomponent(movement, &c); } lua_pop(L, 1); lua_getfield(L, 1, "boned"); if(lua_istable(L, -1)) { struct CBoned c; memset(&c, 0, sizeof(c)); c.entity = id; lua_getfield(L, -1, "size"); if(lua_isnil(L, -1)) { lua_pop(L, 1); lua_getfield(L, -1, "bones"); c.size = lua_isnil(L, -1) ? 0 : luaL_len(L, -1); } else { c.size = lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "bones"); } c.bones = _mm_malloc(sizeof(*c.bones) * c.size, 16); if(lua_isnil(L, -1)) { glm_quat_identity_array((versor*) c.bones, c.size * 2); } else { for(size_t i = 0; i < c.size; i++) { lua_rawgeti(L, -1, i + 1); // bone if(lua_isnil(L, -1)) { c.bones[i].translation[0] = 0; c.bones[i].translation[1] = 0; c.bones[i].translation[2] = 0; c.bones[i].translation[3] = 1; c.bones[i].rotation[0] = 0; c.bones[i].rotation[1] = 0; c.bones[i].rotation[2] = 0; c.bones[i].rotation[3] = 1; } else { lua_rawgeti(L, -1, 1); // translation lua_rawgeti(L, -1, 1); // x lua_rawgeti(L, -2, 2); // y lua_rawgeti(L, -3, 3); // z lua_rawgeti(L, -4, 4); // w c.bones[i].translation[0] = lua_tonumber(L, -4); c.bones[i].translation[1] = lua_tonumber(L, -3); c.bones[i].translation[2] = lua_tonumber(L, -2); c.bones[i].translation[3] = lua_tonumber(L, -1); lua_pop(L, 5); lua_rawgeti(L, -1, 2); // rotation lua_rawgeti(L, -1, 1); // x lua_rawgeti(L, -2, 2); // y lua_rawgeti(L, -3, 3); // z lua_rawgeti(L, -4, 4); // w c.bones[i].rotation[0] = lua_tonumber(L, -4); c.bones[i].rotation[1] = lua_tonumber(L, -3); c.bones[i].rotation[2] = lua_tonumber(L, -2); c.bones[i].rotation[3] = lua_tonumber(L, -1); lua_pop(L, 5); } lua_pop(L, 1); } } lua_pop(L, 1); lua_getfield(L, -1, "anim"); if(lua_istable(L, -1)) { lua_getfield(L, -1, "id"); c.anim.id = lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "standard"); c.anim.standard = lua_toboolean(L, -1); lua_pop(L, 1); } lua_pop(L, 1); game_addcomponent(boned, &c); } lua_pop(L, 1); lua_pushinteger(L, id); return 1; } double LuaapiStartTime; float LuaapiFov = 75; static int gametriggersref; static int luaapi_triggerfunc(size_t id, uint16_t ethis, uint16_t ethat, uint8_t event) { lua_rawgeti(L, LUA_REGISTRYINDEX, gametriggersref); lua_rawgeti(L, -1, id); lua_pushinteger(L, id); if(ethis == ENT_ID_INVALID) lua_pushnil(L); else lua_pushinteger(L, ethis); if(ethat == ENT_ID_INVALID) lua_pushnil(L); else lua_pushinteger(L, ethat); lua_pushinteger(L, event); if(lua_pcall(L, 4, 0, 0) != LUA_OK) { puts(lua_tolstring(L, -1, NULL)); lua_pop(L, 1); } lua_pop(L, 1); } static int game_triggers_set(lua_State *L) { size_t idx = lua_tointeger(L, 2); if(idx >= 1 && idx <= MAX_TRIGGERS) { lua_pushvalue(L, 2); lua_pushvalue(L, 3); lua_rawset(L, 1); if(idx > Game.triggerCount) { Game.triggers = realloc(Game.triggers, sizeof(*Game.triggers) * idx); for(size_t j = Game.triggerCount; j < idx; j++) { Game.triggers[j] = NULL; } Game.triggerCount = idx; } Game.triggers[idx - 1] = luaapi_triggerfunc; } return 0; } static int game_triggers_get(lua_State *L) { lua_pushvalue(L, 2); lua_rawget(L, 1); return 1; } static int gameconveyorsref; static int game_conveyors_set(lua_State *L) { size_t idx = lua_tointeger(L, 2); if(idx >= 1 && idx <= MAX_CONVEYORS) { if(Game.conveyorCount < idx) { Game.conveyors = realloc(Game.conveyors, sizeof(*Game.conveyors) * idx); for(size_t i = Game.conveyorCount; i < idx; i++) { Game.conveyors[i] = NULL; } Game.conveyorCount = idx; } struct Conveyor *c = Game.conveyors[idx - 1]; if(!c) { c = calloc(sizeof(*c), 1); } lua_getfield(L, 3, "entity"); c->entity = lua_isnil(L, -1) ? ENT_ID_INVALID : lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, 3, "active"); c->active = lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, 3, "type"); c->type = lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, 3, "speed"); c->speed = lua_tonumber(L, -1); lua_pop(L, 1); lua_getfield(L, 3, "position"); c->position = lua_tonumber(L, -1); lua_pop(L, 1); lua_getfield(L, 3, "points"); c->pointCount = lua_rawlen(L, -1); c->points = realloc(c->points, sizeof(*c->points) * c->pointCount); for(size_t i = 0; i < c->pointCount; i++) { lua_rawgeti(L, -1, i + 1); lua_rawgeti(L, -1, 1); lua_rawgeti(L, -2, 2); lua_rawgeti(L, -3, 3); c->points[i][0] = lua_tonumber(L, -3); c->points[i][1] = lua_tonumber(L, -2); c->points[i][2] = lua_tonumber(L, -1); lua_pop(L, 4); } lua_pop(L, 1); lua_getfield(L, 3, "trigger"); c->trigger = lua_isnil(L, -1) ? TRIGGER_INVALID : lua_tointeger(L, -1); lua_pop(L, 1); Game.conveyors[idx - 1] = c; } return 0; } static int game_conveyors_get(lua_State *L) { size_t idx = lua_tointeger(L, 2); if(idx >= 1 && idx <= Game.conveyorCount && Game.conveyors[idx - 1]) { size_t *z = lua_newuserdata(L, sizeof(*z)); *z = idx; luaL_setmetatable(L, "k3conveyor"); } else { lua_pushnil(L); } return 1; } static int game_conveyor_set(lua_State *L) { const char *k = lua_tostring(L, 2); size_t *idxptr = lua_touserdata(L, 1); size_t idx = *idxptr; if(idx == 0 || idx > Game.conveyorCount || !Game.conveyors[idx - 1]) { return 0; } struct Conveyor *conv = Game.conveyors[idx - 1]; if(!strcmp(k, "entity")) { conv->entity = lua_isnil(L, 3) ? ENT_ID_INVALID : lua_tointeger(L, 3); } else if(!strcmp(k, "active")) { conv->active = lua_tointeger(L, 3); } else if(!strcmp(k, "type")) { conv->type = lua_tointeger(L, 3); } else if(!strcmp(k, "speed")) { conv->speed = lua_tonumber(L, 3); } else if(!strcmp(k, "position")) { conv->position = lua_tonumber(L, 3); } else if(!strcmp(k, "points")) { // TODO } return 0; } static int game_conveyor_get(lua_State *L) { const char *k = lua_tostring(L, 2); size_t *idxptr = lua_touserdata(L, 1); size_t idx = *idxptr; if(idx == 0 || idx > Game.conveyorCount || !Game.conveyors[idx - 1]) { return 0; } struct Conveyor *conv = Game.conveyors[idx - 1]; if(!strcmp(k, "entity")) { if(conv->entity == ENT_ID_INVALID) { lua_pushnil(L); } else { lua_pushinteger(L, conv->entity); } } else if(!strcmp(k, "active")) { lua_pushinteger(L, conv->active); } else if(!strcmp(k, "type")) { lua_pushinteger(L, conv->type); } else if(!strcmp(k, "speed")) { lua_pushnumber(L, conv->speed); } else if(!strcmp(k, "position")) { lua_pushnumber(L, conv->position); } else if(!strcmp(k, "points")) { lua_newtable(L); for(size_t i = 0; i < conv->pointCount; i++) { lua_newtable(L); lua_pushnumber(L, conv->points[i][0]); lua_rawseti(L, -2, 1); lua_pushnumber(L, conv->points[i][1]); lua_rawseti(L, -2, 2); lua_pushnumber(L, conv->points[i][2]); lua_rawseti(L, -2, 3); lua_rawseti(L, -2, i + 1); } } else return 0; return 1; } static int game_water(lua_State *L) { void **mat = lua_touserdata(L, 4); struct k3Water **w = lua_newuserdata(L, sizeof(*w)); *w = k3WaterCreate(lua_tonumber(L, 1), lua_tonumber(L, 2), lua_tointeger(L, 3), *mat); luaL_setmetatable(L, "k3water"); return 1; } static int game_water_simulate(lua_State *L) { void **w = lua_touserdata(L, 1); k3WaterSimulate(*w, lua_tonumber(L, 2), lua_tonumber(L, 3), lua_tonumber(L, 4), lua_tointeger(L, 5)); return 0; } static int game_water_update(lua_State *L) { void **w = lua_touserdata(L, 1); k3WaterUpdate(*w); return 0; } static int game_water_disturb(lua_State *L) { void **w = lua_touserdata(L, 1); k3WaterDisturb(*w, lua_tonumber(L, 2), lua_tonumber(L, 3), lua_tonumber(L, 4), lua_tonumber(L, 5)); return 0; } struct mixitem { void *thing; #define MIXITEM_SOURCE 0 #define MIXITEM_SOUND 1 #define MIXITEM_QUEUE 2 #define MIXITEM_POWER 3 int type; }; static int dagame_mixsound(lua_State *L) { struct mixitem *i = lua_touserdata(L, 1); assert(i->type == MIXITEM_SOURCE); struct mixitem *r = lua_newuserdata(L, sizeof(*r)); r->type = MIXITEM_SOUND; r->thing = k3MixSimple(i->thing); luaL_setmetatable(L, "k3mixwave"); return 1; } static int game_mixplay(lua_State *L) { struct mixitem *i = lua_touserdata(L, 1); assert(i->type != MIXITEM_SOURCE); k3MixPlay(i->thing); lua_pushvalue(L, 1); return 1; } static int dagame_mixstop(lua_State *L) { struct mixitem *i = lua_touserdata(L, 1); assert(i->type != MIXITEM_SOURCE); k3MixStop(i->thing); lua_pushvalue(L, 1); return 1; } static int dagame_mixpower(lua_State *L) { struct mixitem *i = lua_touserdata(L, 1); assert(i->type != MIXITEM_SOURCE); struct k3MixWave *wav = k3MixPowerMeasurement(i->thing); struct mixitem *r = lua_newuserdata(L, sizeof(*r)); r->thing = wav; r->type = MIXITEM_POWER; luaL_setmetatable(L, "k3mixwave"); return 1; } static int game_mixwave_set(lua_State *L) { struct mixitem *i = lua_touserdata(L, 1); assert(i->type != MIXITEM_SOURCE); if(!strcmp(lua_tostring(L, 2), "volume")) { ((struct k3MixWave*) i->thing)->volume = lua_tonumber(L, 3); } else if(!strcmp(lua_tostring(L, 2), "loop")) { ((struct k3MixWave*) i->thing)->loop = lua_toboolean(L, 3); } else if(i->type == MIXITEM_SOUND && !strcmp(lua_tostring(L, 2), "time")) { k3MixSimpleSeek((struct k3MixWave*) i->thing, lua_tonumber(L, 3)); } return 0; } static int game_mixwave_get(lua_State *L) { struct mixitem *i = lua_touserdata(L, 1); if(!strcmp(lua_tostring(L, 2), "volume")) { lua_pushnumber(L, ((struct k3MixWave*) i->thing)->volume); return 1; } else if(!strcmp(lua_tostring(L, 2), "loop")) { lua_pushboolean(L, ((struct k3MixWave*) i->thing)->loop); return 1; } else if(i->type == MIXITEM_POWER && !strcmp(lua_tostring(L, 2), "rms")) { lua_pushnumber(L, k3MixPowerMeasurementGetRMS(((struct k3MixWave*) i->thing))); return 1; } return 0; } static int dagame_mixwave_gc(lua_State *L) { struct mixitem *i = lua_touserdata(L, 1); if(i->type != MIXITEM_SOURCE) { struct k3MixWave *wav = i->thing; wav->close(wav); } return 0; } static int game_mixqueue(lua_State *L) { struct mixitem *i = lua_newuserdata(L, sizeof(*i)); i->thing = k3MixQueue(); i->type = MIXITEM_QUEUE; luaL_setmetatable(L, "k3mixqueue"); return 1; } static int game_queue_add(lua_State *L) { struct mixitem *i1 = lua_touserdata(L, 1); struct mixitem *i2 = lua_touserdata(L, 2); if(i1->type != MIXITEM_QUEUE) { lua_pushliteral(L, "Not queue."); lua_error(L); return 0; } if(lua_gettop(L) > 2) { lua_getfield(L, 3, "loop"); ((struct k3MixWave*) i2->thing)->loop = lua_toboolean(L, -1); lua_pop(L, 1); } else { ((struct k3MixWave*) i2->thing)->loop = 0; } k3MixQueueAdd(i1->thing, i2->thing); } static int game_queue_clear(lua_State *L) { struct k3MixWave **ud = lua_touserdata(L, 1); k3MixQueueClear(*ud); return 0; } static int game_queue_gc(lua_State *L) { struct k3MixWave **ud = lua_touserdata(L, 1); (*ud)->close(*ud); return 0; } static int game_ref(lua_State *L) { enum ResManType type; const char *ltype = lua_tolstring(L, 1, NULL); const char *meta = NULL; int sz; if(!strcmp(ltype, "stream")) { type = RESMAN_STREAM; meta = "k3mixwave"; sz = sizeof(struct mixitem); } else if(!strcmp(ltype, "mat")) { type = RESMAN_MATERIAL; meta = "k3mat"; sz = sizeof(void*); } else if(!strcmp(ltype, "mdl")) { type = RESMAN_MODEL; meta = "k3mdl"; sz = sizeof(void*); } else if(!strcmp(ltype, "tex")) { type = RESMAN_TEXTURE; meta = "k3tex"; sz = sizeof(void*); } else if(!strcmp(ltype, "physics")) { type = RESMAN_PHYSICS; meta = "k3physics"; sz = sizeof(void*); } else if(!strcmp(ltype, "font")) { type = RESMAN_FONT; meta = NULL; sz = sizeof(void*); } else return 0; const char *lname = lua_tostring(L, 2); void **ref = lua_newuserdata(L, sz); *ref = resman_ref(type, lname); if(type == RESMAN_STREAM) { ((struct mixitem*) ref)->type = MIXITEM_SOURCE; } if(meta) luaL_setmetatable(L, meta); return 1; } static int game_batch(lua_State *L) { void **r = lua_touserdata(L, 1); struct k3Mdl *mdl; if(luaL_testudata(L, 1, "k3water")) { mdl = ((struct k3Water*) *r)->mdl; } else if(luaL_testudata(L, 1, "k3mdl")) { mdl = *r; } else return 0; lua_rawgeti(L, 2, 1); lua_rawgeti(L, 2, 2); lua_rawgeti(L, 2, 3); mat4 m = GLM_MAT4_IDENTITY_INIT; glm_translate(m, (vec3) {lua_tonumber(L, -3), lua_tonumber(L, -2), lua_tonumber(L, -1)}); lua_pop(L, 3); if(lua_gettop(L) == 3) { mat4 anchor; for(int i = 0; i < 16; i++) { lua_rawgeti(L, 3, i + 1); ((float*) anchor)[i] = lua_tonumber(L, -1); } lua_pop(L, 16); glm_mat4_mul(anchor, m, m); } k3Batch(mdl, m, NULL); return 0; } static int game_get_component(lua_State *L) { if(!lua_isinteger(L, 1)) { return 0; } size_t idx = lua_tointeger(L, 1); const char *type = lua_tostring(L, 2); if(!strcmp(type, "physics")) { struct CPhysics *c = game_getcomponent(idx, physics); if(c) { struct CPhysics **lc = lua_newuserdata(L, sizeof(*lc)); *lc = c; luaL_setmetatable(L, "k3cphysics"); } else { lua_pushnil(L); } return 1; } else if(!strcmp(type, "render")) { struct CRender *c = game_getcomponent(idx, render); if(c) { struct CRender **lc = lua_newuserdata(L, sizeof(*lc)); *lc = c; luaL_setmetatable(L, "k3crender"); } else { lua_pushnil(L); } return 1; } else if(!strcmp(type, "boned")) { struct CBoned *c = game_getcomponent(idx, boned); if(c) { struct CBoned **lc = lua_newuserdata(L, sizeof(*lc)); *lc = c; luaL_setmetatable(L, "k3cboned"); } else { lua_pushnil(L); } return 1; } return 0; } static int game_submissive_assign(lua_State *L) { ENetPeer **ud = lua_touserdata(L, 1); net_server_assign(*ud, lua_tointeger(L, 2)); return 0; } static int game_submissive_load(lua_State *L) { ENetPeer **ud = lua_touserdata(L, 1); net_server_force_load(*ud, lua_tostring(L, 2)); return 0; } static int game_submissive_send(lua_State *L) { ENetPeer **ud = lua_touserdata(L, 1); struct bstr b; bstr(&b, 64); lua_pushvalue(L, 2); if(!serialize(&b)) { lua_pushliteral(L, "Object not serializable."); lua_error(L); return 0; } lua_pop(L, 1); net_server_send_msg(*ud, &b); free(b.data); return 0; } static int game_cphysics_get(lua_State *L) { struct CPhysics *c = *(struct CPhysics**) lua_touserdata(L, 1); const char *k = lua_tostring(L, 2); if(!strcmp(k, "trigger")) { lua_pushinteger(L, c->trigger); return 1; } else if(!strcmp(k, "type")) { lua_pushinteger(L, c->type); return 1; } else if(!strcmp(k, "dynamics")) { lua_pushinteger(L, c->dynamics); return 1; } else if(!strcmp(k, "pos")) { if(c->geom) { lua_newtable(L); const float *p = dGeomGetPosition(c->geom); lua_pushnumber(L, p[0]); lua_pushnumber(L, p[1]); lua_pushnumber(L, p[2]); lua_rawseti(L, -4, 3); lua_rawseti(L, -3, 2); lua_rawseti(L, -2, 1); } else { lua_pushnil(L); } return 1; } else if(!strcmp(k, "vel")) { lua_newtable(L); dBodyID bid = dGeomGetBody(c->geom); if(bid) { const float *v = dBodyGetLinearVel(bid); lua_pushnumber(L, v[0]); lua_pushnumber(L, v[1]); lua_pushnumber(L, v[2]); } else { lua_pushnumber(L, 0); lua_pushnumber(L, 0); lua_pushnumber(L, 0); } lua_rawseti(L, -4, 3); lua_rawseti(L, -3, 2); lua_rawseti(L, -2, 1); return 1; } else if(!strcmp(k, "body")) { if(c->geom) { dGeomID *g = lua_newuserdata(L, sizeof(*g)); *g = c->geom; luaL_setmetatable(L, "k3body"); } else { lua_pushnil(L); } return 1; } else if(!strcmp(k, "speed")) { lua_pushnumber(L, c->speed); return 1; } else if(!strcmp(k, "add_force")) { //lua_pushcfunction(L, ); return 1; } return 0; } static int game_cphysics_set(lua_State *L) { struct CPhysics *c = *(struct CPhysics**) lua_touserdata(L, 1); const char *k = lua_tostring(L, 2); if(!strcmp(k, "dynamics")) { // Only allow ghost changes, not static/kinematic if((c->dynamics ^ lua_tointeger(L, 3)) & CPHYSICS_GHOST) { c->dynamics = (c->dynamics & ~CPHYSICS_GHOST) | (lua_tointeger(L, 3) & CPHYSICS_GHOST); } } else if(!strcmp(k, "pos")) { lua_rawgeti(L, 3, 1); lua_rawgeti(L, 3, 2); lua_rawgeti(L, 3, 3); dGeomSetPosition(c->geom, lua_tonumber(L, -3), lua_tonumber(L, -2), lua_tonumber(L, -1)); lua_pop(L, 3); } else if(!strcmp(k, "rot")) { lua_rawgeti(L, 3, 1); lua_rawgeti(L, 3, 2); lua_rawgeti(L, 3, 3); lua_rawgeti(L, 3, 4); dGeomSetQuaternion(c->geom, (dQuaternion) {lua_tonumber(L, -1), lua_tonumber(L, -4), lua_tonumber(L, -3), lua_tonumber(L, -2)}); lua_pop(L, 4); } else if(!strcmp(k, "vel")) { dBodyID bid = dGeomGetBody(c->geom); if(bid) { lua_rawgeti(L, 3, 1); lua_rawgeti(L, 3, 2); lua_rawgeti(L, 3, 3); dBodySetLinearVel(bid, lua_tonumber(L, -3), lua_tonumber(L, -2), lua_tonumber(L, -1)); lua_pop(L, 3); } } else if(!strcmp(k, "speed")) { c->speed = lua_tonumber(L, 3); } return 0; } static int game_crender_get(lua_State *L) { struct CRender *c = *(struct CRender**) lua_touserdata(L, 1); const char *k = lua_tostring(L, 2); if(!strcmp(k, "mdl")) { lua_pushlstring(L, c->mdl, strnlen(c->mdl, MODELNAME_LENGTH)); return 1; } else if(!strcmp(k, "mdlcache")) { struct k3Mdl **cache = lua_newuserdata(L, sizeof(*cache)); *cache = c->cache; luaL_setmetatable(L, "k3mdl"); return 1; } else if(!strcmp(k, "bones")) { lua_newtable(L); return 1; } return 0; } static int game_cboned_get(lua_State *L) { struct CBoned *c = *(struct CBoned**) lua_touserdata(L, 1); const char *k = lua_tostring(L, 2); if(!strcmp(k, "bones")) { lua_newtable(L); for(size_t i = 0; i < c->size; i++) { lua_newtable(L); lua_newtable(L); lua_pushnumber(L, c->bones[i].translation[0]); lua_pushnumber(L, c->bones[i].translation[1]); lua_pushnumber(L, c->bones[i].translation[2]); lua_pushnumber(L, c->bones[i].translation[3]); lua_rawseti(L, -5, 4); lua_rawseti(L, -4, 3); lua_rawseti(L, -3, 2); lua_rawseti(L, -2, 1); lua_rawseti(L, -2, 1); lua_newtable(L); lua_pushnumber(L, c->bones[i].rotation[0]); lua_pushnumber(L, c->bones[i].rotation[1]); lua_pushnumber(L, c->bones[i].rotation[2]); lua_pushnumber(L, c->bones[i].rotation[3]); lua_rawseti(L, -5, 4); lua_rawseti(L, -4, 3); lua_rawseti(L, -3, 2); lua_rawseti(L, -2, 1); lua_rawseti(L, -2, 2); lua_rawseti(L, -2, i + 1); } return 1; } else if(!strcmp(k, "anim")) { struct CBoned **c2 = lua_newuserdata(L, sizeof(*c2)); *c2 = c; luaL_setmetatable(L, "k3cbonedanim"); return 1; } return 0; } static int game_cboned_set(lua_State *L) { struct CBoned *c = *(struct CBoned**) lua_touserdata(L, 1); const char *k = lua_tostring(L, 2); if(!strcmp(k, "bones")) { size_t newlen = luaL_len(L, 3); if(newlen != c->size) { c->bones = realloc(c->bones, sizeof(*c->bones) * newlen); } for(size_t i = 0; i < newlen; i++) { lua_rawgeti(L, 3, i + 1); lua_rawgeti(L, -1, 1); lua_rawgeti(L, -1, 1); lua_rawgeti(L, -2, 2); lua_rawgeti(L, -3, 3); lua_rawgeti(L, -4, 4); c->bones[i].translation[0] = lua_tonumber(L, -4); c->bones[i].translation[1] = lua_tonumber(L, -3); c->bones[i].translation[2] = lua_tonumber(L, -2); c->bones[i].translation[3] = lua_tonumber(L, -1); lua_pop(L, 5); lua_rawgeti(L, -1, 2); lua_rawgeti(L, -1, 1); lua_rawgeti(L, -2, 2); lua_rawgeti(L, -3, 3); lua_rawgeti(L, -4, 4); c->bones[i].rotation[0] = lua_tonumber(L, -4); c->bones[i].rotation[1] = lua_tonumber(L, -3); c->bones[i].rotation[2] = lua_tonumber(L, -2); c->bones[i].rotation[3] = lua_tonumber(L, -1); lua_pop(L, 5); lua_pop(L, 1); } } return 0; } static int game_cbonedanim_set(lua_State *L) { struct CBoned *c = *(struct CBoned**) lua_touserdata(L, 1); const char *k = lua_tostring(L, 2); if(!strcmp(k, "id")) { c->anim.id = lua_tointeger(L, 3); c->anim.cache.base = NULL; } else if(!strcmp(k, "standard")) { c->anim.standard = lua_toboolean(L, 3); } return 0; } int LuaapiCamFocus; vec3 LuaapiCamFocusPos; vec3 LuaapiCamFocusDir; int LuaapiFirstPerson; static int game_camfocus(lua_State *L) { if(lua_gettop(L) < 3 || lua_isnil(L, 2) || lua_isnil(L, 3)) { LuaapiCamFocus = 0; } else if(lua_gettop(L) == 3) { LuaapiCamFocus = 1; lua_rawgeti(L, 2, 1); lua_rawgeti(L, 2, 2); lua_rawgeti(L, 2, 3); LuaapiCamFocusPos[0] = lua_tonumber(L, -3); LuaapiCamFocusPos[1] = lua_tonumber(L, -2); LuaapiCamFocusPos[2] = lua_tonumber(L, -1); lua_rawgeti(L, 3, 1); lua_rawgeti(L, 3, 2); lua_rawgeti(L, 3, 3); LuaapiCamFocusDir[0] = lua_tonumber(L, -3); LuaapiCamFocusDir[1] = lua_tonumber(L, -2); LuaapiCamFocusDir[2] = lua_tonumber(L, -1); lua_pop(L, 6); } return 0; } #define MENUITEM_SCREEN 0 #define MENUITEM_LABEL 1 #define MENUITEM_TEXTBUTTON 2 #define MENUITEM_TEXTINPUT 3 struct menuitem { int type; struct k3MObj *ptr; }; mat4 LuaapiCamMatrix; static int game_get(lua_State *L) { const char *i = lua_tostring(L, 2); if(!strcmp(i, "mp")) { lua_pushboolean(L, Game.isMultiplayer); } else if(!strcmp(i, "authority")) { lua_pushboolean(L, Game.isAuthority); } else if(!strcmp(i, "spectated")) { lua_pushinteger(L, Game.spectated); } else if(!strcmp(i, "fov")) { lua_pushnumber(L, LuaapiFov); } else if(!strcmp(i, "rate")) { lua_pushinteger(L, GAME_TPS); } else if(!strcmp(i, "camera")) { lua_newtable(L); for(int i = 0; i < 16; i++) { lua_pushnumber(L, ((float*) LuaapiCamMatrix)[i]); lua_rawseti(L, -2, i + 1); } } else if(!strcmp(i, "menu")) { if(UiActive) { struct menuitem *item = lua_newuserdata(L, sizeof(*item)); item->type = MENUITEM_SCREEN; item->ptr = UiActive; } else { lua_pushnil(L); } } else { lua_pushvalue(L, 2); lua_rawget(L, 1); } return 1; } static int game_set(lua_State *L) { const char *i = lua_tostring(L, 2); if(!strcmp(i, "spectated")) { Game.spectated = lua_isnil(L, 3) ? ENT_ID_INVALID : lua_tointeger(L, 3); } else if(!strcmp(i, "fov")) { LuaapiFov = lua_tonumber(L, 3); } else if(!strcmp(i, "menu")) { struct menuitem *item = lua_touserdata(L, 3); UiActive = item ? item->ptr : NULL; set_ui_mode(!!UiActive); } else { lua_pushvalue(L, 2); lua_pushvalue(L, 3); lua_rawset(L, 1); } return 0; } static int game_k3mdl_addmesh(lua_State *L) { struct k3Mdl *mdl = *(struct k3Mdl**) lua_touserdata(L, 1); lua_getfield(L, 2, "material"); struct k3Mat mat; memset(&mat, 0, sizeof(mat)); luaapi_fillmaterial_direct(&mat); lua_getfield(L, 2, "start"); size_t start = lua_tointeger(L, -1); lua_getfield(L, 2, "count"); size_t count = lua_tointeger(L, -1); lua_pop(L, 3); k3MdlAddMesh(mdl, &mat, start, count); } static int game_k3mdl_get(lua_State *L) { struct k3Mdl *mdl = *(struct k3Mdl**) lua_touserdata(L, 1); size_t count; struct k3Mesh *meshes = k3MdlGetMeshes(mdl, &count); if(lua_type(L, 2) == LUA_TNUMBER) { lua_Integer i = lua_tointeger(L, 2); if(i >= 1 && i <= count) { struct k3Mat **m = lua_newuserdata(L, sizeof(*m)); *m = &meshes[i - 1].mat; luaL_setmetatable(L, "k3mat"); } else { lua_pushnil(L); } } else if(!strcmp(lua_tostring(L, 2), "addmesh")) { lua_pushcfunction(L, game_k3mdl_addmesh); } return 1; } static int game_k3mat_get(lua_State *L) { struct k3Mat *mat = *(struct k3Mat**) lua_touserdata(L, 1); const char *k = lua_tostring(L, 2); if(!strcmp(k, "uniforms")) { if(mat->passes[0].glsl.hp) { struct k3Mat **again = lua_newuserdata(L, sizeof(*again)); *again = mat; luaL_setmetatable(L, "k3matuniforms"); } else { lua_pushnil(L); } } else { lua_pushnil(L); } return 1; } static int game_k3matuniforms_set(lua_State *L) { struct k3Mat *mat = *(struct k3Mat**) lua_touserdata(L, 1); const char *key = lua_tostring(L, 2); for(int i = 0; i < mat->passes[0].glsl.uCount; i++) { if(!strncmp(key, mat->passes[0].glsl.u[i].name, sizeof(mat->passes[0].glsl.u[i].name))) { if(mat->passes[0].glsl.u[i].type == k3_MAT_UNIFORM_I1) { mat->passes[0].glsl.u[i].i1 = lua_tointeger(L, 3); } else if(mat->passes[0].glsl.u[i].type == k3_MAT_UNIFORM_F1) { mat->passes[0].glsl.u[i].f1 = lua_tonumber(L, 3); } } } return 0; } static int game_k3matuniforms_get(lua_State *L) { struct k3Mat *mat = *(struct k3Mat**) lua_touserdata(L, 1); const char *key = lua_tostring(L, 2); for(int i = 0; i < mat->passes[0].glsl.uCount; i++) { if(!strncmp(key, mat->passes[0].glsl.u[i].name, sizeof(mat->passes[0].glsl.u[i].name))) { if(mat->passes[0].glsl.u[i].type == k3_MAT_UNIFORM_I1) { lua_pushinteger(L, mat->passes[0].glsl.u[i].i1); return 1; } else if(mat->passes[0].glsl.u[i].type == k3_MAT_UNIFORM_F1) { lua_pushnumber(L, mat->passes[0].glsl.u[i].f1); return 1; } } } return 0; } static size_t moduleCount; static struct Module { //uint64_t hash; char *name; int ref; } *modules; static int luaapi_require(lua_State *L) { const char *name = lua_tostring(L, 1); for(size_t i = 0; i < moduleCount; i++) { if(!strcmp(modules[i].name, name)) { lua_rawgeti(L, LUA_REGISTRYINDEX, modules[i].ref); return 1; } } char buf[128]; snprintf(buf, sizeof(buf), "%s.lua", name); buf[127] = 0; for(size_t i = 0; buf[i] && i < strlen(buf) - 4; i++) { if(buf[i] == '.') buf[i] = '/'; } struct ResManBin *bin = resman_ref(RESMAN_BIN, buf); if(luaL_dostring(L, bin->data)) { puts(lua_tolstring(L, -1, NULL)); lua_pop(L, 1); } resman_unref(RESMAN_BIN, buf); lua_pushvalue(L, -1); modules = realloc(modules, sizeof(*modules) * (moduleCount + 1)); modules[moduleCount++] = (struct Module) { .name = strdup(name), .ref = luaL_ref(L, LUA_REGISTRYINDEX), }; return 1; } static int game_load(lua_State *L) { luaapi_load(lua_tostring(L, 1)); return 0; } static int dagame_cleanup(lua_State *L) { luaapi_cleanup(); return 0; } static int dagame_joint(lua_State *L) { lua_getfield(L, 1, "type"); const char *type = lua_tostring(L, -1); lua_pop(L, 1); if(!strcmp(type, "ballsocket")) { lua_geti(L, 1, 1); lua_geti(L, 1, 2); dGeomID *gid1 = lua_touserdata(L, -2); dGeomID *gid2 = lua_touserdata(L, -1); lua_pop(L, 2); dBodyID bid1 = dGeomGetBody(*gid1); dBodyID bid2 = dGeomGetBody(*gid2); if(bid1 && bid2) { dJointID jid = dJointCreateBall(Game.phys, 0); lua_getfield(L, 1, "anchor"); lua_rawgeti(L, -1, 1); lua_rawgeti(L, -2, 2); lua_rawgeti(L, -3, 3); dJointSetBallAnchor(jid, lua_tonumber(L, -3), lua_tonumber(L, -2), lua_tonumber(L, -1)); lua_pop(L, 4); dJointAttach(jid, bid1, bid2); } } else if(!strcmp(type, "slider")) { lua_geti(L, 1, 1); lua_geti(L, 1, 2); dGeomID *gid1 = lua_touserdata(L, -2); dGeomID *gid2 = lua_touserdata(L, -1); lua_pop(L, 2); dBodyID bid1 = dGeomGetBody(*gid1); dBodyID bid2 = dGeomGetBody(*gid2); if(bid1 && bid2) { dJointID jid = dJointCreateSlider(Game.phys, 0); dJointAttach(jid, bid1, bid2); lua_getfield(L, 1, "axis"); lua_rawgeti(L, -1, 1); lua_rawgeti(L, -2, 2); lua_rawgeti(L, -3, 3); dJointSetSliderAxis(jid, lua_tonumber(L, -3), lua_tonumber(L, -2), lua_tonumber(L, -1)); lua_pop(L, 4); } } return 0; } static int dagame_setphys(lua_State *L) { game_setphys(*(struct TrimeshData**) lua_touserdata(L, 1)); return 0; } static int dagame_controls_get(lua_State *L) { const char *k = lua_tostring(L, 2); int btn = k4_control_get_by_name(k); if(btn == -1) { lua_pushnil(L); } else { lua_pushinteger(L, btn); } return 1; } static int dagame_controls_set(lua_State *L) { const char *k = lua_tostring(L, 2); int btn; // lua_isstring returns true for numbers because yes if(lua_type(L, 3) == LUA_TSTRING) { btn = lua_tostring(L, 3)[0]; } else { btn = lua_tointeger(L, 3); } k4_control_set(k, btn); return 0; } static int dagame_mdl(lua_State *L) { vec3 *positions = NULL; int8_t *normals = NULL; uint16_t *indices = NULL; vec2 *uvs = NULL; size_t vertexCount = 0; size_t indexCount = 0; lua_getfield(L, 1, "positions"); lua_len(L, -1); vertexCount = lua_tointeger(L, -1) / 3; lua_pop(L, 1); positions = malloc(sizeof(*positions) * vertexCount); for(size_t i = 0; i < vertexCount; i++) { lua_geti(L, -1, i * 3 + 1); lua_geti(L, -2, i * 3 + 2); lua_geti(L, -3, i * 3 + 3); positions[i][0] = lua_tonumber(L, -3); positions[i][1] = lua_tonumber(L, -2); positions[i][2] = lua_tonumber(L, -1); lua_pop(L, 3); } lua_pop(L, 1); lua_getfield(L, 1, "indices"); lua_len(L, -1); indexCount = lua_tointeger(L, -1); lua_pop(L, 1); indices = malloc(sizeof(*indices) * indexCount); for(size_t i = 0; i < indexCount; i++) { lua_geti(L, -1, i + 1); indices[i] = lua_tointeger(L, -1); if(indices[i] >= vertexCount) { k3Log(k3_ERR, "Index goes over vertex count!"); } lua_pop(L, 1); } lua_pop(L, 1); lua_getfield(L, 1, "normals"); lua_len(L, -1); size_t c = lua_tointeger(L, -1) / 3; lua_pop(L, 1); if(c != vertexCount) { // TODO: free return 0; } normals = malloc(sizeof(*normals) * 3 * vertexCount); for(size_t i = 0; i < vertexCount; i++) { lua_geti(L, -1, i * 3 + 1); lua_geti(L, -2, i * 3 + 2); lua_geti(L, -3, i * 3 + 3); normals[i * 3 + 0] = lua_tonumber(L, -3) * 127; normals[i * 3 + 1] = lua_tonumber(L, -2) * 127; normals[i * 3 + 2] = lua_tonumber(L, -1) * 127; lua_pop(L, 3); } lua_pop(L, 1); lua_getfield(L, 1, "uvs"); lua_len(L, -1); c = lua_tointeger(L, -1) / 2; lua_pop(L, 1); if(c != vertexCount) { // TODO: free return 0; } uvs = malloc(sizeof(*uvs) * vertexCount); for(size_t i = 0; i < vertexCount; i++) { lua_geti(L, -1, i * 2 + 1); lua_geti(L, -2, i * 2 + 2); uvs[i][0] = lua_tonumber(L, -2); uvs[i][1] = lua_tonumber(L, -1); lua_pop(L, 2); } lua_pop(L, 1); struct k3Mdl *mdl = k3MdlCreate(vertexCount, indexCount, 0, positions, normals, uvs, NULL, NULL, NULL, indices, NULL, NULL); struct k3Mdl **ud = lua_newuserdata(L, sizeof(*ud)); *ud = mdl; luaL_setmetatable(L, "k3mdl"); return 1; } static int dagame_trimesh(lua_State *L) { lua_getfield(L, 1, "indices"); lua_len(L, -1); size_t indexCount = lua_tointeger(L, -1); lua_pop(L, 1); int *indices = malloc(sizeof(*indices) * indexCount); for(size_t i = 0; i < indexCount; i++) { lua_geti(L, -1, i + 1); indices[i] = lua_tointeger(L, -1); lua_pop(L, 1); } lua_pop(L, 1); lua_getfield(L, 1, "positions"); lua_len(L, -1); size_t vertexCount = lua_tointeger(L, -1) / 3; lua_pop(L, 1); vec3 *positions = malloc(sizeof(*positions) * vertexCount); for(size_t i = 0; i < vertexCount; i++) { lua_geti(L, -1, i * 3 + 1); lua_geti(L, -2, i * 3 + 2); lua_geti(L, -3, i * 3 + 3); positions[i][0] = lua_tonumber(L, -3); positions[i][1] = lua_tonumber(L, -2); positions[i][2] = lua_tonumber(L, -1); lua_pop(L, 3); } lua_pop(L, 1); dTriMeshDataID trid = dGeomTriMeshDataCreate(); dGeomTriMeshDataBuildSingle(trid, positions, sizeof(vec3), vertexCount, indices, indexCount, 3 * sizeof(int)); struct TrimeshData *ret = malloc(sizeof(*ret)); ret->vdata = positions; ret->idata = indices; ret->trid = trid; struct TrimeshData **ud = lua_newuserdata(L, sizeof(*ud)); *ud = ret; luaL_setmetatable(L, "k3physics"); return 1; } static int dagame_firstperson(lua_State *L) { LuaapiFirstPerson = lua_toboolean(L, 1); return 0; } static int dagame_ray(lua_State *L) { struct LocalRay *lr = lua_newuserdata(L, sizeof(*lr)); luaL_setmetatable(L, "k4ray"); struct CPhysics *cp = game_getcomponent(Game.spectated, physics); struct CPlayerCtrl *cc = game_getcomponent(Game.spectated, playerctrl); memcpy(lr->pos, dGeomGetPosition(cp->geom), sizeof(vec3)); lr->dir[0] = 0; lr->dir[1] = 0; lr->dir[2] = -1; glm_vec3_rotate(lr->dir, cc->pitch, (vec3) {1, 0, 0}); glm_vec3_rotate(lr->dir, cc->yaw, (vec3) {0, 1, 0}); lr->ignore = Game.spectated; lr->hit = ENT_ID_INVALID; lr->out[0] = NAN; lr->out[1] = NAN; lr->out[2] = NAN; lr->maxlen = lua_type(L, 1) == LUA_TNUMBER ? lua_tonumber(L, 1) : 3; game_raycast(lr, 1); return 1; } static int dagame_ray_get(lua_State *L) { struct LocalRay *lr = luaL_checkudata(L, 1, "k4ray"); const char *key = lua_tostring(L, 2); if(!strcmp(key, "pos")) { lua_newtable(L); lua_pushnumber(L, lr->out[0]); lua_rawseti(L, -2, 1); lua_pushnumber(L, lr->out[1]); lua_rawseti(L, -2, 2); lua_pushnumber(L, lr->out[2]); lua_rawseti(L, -2, 3); return 1; } else if(!strcmp(key, "dir")) { lua_newtable(L); lua_pushnumber(L, lr->dir[0]); lua_rawseti(L, -2, 1); lua_pushnumber(L, lr->dir[1]); lua_rawseti(L, -2, 2); lua_pushnumber(L, lr->dir[2]); lua_rawseti(L, -2, 3); return 1; } else if(!strcmp(key, "hit")) { if(lr->hit == ENT_ID_INVALID) { lua_pushnil(L); } else { lua_pushinteger(L, lr->hit); } return 1; } else if(!strcmp(key, "ready")) { lua_pushboolean(L, !isnanf(lr->out[0])); return 1; } return 0; } #include"k3font.h" static int dagame_draw(lua_State *L) { lua_getfield(L, 1, "fill"); if(lua_toboolean(L, -1)) { lua_pop(L, 1); lua_getfield(L, 1, "tex"); lua_getfield(L, 1, "sx"); lua_getfield(L, 1, "sy"); lua_getfield(L, 1, "sw"); lua_getfield(L, 1, "sh"); lua_getfield(L, 1, "x"); lua_getfield(L, 1, "y"); lua_getfield(L, 1, "w"); lua_getfield(L, 1, "h"); lua_getfield(L, 1, "r"); lua_getfield(L, 1, "g"); lua_getfield(L, 1, "b"); lua_getfield(L, 1, "a"); void *texptr = lua_touserdata(L, -13); struct k3Tex *tex = texptr ? *(void**)texptr : NULL; float sx = lua_type(L, -12) == LUA_TNUMBER ? lua_tonumber(L, -12) : 0; float sy = lua_type(L, -11) == LUA_TNUMBER ? lua_tonumber(L, -11) : 0; float sw = lua_type(L, -10) == LUA_TNUMBER ? lua_tonumber(L, -10) : 1; float sh = lua_type(L, -9) == LUA_TNUMBER ? lua_tonumber(L, -9) : 1; float x = lua_tonumber(L, -8); float y = lua_tonumber(L, -7); float w = lua_tonumber(L, -6); float h = lua_tonumber(L, -5); float r = lua_type(L, -4) == LUA_TNUMBER ? lua_tonumber(L, -4) : 1; float g = lua_type(L, -3) == LUA_TNUMBER ? lua_tonumber(L, -3) : 1; float b = lua_type(L, -2) == LUA_TNUMBER ? lua_tonumber(L, -2) : 1; float a = lua_type(L, -1) == LUA_TNUMBER ? lua_tonumber(L, -1) : 1; k3BatchAdd(tex, (struct k3RectF) {.x = sx, .y = sy, .w = sw, .h = sh}, (struct k3RectF) {.x = x, .y = y, .w = w, .h = h}, 0, (vec4) {r, g, b, a}); lua_pop(L, 13); } else { lua_pop(L, 1); lua_getfield(L, 1, "align"); lua_getfield(L, 1, "r"); lua_getfield(L, 1, "g"); lua_getfield(L, 1, "b"); lua_getfield(L, 1, "a"); lua_getfield(L, 1, "sz"); lua_getfield(L, 1, "text"); lua_getfield(L, 1, "x"); lua_getfield(L, 1, "y"); lua_getfield(L, 1, "font"); struct k3Font *font = *(void**) lua_touserdata(L, 1); struct k3RectF rect; k3FontSz(font, lua_tonumber(L, -4), lua_tostring(L, -3), &rect); if(!strcmp(lua_tostring(L, -9), "bottommiddle")) { rect.x -= rect.w / 2; } else if(!strcmp(lua_tostring(L, -9), "bottomright")) { rect.x -= rect.w; } else if(!strcmp(lua_tostring(L, -9), "middlemiddle")) { rect.x -= rect.w / 2; rect.y -= rect.h / 2; } k3FontDraw(font, lua_tonumber(L, -2) + rect.x, lua_tonumber(L, -1) + rect.y, lua_tonumber(L, -4), lua_tostring(L, -3), (vec4) {lua_tonumber(L, -8), lua_tonumber(L, -7), lua_tonumber(L, -6), lua_tonumber(L, -5)}); lua_pop(L, 9); } return 0; } struct k3Tex *LuaapiSkybox; vec4 LuaapiSkyboxRotation; bool LuaapiSkyboxRotationFunky; static int dagame_skybox(lua_State *L) { LuaapiSkybox = *(struct k3Tex**) lua_touserdata(L, 1); if(lua_gettop(L) >= 2) { lua_geti(L, 2, 1); lua_geti(L, 2, 2); lua_geti(L, 2, 3); lua_geti(L, 2, 4); LuaapiSkyboxRotation[0] = lua_tonumber(L, -4); LuaapiSkyboxRotation[1] = lua_tonumber(L, -3); LuaapiSkyboxRotation[2] = lua_tonumber(L, -2); LuaapiSkyboxRotation[3] = lua_tonumber(L, -1); lua_pop(L, 4); if(lua_gettop(L) == 3) { LuaapiSkyboxRotationFunky = lua_toboolean(L, 3); } } else { glm_vec4_zero(LuaapiSkyboxRotation); LuaapiSkyboxRotationFunky = false; } } static int dagame_send(lua_State *L) { struct bstr b; bstr(&b, 64); lua_pushvalue(L, 1); if(!serialize(&b)) { lua_pushliteral(L, "Object not serializable."); lua_error(L); return 0; } lua_pop(L, 1); if(Game.isAuthority) { net_server_broadcast_msg(&b); } else { net_client_send_msg(&b); } free(b.data); return 0; } static int dagame_ui_screen(lua_State *L) { struct menuitem *item = lua_newuserdata(L, sizeof(*item)); item->type = MENUITEM_SCREEN; item->ptr = (void*) k3MScreen(); luaL_setmetatable(L, "k3menuitem"); return 1; } static int dagame_ui_label(lua_State *L) { struct menuitem *item = lua_newuserdata(L, sizeof(*item)); item->type = MENUITEM_LABEL; item->ptr = (void*) k3MLabel(*(void**) lua_touserdata(L, 1), lua_tonumber(L, 2), strdup(lua_tostring(L, 3))); luaL_setmetatable(L, "k3menuitem"); return 1; } static int dagame_ui_textbutton(lua_State *L) { struct menuitem *item = lua_newuserdata(L, sizeof(*item)); item->type = MENUITEM_TEXTBUTTON; item->ptr = (void*) k3MTextButton(*(void**) lua_touserdata(L, 1), lua_tonumber(L, 2), strdup(lua_tostring(L, 3))); luaL_setmetatable(L, "k3menuitem"); return 1; } static int dagame_ui_textinput(lua_State *L) { struct menuitem *item = lua_newuserdata(L, sizeof(*item)); item->type = MENUITEM_TEXTINPUT; item->ptr = (void*) k3MTextInput(*(void**) lua_touserdata(L, 1), lua_tonumber(L, 2), strdup(lua_tostring(L, 3)), strdup(lua_tostring(L, 4))); luaL_setmetatable(L, "k3menuitem"); return 1; } static int dagame_k3menuitem_set_bounds(lua_State *L) { struct menuitem *item = lua_touserdata(L, 1); item->ptr->x = lua_tonumber(L, 2); item->ptr->y = lua_tonumber(L, 3); item->ptr->w = lua_tonumber(L, 4); item->ptr->h = lua_tonumber(L, 5); lua_pushvalue(L, 1); return 1; } static int dagame_k3menuitem_add_child(lua_State *L) { struct menuitem *parent = lua_touserdata(L, 1); struct menuitem *child = lua_touserdata(L, 2); k3MAddChild(parent->ptr, child->ptr); lua_pushvalue(L, 1); return 1; } static bool k3menuitem_event_callback(struct k3MEvent *ev, uint8_t *ud_) { void **ud = ud_; lua_State *L = ud[0]; intptr_t luaref = (intptr_t) ud[1]; int oldtop = lua_gettop(L); lua_rawgeti(L, LUA_REGISTRYINDEX, luaref); if(lua_pcall(L, 0, 1, 0) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 1); return false; } bool handled = lua_toboolean(L, -1); lua_pop(L, 1); assert(lua_gettop(L) == oldtop); return handled; } static int dagame_k3menuitem_on(lua_State *L) { struct menuitem *item = lua_touserdata(L, 1); uint16_t evcode = 0xFFFF; if(!strcmp(lua_tostring(L, 2), "mouse_enter")) { evcode = k3M_EVENT_MOUSE_ENTER; } else if(!strcmp(lua_tostring(L, 2), "mouse_motion")) { evcode = k3M_EVENT_MOUSE_MOTION; } else if(!strcmp(lua_tostring(L, 2), "mouse_leave")) { evcode = k3M_EVENT_MOUSE_RELEASE; } else if(!strcmp(lua_tostring(L, 2), "mouse_press")) { evcode = k3M_EVENT_MOUSE_PRESS; } else if(!strcmp(lua_tostring(L, 2), "mouse_release")) { evcode = k3M_EVENT_MOUSE_RELEASE; } else if(!strcmp(lua_tostring(L, 2), "click")) { evcode = k3M_EVENT_MOUSE_CLICK; } else if(!strcmp(lua_tostring(L, 2), "key_press")) { evcode = k3M_EVENT_KEY_PRESS; } else if(!strcmp(lua_tostring(L, 2), "key_release")) { evcode = k3M_EVENT_KEY_RELEASE; } else if(!strcmp(lua_tostring(L, 2), "input")) { evcode = k3M_EVENT_INPUT; } else if(!strcmp(lua_tostring(L, 2), "draw")) { evcode = k3M_EVENT_DRAW; } else if(!strcmp(lua_tostring(L, 2), "complete")) { evcode = k3M_EVENT_COMPLETE; } else if(!strcmp(lua_tostring(L, 2), "all")) { evcode = k3M_EVENT_ALL; } if(evcode != 0xFFFF) { lua_pushvalue(L, 3); void *ud[2] = {L, (intptr_t) luaL_ref(L, LUA_REGISTRYINDEX)}; k3MRegisterEventHandler(item->ptr, evcode, k3menuitem_event_callback, ud, sizeof(ud)); } lua_pushvalue(L, 1); return 1; } static int dagame_k3menuitem_get(lua_State *L) { struct menuitem *item = lua_touserdata(L, 1); if(!strcmp(lua_tostring(L, 2), "invisible")) { lua_pushboolean(L, item->ptr->invisible); } else if(!strcmp(lua_tostring(L, 2), "disabled")) { lua_pushboolean(L, item->ptr->disabled); } else if(!strcmp(lua_tostring(L, 2), "set_bounds")) { lua_pushcfunction(L, dagame_k3menuitem_set_bounds); } else if(!strcmp(lua_tostring(L, 2), "add_child")) { lua_pushcfunction(L, dagame_k3menuitem_add_child); } else if(!strcmp(lua_tostring(L, 2), "on")) { lua_pushcfunction(L, dagame_k3menuitem_on); } else if(!strcmp(lua_tostring(L, 2), "text")) { char *txt = NULL; if(item->type == MENUITEM_LABEL) { txt = ((struct k3MLabel*) item->ptr)->txt; } else if(item->type == MENUITEM_TEXTBUTTON) { txt = ((struct k3MTextButton*) item->ptr)->txt; } else if(item->type == MENUITEM_TEXTINPUT) { txt = ((struct k3MTextInput*) item->ptr)->txt; } if(txt) { lua_pushstring(L, txt); } else { lua_pushnil(L); } } else lua_pushnil(L); return 1; } static int dagame_k3menuitem_set(lua_State *L) { struct menuitem *item = lua_touserdata(L, 1); if(!strcmp(lua_tostring(L, 2), "invisible")) { item->ptr->invisible = lua_toboolean(L, 3); } else if(!strcmp(lua_tostring(L, 2), "disabled")) { item->ptr->disabled = lua_toboolean(L, 3); } else if(!strcmp(lua_tostring(L, 2), "text")) { char **txt = NULL; if(item->type == MENUITEM_LABEL) { txt = &((struct k3MLabel*) item->ptr)->txt; } else if(item->type == MENUITEM_TEXTBUTTON) { txt = &((struct k3MTextButton*) item->ptr)->txt; } else if(item->type == MENUITEM_TEXTINPUT) { txt = &((struct k3MTextInput*) item->ptr)->txt; } if(txt) { if(*txt) { free(*txt); } const char *new = lua_tostring(L, 3); *txt = strdup(new ? new : "nil"); } } return 0; } static int dagame_set_reduction(lua_State *L) { float f = lua_type(L, 1) == LUA_TNUMBER ? lua_tonumber(L, 1) : 1; k4_set_reduction(f); return 0; } static int dagame_set_texture_reduction(lua_State *L) { int i = lua_type(L, 1) == LUA_TNUMBER ? lua_tointeger(L, 1) : 1; k4_set_texture_reduction(i); return 0; } int PeercodeHandler = LUA_NOREF; static int dagame_net_gen_peercode(lua_State *L) { if(NetWrap.stage != 0 || PeercodeHandler != LUA_NOREF) { lua_pushliteral(L, "Peercode generation already in progress"); lua_error(L); return 0; } NetWrap.stoon = stoon_init("stun.easybell.de", 3478, 26656); NetWrap.stage = 1; lua_pushvalue(L, 1); PeercodeHandler = luaL_ref(L, LUA_REGISTRYINDEX); return 0; } static int dagame_clipboard_set(lua_State *L) { const char *k = lua_tostring(L, 2); if(!strcmp(k, "text")) { k4_set_clipboard_text(lua_tostring(L, 3)); } return 0; } static int dagame_clipboard_get(lua_State *L) { const char *k = lua_tostring(L, 2); if(!strcmp(k, "text")) { const char *str = k4_get_clipboard_text(); if(str) { lua_pushstring(L, str); } else { lua_pushnil(L); } return 1; } return 0; } #define CONN_INVALID_PEERCODE 1 static int conn(const char *peercode, bool host) { for(int i = 0; i < STOON_CONN_INFO_SIZE; i++) { int b = 0; if(peercode[i * 2 + 0] >= '0' && peercode[i * 2 + 0] <= '9') { b |= (peercode[i * 2 + 0] - '0') << 4; } else if(peercode[i * 2 + 0] >= 'a' && peercode[i * 2 + 0] <= 'f') { b |= (peercode[i * 2 + 0] - 'a' + 10) << 4; } else return CONN_INVALID_PEERCODE; if(peercode[i * 2 + 1] >= '0' && peercode[i * 2 + 1] <= '9') { b |= (peercode[i * 2 + 1] - '0') << 0; } else if(peercode[i * 2 + 1] >= 'a' && peercode[i * 2 + 1] <= 'f') { b |= (peercode[i * 2 + 1] - 'a' + 10) << 0; } else return CONN_INVALID_PEERCODE; NetWrap.otherpeer[i] = b; } NetWrap.isHost = host; NetWrap.stage = 3; return 0; } static int dagame_net_host(lua_State *L) { int status = conn(lua_tostring(L, 1), true); lua_pushboolean(L, !status); if(status) { if(status == CONN_INVALID_PEERCODE) { lua_pushliteral(L, "peercode"); } else { lua_pushliteral(L, "unknown"); } return 2; } return 1; } static int dagame_net_join(lua_State *L) { int status = conn(lua_tostring(L, 1), false); lua_pushboolean(L, !status); if(status) { if(status == CONN_INVALID_PEERCODE) { lua_pushliteral(L, "peercode"); } else { lua_pushliteral(L, "unknown"); } return 2; } return 1; } #include static int os_time(lua_State *L) { lua_pushnumber(L, glfwGetTime() - LuaapiStartTime); return 1; } void luaapi_init() { k3Log(k3_DEBUG, "Creating Lua environment"); L = luaL_newstate(); /*lua_newtable(L); lua_newtable(L); lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); lua_setfield(L, -2, "__index"); lua_setmetatable(L, -2); consoleEnvironment = luaL_ref(L, LUA_REGISTRYINDEX);*/ k3Log(k3_DEBUG, "Loading Lua stdlib"); luaL_requiref(L, "_G", luaopen_base, 1); luaL_requiref(L, LUA_TABLIBNAME, luaopen_table, 1); luaL_requiref(L, LUA_STRLIBNAME, luaopen_string, 1); luaL_requiref(L, LUA_MATHLIBNAME, luaopen_math, 1); luaL_requiref(L, LUA_DBLIBNAME, luaopen_debug, 1); lua_pop(L, 5); k3Log(k3_DEBUG, "Setting custom require"); lua_pushcfunction(L, luaapi_require); lua_setglobal(L, "require"); k3Log(k3_DEBUG, "Creating table game"); lua_newtable(L); lua_pushcfunction(L, game_addentity); lua_setfield(L, -2, "addentity"); k3Log(k3_DEBUG, "Creating table game.triggers"); lua_newtable(L); lua_pushvalue(L, -1); gametriggersref = luaL_ref(L, LUA_REGISTRYINDEX); lua_newtable(L); lua_pushcfunction(L, game_triggers_set); lua_setfield(L, -2, "__newindex"); lua_pushcfunction(L, game_triggers_get); lua_setfield(L, -2, "__index"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_setmetatable(L, -2); lua_setfield(L, -2, "triggers"); k3Log(k3_DEBUG, "Creating table game.conveyors"); lua_newtable(L); lua_newtable(L); lua_pushcfunction(L, game_conveyors_set); lua_setfield(L, -2, "__newindex"); lua_pushcfunction(L, game_conveyors_get); lua_setfield(L, -2, "__index"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pushvalue(L, -1); gameconveyorsref = luaL_ref(L, LUA_REGISTRYINDEX); lua_setmetatable(L, -2); lua_setfield(L, -2, "conveyors"); lua_pushcfunction(L, game_water); lua_setfield(L, -2, "water"); k3Log(k3_DEBUG, "Creating table game.mix"); lua_newtable(L); lua_pushcfunction(L, dagame_mixsound); lua_setfield(L, -2, "sound"); lua_pushcfunction(L, game_mixplay); lua_setfield(L, -2, "play"); lua_pushcfunction(L, dagame_mixstop); lua_setfield(L, -2, "stop"); lua_pushcfunction(L, game_mixqueue); lua_setfield(L, -2, "queue"); lua_pushcfunction(L, dagame_mixpower); lua_setfield(L, -2, "power"); lua_setfield(L, -2, "mix"); lua_pushcfunction(L, game_ref); lua_setfield(L, -2, "ref"); lua_pushcfunction(L, game_batch); lua_setfield(L, -2, "batch"); lua_pushcfunction(L, game_get_component); lua_setfield(L, -2, "getcomponent"); lua_pushcfunction(L, game_camfocus); lua_setfield(L, -2, "camfocus"); lua_pushcfunction(L, game_load); lua_setfield(L, -2, "load"); lua_pushcfunction(L, dagame_cleanup); lua_setfield(L, -2, "cleanup"); lua_pushcfunction(L, dagame_joint); lua_setfield(L, -2, "joint"); lua_pushcfunction(L, dagame_setphys); lua_setfield(L, -2, "setphys"); lua_pushcfunction(L, dagame_mdl); lua_setfield(L, -2, "mdl"); lua_pushcfunction(L, dagame_trimesh); lua_setfield(L, -2, "trimesh"); lua_pushcfunction(L, dagame_firstperson); lua_setfield(L, -2, "firstperson"); lua_pushcfunction(L, dagame_ray); lua_setfield(L, -2, "ray"); lua_pushcfunction(L, dagame_draw); lua_setfield(L, -2, "draw"); lua_pushcfunction(L, dagame_skybox); lua_setfield(L, -2, "skybox"); lua_pushcfunction(L, dagame_send); lua_setfield(L, -2, "send"); lua_pushcfunction(L, dagame_set_reduction); lua_setfield(L, -2, "set_reduction"); lua_pushcfunction(L, dagame_set_texture_reduction); lua_setfield(L, -2, "set_texture_reduction"); k3Log(k3_DEBUG, "Creating table game.ui"); lua_newtable(L); lua_pushcfunction(L, dagame_ui_screen); lua_setfield(L, -2, "screen"); lua_pushcfunction(L, dagame_ui_label); lua_setfield(L, -2, "label"); lua_pushcfunction(L, dagame_ui_textbutton); lua_setfield(L, -2, "textbutton"); lua_pushcfunction(L, dagame_ui_textinput); lua_setfield(L, -2, "textinput"); lua_setfield(L, -2, "ui"); k3Log(k3_DEBUG, "Creating table game.net"); lua_newtable(L); lua_pushcfunction(L, dagame_net_gen_peercode); lua_setfield(L, -2, "gen_peercode"); lua_pushcfunction(L, dagame_net_host); lua_setfield(L, -2, "host"); lua_pushcfunction(L, dagame_net_join); lua_setfield(L, -2, "join"); lua_setfield(L, -2, "net"); k3Log(k3_DEBUG, "Creating table game.controls"); lua_newtable(L); lua_newtable(L); lua_pushcfunction(L, dagame_controls_get); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, dagame_controls_set); lua_setfield(L, -2, "__newindex"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_setmetatable(L, -2); lua_setfield(L, -2, "controls"); k3Log(k3_DEBUG, "Creating table game.clipboard"); lua_newtable(L); lua_newtable(L); lua_pushcfunction(L, dagame_clipboard_set); lua_setfield(L, -2, "__newindex"); lua_pushcfunction(L, dagame_clipboard_get); lua_setfield(L, -2, "__index"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_setmetatable(L, -2); lua_setfield(L, -2, "clipboard"); k3Log(k3_DEBUG, "Creating metatable for game"); lua_newtable(L); lua_pushcfunction(L, game_get); lua_setfield(L, -2, "__index"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pushcfunction(L, game_set); lua_setfield(L, -2, "__newindex"); lua_setmetatable(L, -2); lua_setglobal(L, "game"); luaL_newmetatable(L, "k3water"); lua_pushcfunction(L, game_water_simulate); lua_setfield(L, -2, "simulate"); lua_pushcfunction(L, game_water_update); lua_setfield(L, -2, "update"); lua_pushcfunction(L, game_water_disturb); lua_setfield(L, -2, "disturb"); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3mixwave"); lua_pushcfunction(L, game_mixwave_set); lua_setfield(L, -2, "__newindex"); lua_pushcfunction(L, game_mixwave_get); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, dagame_mixwave_gc); lua_setfield(L, -2, "__gc"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3mixqueue"); lua_pushcfunction(L, game_queue_add); lua_setfield(L, -2, "add"); lua_pushcfunction(L, game_queue_clear); lua_setfield(L, -2, "clear"); lua_pushcfunction(L, game_queue_gc); lua_setfield(L, -2, "__gc"); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, game_mixwave_set); lua_setfield(L, -2, "__newindex"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3submissive"); lua_pushcfunction(L, game_submissive_assign); lua_setfield(L, -2, "assign"); lua_pushcfunction(L, game_submissive_load); lua_setfield(L, -2, "load"); lua_pushcfunction(L, game_submissive_send); lua_setfield(L, -2, "send"); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3cphysics"); lua_pushcfunction(L, game_cphysics_get); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, game_cphysics_set); lua_setfield(L, -2, "__newindex"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3crender"); lua_pushcfunction(L, game_crender_get); lua_setfield(L, -2, "__index"); //lua_pushcfunction(L, game_crender_set); //lua_setfield(L, -2, "__newindex"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3cboned"); lua_pushcfunction(L, game_cboned_get); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, game_cboned_set); lua_setfield(L, -2, "__newindex"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3cbonedanim"); lua_pushcfunction(L, game_cbonedanim_set); lua_setfield(L, -2, "__newindex"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3mdl"); lua_pushcfunction(L, game_k3mdl_get); lua_setfield(L, -2, "__index"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3mat"); lua_pushcfunction(L, game_k3mat_get); lua_setfield(L, -2, "__index"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3physics"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3matuniforms"); lua_pushcfunction(L, game_k3matuniforms_get); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, game_k3matuniforms_set); lua_setfield(L, -2, "__newindex"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3body"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3conveyor"); lua_pushcfunction(L, game_conveyor_set); lua_setfield(L, -2, "__newindex"); lua_pushcfunction(L, game_conveyor_get); lua_setfield(L, -2, "__index"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k4ray"); lua_pushcfunction(L, dagame_ray_get); lua_setfield(L, -2, "__index"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3menuitem"); lua_pushcfunction(L, dagame_k3menuitem_set); lua_setfield(L, -2, "__newindex"); lua_pushcfunction(L, dagame_k3menuitem_get); lua_setfield(L, -2, "__index"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); luaL_newmetatable(L, "k3mixsource"); lua_pushboolean(L, 0); lua_setfield(L, -2, "__metatable"); lua_pop(L, 1); lua_newtable(L); lua_pushcfunction(L, os_time); lua_setfield(L, -2, "time"); lua_setglobal(L, "os"); } void luaapi_load(const char *name) { lua_getglobal(L, "require"); lua_pushstring(L, name); if(lua_pcall(L, 1, 1, 0) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 1); return; } lua_getfield(L, -1, "load"); if(lua_pcall(L, 0, 0, 0) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 1); return; } if(Game.isAuthority) { luaapi_join(NULL, 0); } } void luaapi_render(double dt, double alpha) { lua_getglobal(L, "game"); lua_getfield(L, -1, "render"); if(!lua_isfunction(L, -1)) { lua_pop(L, 1); } else { lua_pushnumber(L, dt); lua_pushnumber(L, alpha); if(lua_pcall(L, 2, 0, 0) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 1); } } lua_pop(L, 1); } static int game_me_assign(lua_State *L) { Game.controlled = lua_tointeger(L, 2); Game.spectated = lua_tointeger(L, 2); return 0; } int luaapi_join(ENetPeer *peer, int oldlua) { int ret; lua_getglobal(L, "game"); lua_getfield(L, -1, "join"); if(lua_isfunction(L, -1)) { if(oldlua) { ret = oldlua; lua_rawgeti(L, LUA_REGISTRYINDEX, oldlua); } else if (peer) { ENetPeer **ud = lua_newuserdata(L, sizeof(peer)); *ud = peer; luaL_setmetatable(L, "k3submissive"); lua_pushvalue(L, -1); ret = luaL_ref(L, LUA_REGISTRYINDEX); } else { lua_newtable(L); lua_pushcfunction(L, game_me_assign); lua_setfield(L, -2, "assign"); lua_pushvalue(L, -1); ret = luaL_ref(L, LUA_REGISTRYINDEX); } if(lua_pcall(L, 1, 0, 0) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 1); } } else { lua_pop(L, 1); } lua_pop(L, 1); return ret; } void luaapi_leave(int ref) { lua_getglobal(L, "game"); lua_getfield(L, -1, "leave"); if(lua_isfunction(L, -1)) { lua_rawgeti(L, LUA_REGISTRYINDEX, ref); if(lua_pcall(L, 1, 0, 0) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 1); } } else { lua_pop(L, 1); } lua_pop(L, 1); } static char *read_full_file(const char *prepend, const char *fnprefix, const char *fn, const char *append) { char namebuf[256]; snprintf(namebuf, sizeof(namebuf), "%s%s", fnprefix, fn); FILE *f = fopen(namebuf, "rb"); if(!f) return NULL; fseek(f, 0, SEEK_END); size_t sz = ftell(f); fseek(f, 0, SEEK_SET); char *ret = malloc(strlen(prepend) + sz + strlen(append) + 1); strcpy(ret, prepend); fread(ret + strlen(prepend), 1, sz, f); strcpy(ret + strlen(prepend) + sz, append); ret[strlen(prepend) + sz + strlen(append)] = 0; fclose(f); return ret; } static int glsl_version_supported(const char *gl, size_t lua) { if((k3IsCore && lua < 330) || (!k3IsCore && lua >= 330)) { return 0; } char buf[16]; snprintf(buf, sizeof(buf), "%u", lua); if(gl[0] < buf[0]) { return 0; } return strcmp(buf + 1, gl + 2) <= 0; } static const char *k3glslloader(const char *fn) { char buf[512]; snprintf(buf, sizeof(buf), "assets/mdl/%s", fn); FILE *f = fopen(buf, "rb"); fseek(f, 0, SEEK_END); size_t len = ftell(f); char *data = malloc(len + 1); fseek(f, 0, SEEK_SET); fread(data, 1, len, f); data[len] = 0; fclose(f); return data; } // The glsl field of a material table is cached and stored // in the resource manager. This function generates its name. static char *glsl_table_fullname() { lua_getfield(L, -1, "vs"); lua_geti(L, -1, 1); // Version size_t vsVer = lua_tointeger(L, -1); lua_geti(L, -2, 2); // Filename char *vsFile = strdup(lua_tostring(L, -1)); lua_pop(L, 3); lua_getfield(L, -1, "fs"); lua_geti(L, -1, 1); // Version size_t fsVer = lua_tointeger(L, -1); lua_geti(L, -2, 2); // Filename char *fsFile = strdup(lua_tostring(L, -1)); lua_pop(L, 3); char ret[1024]; snprintf(ret, sizeof(ret), "\x1E%u\x1E%s\x1E%u\x1E%s\x1E", vsVer, vsFile, fsVer, fsFile); lua_getfield(L, -1, "defs"); lua_pushnil(L); while(lua_next(L, -2)) { if(!lua_isstring(L, -2)) { puts("Ignoring non-string define name."); lua_pop(L, 1); continue; } char def[128]; snprintf(def, sizeof(def), "%s\x1F%s\x1E", lua_tostring(L, -2), lua_tostring(L, -1)); strncat(ret, def, sizeof(ret) - strlen(ret) - 1); lua_pop(L, 1); } lua_pop(L, 1); free(vsFile); free(fsFile); return strdup(ret); } static int parse_glsl_table(struct k3Mat *mat) { #ifdef k3_IRREGULAR_SHADOWS static struct k3GLSLF *irreg1frag, *irreg2frag; static struct k3GLSLG *irreg2geom; if(!irreg1frag) { char *src = read_full_file("", "", "assets/irregpass1.fs.glsl", ""); irreg1frag = k3ShaderGLSLF(src, k3glslloader); free(src); } if(!irreg2frag) { char *src = read_full_file("", "", "assets/irregpass2.fs.glsl", ""); irreg2frag = k3ShaderGLSLF(src, k3glslloader); free(src); } if(!irreg2geom) { char *src = read_full_file("", "", "assets/irregpass2.gs.glsl", ""); irreg2geom = k3ShaderGLSLG(src, k3glslloader); free(src); } #endif char *glslfullname = glsl_table_fullname(); struct ResManRes *res = resman_reffull(RESMAN_OTHER, glslfullname); if(res->thing) { mat->passes[0].glsl.hp = res->thing; #ifdef k3_IRREGULAR_SHADOWS glslfullname[0]--; mat->passes[0].glsl.hpIrreg1 = resman_ref(RESMAN_OTHER, glslfullname); glslfullname[0]--; mat->passes[0].glsl.hpIrreg2 = resman_ref(RESMAN_OTHER, glslfullname); #endif } else { const char *maxGLSL = glGetString(GL_SHADING_LANGUAGE_VERSION_ARB); int i; lua_getfield(L, -1, "vs"); lua_geti(L, -1, 1); // Version size_t vsVer = lua_tointeger(L, -1); lua_geti(L, -2, 2); // Filename char *vsFile = strdup(lua_tostring(L, -1)); i = glsl_version_supported(maxGLSL, vsVer); lua_pop(L, 3); if(!i) { return 0; } lua_getfield(L, -1, "fs"); lua_geti(L, -1, 1); // Version size_t fsVer = lua_tointeger(L, -1); lua_geti(L, -2, 2); // Filename char *fsFile = strdup(lua_tostring(L, -1)); i = glsl_version_supported(maxGLSL, fsVer); lua_pop(L, 3); if(!i) { return 0; } char defs[512] = {}; lua_getfield(L, -1, "defs"); lua_pushnil(L); while(lua_next(L, -2)) { if(!lua_isstring(L, -2)) { puts("Ignoring non-string define name."); lua_pop(L, 1); continue; } char def[128]; snprintf(def, sizeof(def), "#define %s %s\n", lua_tostring(L, -2), lua_tostring(L, -1)); strncat(defs, def, sizeof(defs) - strlen(defs) - 1); lua_pop(L, 1); } lua_pop(L, 1); char prefix[272]; snprintf(prefix, sizeof(prefix), "#version %lu\n%s", vsVer, defs); char *vsSrc = read_full_file(prefix, "assets/mdl/", vsFile, ""); snprintf(prefix, sizeof(prefix), "#version %lu\n%s", fsVer, defs); char *fsSrc = read_full_file(prefix, "assets/mdl/", fsFile, ""); k3Log(k3_INFO, "Attempting to compile \"%s\" & \"%s\"...", vsFile, fsFile); free(vsFile); free(fsFile); struct k3GLSLV *vs = k3ShaderGLSLV(vsSrc, k3glslloader); struct k3GLSLF *fs = k3ShaderGLSLF(fsSrc, k3glslloader); free(vsSrc); free(fsSrc); // FIXME: mark shaders themselves for deletion? struct k3GLSLP *p = k3ProgramGLSL(vs, fs, NULL); res->thing = p; mat->passes[0].glsl.hp = p; #ifdef k3_IRREGULAR_SHADOWS glslfullname[0]--; mat->passes[0].glsl.hpIrreg1 = k3ProgramGLSL(vs, irreg1frag, NULL); resman_reffull(RESMAN_OTHER, glslfullname)->thing = mat->passes[0].glsl.hpIrreg1; glslfullname[0]--; mat->passes[0].glsl.hpIrreg2 = k3ProgramGLSL(vs, irreg2frag, irreg2geom); resman_reffull(RESMAN_OTHER, glslfullname)->thing = mat->passes[0].glsl.hpIrreg2; #endif } lua_getfield(L, -1, "u"); // Uniforms lua_pushnil(L); while(lua_next(L, -2)) { if(!lua_isstring(L, -2)) { // Ignore non-string key k3Log(k3_ERR, "Non-string uniform name in lua material definition."); lua_pop(L, 1); continue; } int u = mat->passes[0].glsl.uCount; if(u == k3_MAX_GLSL_UNIFORMS) { k3Log(k3_INFO, "Too many uniforms. Skipping."); } else { strncpy(mat->passes[0].glsl.u[u].name, lua_tostring(L, -2), sizeof(mat->passes[0].glsl.u[u].name)); mat->passes[0].glsl.u[u].id = k3ProgramGetUId(res->thing, lua_tostring(L, -2)); if(lua_isinteger(L, -1)) { mat->passes[0].glsl.u[u].type = k3_MAT_UNIFORM_I1; mat->passes[0].glsl.u[u].i1 = lua_tointeger(L, -1); mat->passes[0].glsl.uCount++; } else if(lua_isnumber(L, -1)) { mat->passes[0].glsl.u[u].type = k3_MAT_UNIFORM_F1; mat->passes[0].glsl.u[u].f1 = lua_tonumber(L, -1); mat->passes[0].glsl.uCount++; } else { k3Log(k3_ERR, "Only i1 and f1 uniforms are supported at the moment."); } } lua_pop(L, 1); } lua_pop(L, 1); return 1; } void luaapi_fillmaterial_direct(struct k3Mat *mat) { lua_getfield(L, -1, "primitive"); lua_getfield(L, -1, "diffuse"); if(lua_istable(L, -1)) { lua_geti(L, -1, 1); lua_geti(L, -2, 2); lua_geti(L, -3, 3); lua_geti(L, -4, 4); mat->primitive.diffuse[0] = lua_tonumber(L, -4); mat->primitive.diffuse[1] = lua_tonumber(L, -3); mat->primitive.diffuse[2] = lua_tonumber(L, -2); mat->primitive.diffuse[3] = lua_tonumber(L, -1); lua_pop(L, 4); } lua_pop(L, 1); lua_getfield(L, -1, "specular"); if(lua_istable(L, -1)) { lua_geti(L, -1, 1); lua_geti(L, -2, 2); lua_geti(L, -3, 3); lua_geti(L, -4, 4); mat->primitive.specular[0] = lua_tonumber(L, -4); mat->primitive.specular[1] = lua_tonumber(L, -3); mat->primitive.specular[2] = lua_tonumber(L, -2); mat->primitive.specular[3] = lua_tonumber(L, -1); lua_pop(L, 4); } lua_pop(L, 1); lua_getfield(L, -1, "emission"); if(lua_istable(L, -1)) { lua_geti(L, -1, 1); lua_geti(L, -2, 2); lua_geti(L, -3, 3); lua_geti(L, -4, 4); mat->primitive.emission[0] = lua_tonumber(L, -4); mat->primitive.emission[1] = lua_tonumber(L, -3); mat->primitive.emission[2] = lua_tonumber(L, -2); mat->primitive.emission[3] = lua_tonumber(L, -1); lua_pop(L, 4); } lua_pop(L, 1); lua_getfield(L, -1, "shininess"); mat->primitive.shininess = lua_tonumber(L, -1); lua_pop(L, 1); lua_pop(L, 1); GLuint maxMTUnits = 0, maxGLSLUnits = 0; if(k3IsCore) { glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxGLSLUnits); } else { if(GLAD_GL_ARB_shading_language_100) { glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &maxGLSLUnits); } glGetIntegerv(GL_MAX_TEXTURE_UNITS, &maxMTUnits); } for(size_t i = 1; i <= 128; i++) { int datop = lua_gettop(L); lua_geti(L, -1, i); if(lua_type(L, -1) != LUA_TTABLE) { lua_pop(L, 1); break; } // First pass lua_geti(L, -1, 1); lua_getfield(L, -1, "additive"); mat->passes[0].additive = lua_toboolean(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "transparent"); mat->passes[0].transparent = lua_toboolean(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "nocull"); mat->passes[0].nocull = lua_toboolean(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "alphatest"); mat->passes[0].alphatest = lua_toboolean(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "aabb"); mat->passes[0].aabb = lua_isnil(L, -1) ? NAN : lua_tonumber(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "glsl"); lua_getfield(L, -2, "arbvp"); lua_getfield(L, -3, "arbfp"); if((lua_istable(L, -1) || lua_istable(L, -2)) + lua_istable(L, -3) > 1) { k3Log(k3_ERR, "ARB programs and GLSL programs are mutually exclusive."); lua_settop(L, datop); continue; } lua_pop(L, 3); int usesGLSL = 0; lua_getfield(L, -1, "glsl"); { if(lua_istable(L, -1)) { if(!k3IsCore && !GLAD_GL_ARB_shading_language_100) { // GLSL not supported. lua_settop(L, datop); continue; } else { if(!parse_glsl_table(mat)) { lua_settop(L, datop); continue; } } usesGLSL = 1; } } lua_pop(L, 1); lua_getfield(L, -1, "arbvp"); { if(lua_istable(L, -1)) { lua_geti(L, -1, 2); char *src = read_full_file("", "assets/mdl/", lua_tostring(L, -1), ""); mat->passes[0].arbvp.vp = k3ProgramARBVP(src); free(src); lua_pop(L, 1); } } lua_pop(L, 1); lua_getfield(L, -1, "arbfp"); { if(lua_istable(L, -1)) { lua_geti(L, -1, 2); char *src = read_full_file("", "assets/mdl/", lua_tostring(L, -1), ""); mat->passes[0].arbfp.fp = k3ProgramARBFP(src); free(src); lua_pop(L, 1); } } lua_pop(L, 1); lua_getfield(L, -1, "units"); { size_t amountOfUnits = lua_rawlen(L, -1); if(amountOfUnits > (usesGLSL ? maxGLSLUnits : maxMTUnits) || amountOfUnits > k3_MAX_GLSL_UNITS) { // Too many texture units. // FIXME: This actually isn't a correct test; it should be done per shader lua_settop(L, datop); continue; } for(size_t u = 0; u < amountOfUnits; u++) { lua_geti(L, -1, u + 1); lua_getfield(L, -1, "tex"); mat->passes[0].units[u] = resman_ref(RESMAN_TEXTURE, lua_tostring(L, -1)); lua_pop(L, 2); } mat->passes[0].unitsUsed = amountOfUnits; } lua_pop(L, 1); lua_pop(L, 1); lua_pop(L, 1); break; } } void luaapi_fillmaterial(const char *fn, struct k3Mat *mat) { memset(mat, 0, sizeof(*mat)); k3Log(k3_DEBUG, "Loading material %s", fn); if(luaL_loadfilex(L, fn, "t") != LUA_OK || lua_pcall(L, 0, 1, 0) != LUA_OK) { mat->primitive.diffuse[0] = (float) rand() / RAND_MAX; mat->primitive.diffuse[1] = (float) rand() / RAND_MAX; mat->primitive.diffuse[2] = (float) rand() / RAND_MAX; mat->primitive.diffuse[3] = 1; mat->passes[0].aabb = NAN; puts(lua_tostring(L, -1)); lua_pop(L, 1); return; } luaapi_fillmaterial_direct(mat); lua_pop(L, 1); } struct k3Light *luaapi_getlights(size_t *count) { static struct k3Light *ret; lua_getglobal(L, "game"); lua_getfield(L, -1, "lights"); if(ret) { _mm_free(ret); ret = NULL; } if(!lua_isnil(L, -1)) { lua_len(L, -1); *count = lua_tointeger(L, -1); lua_pop(L, 1); ret = _mm_malloc(sizeof(*ret) * *count, 16); memset(ret, 0, sizeof(*ret) * *count); for(size_t i = 0; i < *count; i++) { lua_geti(L, -1, i + 1); lua_geti(L, -1, 1); // TYPE if(!strcmp(lua_tostring(L, -1), "dir")) { ret[i].type = k3_DIRECTIONAL; } else if(!strcmp(lua_tostring(L, -1), "spot")) { ret[i].type = k3_SPOT; } else if(!strcmp(lua_tostring(L, -1), "omni")) { ret[i].type = k3_OMNI; lua_getfield(L, -2, "direction"); if(!lua_isnil(L, -1)) { ret[i].type = k3_HALF_OMNI; } lua_pop(L, 1); } lua_pop(L, 1); lua_getfield(L, -1, "radius"); ret[i].radius = lua_tonumber(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "color"); lua_geti(L, -1, 1); lua_geti(L, -2, 2); lua_geti(L, -3, 3); lua_geti(L, -4, 4); ret[i].color[0] = lua_tonumber(L, -4); ret[i].color[1] = lua_tonumber(L, -3); ret[i].color[2] = lua_tonumber(L, -2); ret[i].color[3] = lua_tonumber(L, -1); lua_pop(L, 5); if(ret[i].type == k3_OMNI) { lua_getfield(L, -1, "position"); lua_geti(L, -1, 1); lua_geti(L, -2, 2); lua_geti(L, -3, 3); lua_geti(L, -4, 4); ret[i].omni.position[0] = lua_tonumber(L, -4); ret[i].omni.position[1] = lua_tonumber(L, -3); ret[i].omni.position[2] = lua_tonumber(L, -2); ret[i].omni.position[3] = lua_tonumber(L, -1); lua_pop(L, 5); } else if(ret[i].type == k3_HALF_OMNI || ret[i].type == k3_SPOT) { lua_getfield(L, -1, "position"); lua_geti(L, -1, 1); lua_geti(L, -2, 2); lua_geti(L, -3, 3); lua_geti(L, -4, 4); ret[i].spot.position[0] = lua_tonumber(L, -4); ret[i].spot.position[1] = lua_tonumber(L, -3); ret[i].spot.position[2] = lua_tonumber(L, -2); ret[i].spot.position[3] = lua_tonumber(L, -1); lua_pop(L, 5); lua_getfield(L, -1, "direction"); lua_geti(L, -1, 1); lua_geti(L, -2, 2); lua_geti(L, -3, 3); lua_geti(L, -4, 4); ret[i].spot.direction[0] = lua_tonumber(L, -4); ret[i].spot.direction[1] = lua_tonumber(L, -3); ret[i].spot.direction[2] = lua_tonumber(L, -2); ret[i].spot.direction[3] = lua_tonumber(L, -1); lua_pop(L, 5); lua_getfield(L, -1, "angle"); ret[i].spot.angle = lua_tonumber(L, -1); if(ret[i].spot.angle == 0) { ret[i].spot.angle = glm_rad(165); } lua_pop(L, 1); } else if(ret[i].type == k3_DIRECTIONAL) { lua_getfield(L, -1, "direction"); lua_geti(L, -1, 1); lua_geti(L, -2, 2); lua_geti(L, -3, 3); lua_geti(L, -4, 4); ret[i].dir.direction[0] = lua_tonumber(L, -4); ret[i].dir.direction[1] = lua_tonumber(L, -3); ret[i].dir.direction[2] = lua_tonumber(L, -2); ret[i].dir.direction[3] = lua_tonumber(L, -1); glm_vec3_normalize(ret[i].dir.direction); lua_pop(L, 5); } lua_pop(L, 1); } } else *count = 0; lua_pop(L, 2); return ret; } void luaapi_cleanup() { game_cleanup(); lua_getglobal(L, "game"); lua_pushnil(L); lua_setfield(L, -2, "lights"); lua_pushnil(L); lua_setfield(L, -2, "render"); lua_pushnil(L); lua_setfield(L, -2, "render2d"); lua_pushnil(L); lua_setfield(L, -2, "join"); lua_pushnil(L); lua_setfield(L, -2, "leave"); lua_pushnil(L); lua_setfield(L, -2, "ctrl"); lua_pop(L, 1); lua_rawgeti(L, LUA_REGISTRYINDEX, gametriggersref); while(1) { lua_pushnil(L); if(lua_next(L, -2) == 0) { break; } lua_pop(L, 1); lua_pushnil(L); lua_rawset(L, -3); } lua_pop(L, 1); } /*void luaapi_perform(const char *statement) { puts(statement); if(luaL_loadstring(L, statement) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 1); return; } lua_rawgeti(L, LUA_REGISTRYINDEX, consoleEnvironment); lua_setupvalue(L, -2, 1); if(lua_pcall(L, 0, 0, 0) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 1); return; } }*/ void luaapi_ctrlev(int ctrl, int action) { lua_getglobal(L, "game"); lua_getfield(L, -1, "ctrl"); if(lua_isnil(L, -1)) { lua_pop(L, 2); return; } else { lua_pushstring(L, k4_control_get_name_by_ctrl(ctrl)); lua_pushinteger(L, action); if(lua_pcall(L, 2, 0, 0) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 2); return; } } lua_pop(L, 1); } void luaapi_render2d() { lua_getglobal(L, "game"); lua_getfield(L, -1, "render2d"); if(lua_isnil(L, -1)) { lua_pop(L, 2); return; } else { if(lua_pcall(L, 0, 0, 0) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 2); return; } } lua_pop(L, 1); } int luaapi_recvmsg(struct bstr *b, int cli) { lua_getglobal(L, "game"); lua_getfield(L, -1, "receive"); if(cli == LUA_NOREF) { lua_pushnil(L); } else { lua_rawgeti(L, LUA_REGISTRYINDEX, cli); } if(!deserialize(b)) { lua_pop(L, 3); return 0; } if(lua_isnil(L, -3)) { lua_pop(L, 4); return 1; } else { if(lua_pcall(L, 2, 0, 0) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 2); return 1; } } lua_pop(L, 1); return 1; } void luaapi_escape() { lua_getglobal(L, "game"); lua_getfield(L, -1, "escape"); if(lua_isnil(L, -1)) { lua_pop(L, 2); return; } else { if(lua_pcall(L, 0, 0, 0) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 2); return; } } lua_pop(L, 1); } void luaapi_peercode_found(const char *peercode) { if(PeercodeHandler == LUA_NOREF) { puts("Peercode found but no handler"); return; } lua_rawgeti(L, LUA_REGISTRYINDEX, PeercodeHandler); if(lua_isnil(L, -1)) { lua_pop(L, 1); return; } else { lua_pushstring(L, peercode); if(lua_pcall(L, 1, 0, 0) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 1); return; } } lua_pop(L, 1); luaL_unref(L, LUA_REGISTRYINDEX, PeercodeHandler); PeercodeHandler = LUA_NOREF; } void luaapi_update() { lua_getglobal(L, "game"); lua_getfield(L, -1, "update"); if(lua_isnil(L, -1)) { lua_pop(L, 2); return; } else { if(lua_pcall(L, 0, 0, 0) != LUA_OK) { puts(lua_tostring(L, -1)); lua_pop(L, 2); return; } } lua_pop(L, 1); }