k4/src/luaapi.c
2025-01-20 10:52:26 +02:00

3472 lines
81 KiB
C

#include"luaapi.h"
#include"resman.h"
#include<lua.h>
#include<lualib.h>
#include<lauxlib.h>
#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<assert.h>
/*
* 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<GLFW/glfw3.h>
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);
}