360 lines
8.5 KiB
C
360 lines
8.5 KiB
C
/*
|
|
This file is part of Iceball.
|
|
|
|
Iceball is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Iceball is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Iceball. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// common functions
|
|
int icelua_fn_common_map_load(lua_State *L)
|
|
{
|
|
int top = icelua_assert_stack(L, 1, 2);
|
|
const char *fname = lua_tostring(L, 1);
|
|
const char *type = "auto";
|
|
if(top >= 2)
|
|
type = lua_tostring(L, 2);
|
|
|
|
lua_getglobal(L, "common");
|
|
lua_getfield(L, -1, "fetch_block");
|
|
lua_remove(L, -2);
|
|
if(!strcmp(type, "auto"))
|
|
{
|
|
lua_pushstring(L, "map");
|
|
} else if(!strcmp(type, "vxl")) {
|
|
lua_pushstring(L, "vxl");
|
|
} else if(!strcmp(type, "icemap")) {
|
|
lua_pushstring(L, "icemap");
|
|
} else {
|
|
return luaL_error(L, "not a valid map type");
|
|
}
|
|
lua_pushvalue(L, 1);
|
|
lua_call(L, 2, 1);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int icelua_fn_common_map_new(lua_State *L)
|
|
{
|
|
int top = icelua_assert_stack(L, 3, 3);
|
|
int xlen = lua_tointeger(L, 1);
|
|
int ylen = lua_tointeger(L, 2);
|
|
int zlen = lua_tointeger(L, 3);
|
|
|
|
if(xlen < 4 || ylen < 4 || zlen < 4)
|
|
return luaL_error(L, "map size too small");
|
|
|
|
// XXX: shouldn't this be in map.c?
|
|
map_t *map = malloc(sizeof(map_t));
|
|
if(map == NULL)
|
|
{
|
|
int err = errno;
|
|
return luaL_error(L, "could not allocate map: %d / %s", err, strerror(err));
|
|
}
|
|
map->udtype = UD_MAP;
|
|
map->xlen = xlen;
|
|
map->ylen = ylen;
|
|
map->zlen = zlen;
|
|
map->pillars = malloc(xlen*zlen*sizeof(uint8_t *));
|
|
if(map->pillars == NULL)
|
|
{
|
|
int err = errno;
|
|
map_free(map);
|
|
return luaL_error(L, "could not allocate map->pillars: %d / %s", err, strerror(err));
|
|
}
|
|
|
|
int x,z,pi;
|
|
int b = map->ylen-2;
|
|
for(z = 0, pi = 0; z < map->zlen; z++)
|
|
for(x = 0; x < map->xlen; x++, pi++)
|
|
{
|
|
uint8_t *p = map->pillars[pi] = malloc(16);
|
|
// TODO: check if NULL
|
|
uint8_t v = (uint8_t)(x^z);
|
|
*(p++) = 1; *(p++) = 0; *(p++) = 0; *(p++) = 0;
|
|
*(p++) = 0; *(p++) = b; *(p++) = b; *(p++) = 0;
|
|
*(p++) = v; *(p++) = v; *(p++) = v; *(p++) = 1;
|
|
}
|
|
|
|
lua_pushlightuserdata(L, map);
|
|
return 1;
|
|
}
|
|
|
|
int icelua_fn_common_map_free(lua_State *L)
|
|
{
|
|
int top = icelua_assert_stack(L, 1, 1);
|
|
map_t *map = lua_touserdata(L, 1);
|
|
if(map == NULL || map->udtype != UD_MAP)
|
|
return luaL_error(L, "not a map");
|
|
|
|
map_free(map);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int icelua_fn_common_map_set(lua_State *L)
|
|
{
|
|
int top = icelua_assert_stack(L, 1, 1);
|
|
map_t *map = lua_touserdata(L, 1);
|
|
if((map == NULL || map->udtype != UD_MAP) && !lua_isnil(L, 1))
|
|
return luaL_error(L, "not a map");
|
|
|
|
if(L == lstate_server)
|
|
svmap = map;
|
|
else if(L == lstate_client)
|
|
clmap = map;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int icelua_fn_common_map_get(lua_State *L)
|
|
{
|
|
int top = icelua_assert_stack(L, 0, 0);
|
|
map_t *map = NULL;
|
|
|
|
if(L == lstate_server)
|
|
map = svmap;
|
|
else if(L == lstate_client)
|
|
map = clmap;
|
|
|
|
if(map == NULL)
|
|
return 0;
|
|
|
|
lua_pushlightuserdata(L, map);
|
|
return 1;
|
|
}
|
|
|
|
int icelua_fn_common_map_save(lua_State *L)
|
|
{
|
|
int top = icelua_assert_stack(L, 2, 3);
|
|
|
|
map_t *map = lua_touserdata(L, 1);
|
|
if(map == NULL || map->udtype != UD_MAP)
|
|
return luaL_error(L, "not a map");
|
|
const char *fname = lua_tostring(L, 2);
|
|
const char *type = "icemap";
|
|
if(top >= 3)
|
|
type = lua_tostring(L, 3);
|
|
|
|
if(L == lstate_server
|
|
? !path_type_server_writable(path_get_type(fname))
|
|
: !path_type_client_writable(path_get_type(fname)))
|
|
return luaL_error(L, "cannot write to there %d",path_get_type(fname));
|
|
|
|
if(!strcmp(type, "vxl"))
|
|
{
|
|
return luaL_error(L, "cannot save to vxl, sorry!");
|
|
} else if(!strcmp(type, "icemap")) {
|
|
if(map_save_icemap(map, fname))
|
|
return luaL_error(L, "map save failed, check the console");
|
|
} else {
|
|
return luaL_error(L, "not a valid map type");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int icelua_fn_common_map_get_dims(lua_State *L)
|
|
{
|
|
int top = icelua_assert_stack(L, 0, 0);
|
|
|
|
map_t *map = (L == lstate_server ? svmap : clmap);
|
|
|
|
// if no map, just give off nils
|
|
if(map == NULL || map->udtype != UD_MAP)
|
|
{
|
|
return 0;
|
|
} else {
|
|
lua_pushinteger(L, map->xlen);
|
|
lua_pushinteger(L, map->ylen);
|
|
lua_pushinteger(L, map->zlen);
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
int icelua_fn_common_map_pillar_get(lua_State *L)
|
|
{
|
|
int top = icelua_assert_stack(L, 2, 2);
|
|
int px, pz;
|
|
int i;
|
|
|
|
px = lua_tointeger(L, 1);
|
|
pz = lua_tointeger(L, 2);
|
|
|
|
map_t *map = (L == lstate_server ? svmap : clmap);
|
|
|
|
// if no map, return nil
|
|
if(map == NULL || map->udtype != UD_MAP)
|
|
return 0;
|
|
|
|
// get a pillar
|
|
uint8_t *p = map->pillars[(pz&(map->zlen-1))*map->xlen+(px&(map->xlen-1))];
|
|
|
|
// build the list
|
|
int llen = 4*((255&(int)*p)+1);
|
|
lua_createtable(L, llen, 0);
|
|
p += 4;
|
|
|
|
for(i = 1; i <= llen; i++)
|
|
{
|
|
lua_pushinteger(L, i);
|
|
lua_pushinteger(L, *(p++));
|
|
lua_settable(L, -3);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int icelua_fn_common_map_pillar_set(lua_State *L)
|
|
{
|
|
int top = icelua_assert_stack(L, 3, 3);
|
|
int px, pz, tlen;
|
|
int i;
|
|
|
|
px = lua_tointeger(L, 1);
|
|
pz = lua_tointeger(L, 2);
|
|
if(!lua_istable(L, 3))
|
|
return luaL_error(L, "expected a table, got something else");
|
|
tlen = lua_objlen(L, 3);
|
|
|
|
map_t *map = (L == lstate_server ? svmap : clmap);
|
|
|
|
// if no map, ignore (for now)
|
|
if(map == NULL || map->udtype != UD_MAP)
|
|
return 0;
|
|
|
|
// validate that the table is not TOO large and is 4-byte aligned wrt size
|
|
if((tlen&3) || tlen > 1024)
|
|
return luaL_error(L, "table length %d invalid", tlen);
|
|
|
|
// validate the table's input range
|
|
for(i = 0; i < tlen; i++)
|
|
{
|
|
lua_pushinteger(L, 1+i);
|
|
lua_gettable(L, 3);
|
|
int v = lua_tointeger(L, -1);
|
|
lua_pop(L, 1);
|
|
if(v < 0 || v > 255)
|
|
return luaL_error(L, "value at index %d out of unsigned byte range (%d)"
|
|
, 1+i, v);
|
|
}
|
|
|
|
// validate the table data
|
|
i = 0;
|
|
for(;;)
|
|
{
|
|
lua_pushinteger(L, 1+i+0);
|
|
lua_gettable(L, 3);
|
|
lua_pushinteger(L, 1+i+1);
|
|
lua_gettable(L, 3);
|
|
lua_pushinteger(L, 1+i+2);
|
|
lua_gettable(L, 3);
|
|
lua_pushinteger(L, 1+i+3);
|
|
lua_gettable(L, 3);
|
|
int n = lua_tointeger(L, -4);
|
|
int s = lua_tointeger(L, -3);
|
|
int e = lua_tointeger(L, -2);
|
|
int a = lua_tointeger(L, -1);
|
|
lua_pop(L, 4);
|
|
|
|
//printf("%i %i | %i %i | %i %i %i %i\n",px,pz,i,tlen,n,s,e,a);
|
|
|
|
// Note, we are not supporting the shenanigans you can do in VOXLAP.
|
|
// Especially considering that editing said shenanigans causes issues.
|
|
// Also noting that said shenanigans weren't all that exploited,
|
|
// VOXLAP automatically corrects shenanigans when you edit stuff,
|
|
// and pyspades has no support for such shenanigans.
|
|
if(e+1 < s)
|
|
return luaL_error(L, "pillar has end+1 < start (%d < %d)"
|
|
, e+1, s);
|
|
if(i != 0 && s < a)
|
|
return luaL_error(L, "pillar has start < air (%d < %d)"
|
|
, s, a);
|
|
if(n != 0 && n-1 < e-s+1)
|
|
return luaL_error(L, "pillar has length < top length (%d < %d)"
|
|
, n-1, e-s+1);
|
|
|
|
|
|
// NOTE: this doesn't validate the BGRT (colour/type) entries.
|
|
int la = 0;
|
|
if(n == 0)
|
|
{
|
|
int exlen = (e-s+1)*4+i+4;
|
|
if(exlen != tlen)
|
|
return luaL_error(L, "pillar table len should be %d, got %d instead"
|
|
, exlen, tlen);
|
|
break;
|
|
} else {
|
|
i += 4*n;
|
|
// should always be colour on the bottom!
|
|
if(i > tlen-4)
|
|
return luaL_error(L, "pillar table overflow when validating");
|
|
|
|
}
|
|
}
|
|
|
|
// expand the pillar data if necessary
|
|
int idx = (pz&(map->zlen-1))*map->xlen+(px&(map->xlen-1));
|
|
uint8_t *p = map->pillars[idx];
|
|
if((p[0]+1)*4 < tlen)
|
|
{
|
|
p = map->pillars[idx] = realloc(p, tlen+4);
|
|
p[0] = (tlen>>2)-1;
|
|
}
|
|
|
|
// transfer the table data
|
|
p += 4;
|
|
for(i = 1; i <= tlen; i++)
|
|
{
|
|
lua_pushinteger(L, i);
|
|
lua_gettable(L, 3);
|
|
*(p++) = (uint8_t)lua_tointeger(L, -1);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
force_redraw = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// client functions
|
|
int icelua_fn_client_map_fog_get(lua_State *L)
|
|
{
|
|
int top = icelua_assert_stack(L, 0, 0);
|
|
|
|
lua_pushinteger(L, (fog_color>>16)&255);
|
|
lua_pushinteger(L, (fog_color>>8)&255);
|
|
lua_pushinteger(L, (fog_color)&255);
|
|
lua_pushnumber(L, fog_distance);
|
|
|
|
return 4;
|
|
}
|
|
|
|
int icelua_fn_client_map_fog_set(lua_State *L)
|
|
{
|
|
int top = icelua_assert_stack(L, 4, 4);
|
|
|
|
int r = lua_tointeger(L, 1)&255;
|
|
int g = lua_tointeger(L, 2)&255;
|
|
int b = lua_tointeger(L, 3)&255;
|
|
fog_distance = lua_tonumber(L, 4);
|
|
if(fog_distance < 5.0f)
|
|
fog_distance = 5.0f;
|
|
if(fog_distance > FOG_MAX_DISTANCE)
|
|
fog_distance = FOG_MAX_DISTANCE;
|
|
|
|
fog_color = (r<<16)|(g<<8)|b;
|
|
force_redraw = 1;
|
|
|
|
return 4;
|
|
}
|