// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 2020-2023 by Iestyn "Monster Iestyn" Jealous. // Copyright (C) 2020-2023 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file lua_polyobjlib.c /// \brief polyobject library for Lua scripting #include "doomdef.h" #include "fastcmp.h" #include "p_local.h" #include "p_polyobj.h" #include "lua_script.h" #include "lua_libs.h" #include "lua_hud.h" // hud_running errors #define NOHUD if (hud_running)\ return luaL_error(L, "HUD rendering code should not call this function!"); enum polyobj_e { // properties polyobj_valid = 0, polyobj_id, polyobj_parent, polyobj_vertices, polyobj_lines, polyobj_sector, polyobj_angle, polyobj_damage, polyobj_thrust, polyobj_flags, polyobj_translucency, polyobj_triggertag, // special functions - utility polyobj_pointInside, polyobj_mobjTouching, polyobj_mobjInside, // special functions - manipulation polyobj_moveXY, polyobj_rotate }; static const char *const polyobj_opt[] = { // properties "valid", "id", "parent", "vertices", "lines", "sector", "angle", "damage", "thrust", "flags", "translucency", "triggertag", // special functions - utility "pointInside", "mobjTouching", "mobjInside", // special functions - manipulation "moveXY", "rotate", NULL}; static const char *const valid_opt[] ={"valid",NULL}; //////////////////////// // polyobj.vertices[] // //////////////////////// // polyobj.vertices, i -> polyobj.vertices[i] // polyobj.vertices.valid, for validity checking // // see sectorlines_get in lua_maplib.c // static int polyobjvertices_get(lua_State *L) { vertex_t ***polyverts = *((vertex_t ****)luaL_checkudata(L, 1, META_POLYOBJVERTICES)); size_t i; size_t numofverts = 0; lua_settop(L, 2); if (!lua_isnumber(L, 2)) { int field = luaL_checkoption(L, 2, NULL, valid_opt); if (!polyverts || !(*polyverts)) { if (field == 0) { lua_pushboolean(L, 0); return 1; } return luaL_error(L, "accessed polyobj_t.vertices doesn't exist anymore."); } else if (field == 0) { lua_pushboolean(L, 1); return 1; } } numofverts = *(size_t *)FIELDFROM (polyobj_t, polyverts, vertices,/* -> */numVertices); if (!numofverts) return luaL_error(L, "no vertices found!"); i = (size_t)lua_tointeger(L, 2); if (i >= numofverts) return 0; LUA_PushUserdata(L, (*polyverts)[i], META_VERTEX); return 1; } // #(polyobj.vertices) -> polyobj.numVertices static int polyobjvertices_num(lua_State *L) { vertex_t ***polyverts = *((vertex_t ****)luaL_checkudata(L, 1, META_POLYOBJVERTICES)); size_t numofverts = 0; if (!polyverts || !(*polyverts)) return luaL_error(L, "accessed polyobj_t.vertices doesn't exist anymore."); numofverts = *(size_t *)FIELDFROM (polyobj_t, polyverts, vertices,/* -> */numVertices); lua_pushinteger(L, numofverts); return 1; } ///////////////////// // polyobj.lines[] // ///////////////////// // polyobj.lines, i -> polyobj.lines[i] // polyobj.lines.valid, for validity checking // // see sectorlines_get in lua_maplib.c // static int polyobjlines_get(lua_State *L) { line_t ***polylines = *((line_t ****)luaL_checkudata(L, 1, META_POLYOBJLINES)); size_t i; size_t numoflines = 0; lua_settop(L, 2); if (!lua_isnumber(L, 2)) { int field = luaL_checkoption(L, 2, NULL, valid_opt); if (!polylines || !(*polylines)) { if (field == 0) { lua_pushboolean(L, 0); return 1; } return luaL_error(L, "accessed polyobj_t.lines doesn't exist anymore."); } else if (field == 0) { lua_pushboolean(L, 1); return 1; } } numoflines = *(size_t *)FIELDFROM (polyobj_t, polylines, lines,/* -> */numLines); if (!numoflines) return luaL_error(L, "no lines found!"); i = (size_t)lua_tointeger(L, 2); if (i >= numoflines) return 0; LUA_PushUserdata(L, (*polylines)[i], META_LINE); return 1; } // #(polyobj.lines) -> polyobj.numLines static int polyobjlines_num(lua_State *L) { line_t ***polylines = *((line_t ****)luaL_checkudata(L, 1, META_POLYOBJLINES)); size_t numoflines = 0; if (!polylines || !(*polylines)) return luaL_error(L, "accessed polyobj_t.lines doesn't exist anymore."); numoflines = *(size_t *)FIELDFROM (polyobj_t, polylines, lines,/* -> */numLines); lua_pushinteger(L, numoflines); return 1; } ///////////////////////////////// // polyobj_t function wrappers // ///////////////////////////////// // special functions - utility static int lib_polyobj_PointInside(lua_State *L) { polyobj_t *po = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); fixed_t x = luaL_checkfixed(L, 2); fixed_t y = luaL_checkfixed(L, 3); INLEVEL if (!po) return LUA_ErrInvalid(L, "polyobj_t"); lua_pushboolean(L, P_PointInsidePolyobj(po, x, y)); return 1; } static int lib_polyobj_MobjTouching(lua_State *L) { polyobj_t *po = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); INLEVEL if (!po) return LUA_ErrInvalid(L, "polyobj_t"); if (!mo) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_MobjTouchingPolyobj(po, mo)); return 1; } static int lib_polyobj_MobjInside(lua_State *L) { polyobj_t *po = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); INLEVEL if (!po) return LUA_ErrInvalid(L, "polyobj_t"); if (!mo) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_MobjInsidePolyobj(po, mo)); return 1; } // special functions - manipulation static int lib_polyobj_moveXY(lua_State *L) { polyobj_t *po = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); fixed_t x = luaL_checkfixed(L, 2); fixed_t y = luaL_checkfixed(L, 3); boolean checkmobjs = lua_opttrueboolean(L, 4); NOHUD INLEVEL if (!po) return LUA_ErrInvalid(L, "polyobj_t"); lua_pushboolean(L, Polyobj_moveXY(po, x, y, checkmobjs)); return 1; } static int lib_polyobj_rotate(lua_State *L) { polyobj_t *po = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); angle_t delta = luaL_checkangle(L, 2); boolean turnplayers = lua_opttrueboolean(L, 3); boolean turnothers = lua_opttrueboolean(L, 4); boolean checkmobjs = lua_opttrueboolean(L, 5); NOHUD INLEVEL if (!po) return LUA_ErrInvalid(L, "polyobj_t"); lua_pushboolean(L, Polyobj_rotate(po, delta, turnplayers, turnothers, checkmobjs)); return 1; } /////////////// // polyobj_t // /////////////// static int polyobj_get(lua_State *L) { polyobj_t *polyobj = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); enum polyobj_e field = luaL_checkoption(L, 2, NULL, polyobj_opt); if (!polyobj) { if (field == polyobj_valid) { lua_pushboolean(L, false); return 1; } return LUA_ErrInvalid(L, "polyobj_t"); } switch (field) { // properties case polyobj_valid: lua_pushboolean(L, true); break; case polyobj_id: lua_pushinteger(L, polyobj->id); break; case polyobj_parent: lua_pushinteger(L, polyobj->parent); break; case polyobj_vertices: // vertices LUA_PushUserdata(L, &polyobj->vertices, META_POLYOBJVERTICES); // push the address of the "vertices" member in the struct, to allow our hacks to work break; case polyobj_lines: // lines LUA_PushUserdata(L, &polyobj->lines, META_POLYOBJLINES); // push the address of the "lines" member in the struct, to allow our hacks to work break; case polyobj_sector: // shortcut that exists only in Lua! LUA_PushUserdata(L, polyobj->lines[0]->backsector, META_SECTOR); break; case polyobj_angle: lua_pushangle(L, polyobj->angle); break; case polyobj_damage: lua_pushinteger(L, polyobj->damage); break; case polyobj_thrust: lua_pushfixed(L, polyobj->thrust); break; case polyobj_flags: lua_pushinteger(L, polyobj->flags); break; case polyobj_translucency: lua_pushinteger(L, polyobj->translucency); break; case polyobj_triggertag: lua_pushinteger(L, polyobj->triggertag); break; // special functions - utility case polyobj_pointInside: lua_pushcfunction(L, lib_polyobj_PointInside); break; case polyobj_mobjTouching: lua_pushcfunction(L, lib_polyobj_MobjTouching); break; case polyobj_mobjInside: lua_pushcfunction(L, lib_polyobj_MobjInside); break; // special functions - manipulation case polyobj_moveXY: lua_pushcfunction(L, lib_polyobj_moveXY); break; case polyobj_rotate: lua_pushcfunction(L, lib_polyobj_rotate); break; } return 1; } static int polyobj_set(lua_State *L) { polyobj_t *polyobj = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); enum polyobj_e field = luaL_checkoption(L, 2, NULL, polyobj_opt); if (!polyobj) return LUA_ErrInvalid(L, "polyobj_t"); if (hud_running) return luaL_error(L, "Do not alter polyobj_t in HUD rendering code!"); switch (field) { default: return luaL_error(L, LUA_QL("polyobj_t") " field " LUA_QS " cannot be modified.", polyobj_opt[field]); case polyobj_angle: return luaL_error(L, LUA_QL("polyobj_t") " field " LUA_QS " should not be set directly. Use the function " LUA_QL("polyobj:rotate(angle)") " instead.", polyobj_opt[field]); case polyobj_parent: polyobj->parent = luaL_checkinteger(L, 3); break; case polyobj_flags: polyobj->flags = luaL_checkinteger(L, 3); break; case polyobj_translucency: polyobj->translucency = luaL_checkinteger(L, 3); break; } return 0; } static int polyobj_num(lua_State *L) { polyobj_t *polyobj = *((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)); if (!polyobj) return luaL_error(L, "accessed polyobj_t doesn't exist anymore."); lua_pushinteger(L, polyobj-PolyObjects); return 1; } /////////////////// // PolyObjects[] // /////////////////// static int lib_iteratePolyObjects(lua_State *L) { INT32 i = -1; if (lua_gettop(L) < 2) { //return luaL_error(L, "Don't call PolyObjects.iterate() directly, use it as 'for polyobj in PolyObjects.iterate do end'."); lua_pushcfunction(L, lib_iteratePolyObjects); return 1; } lua_settop(L, 2); lua_remove(L, 1); // state is unused. if (!lua_isnil(L, 1)) i = (INT32)(*((polyobj_t **)luaL_checkudata(L, 1, META_POLYOBJ)) - PolyObjects); for (i++; i < numPolyObjects; i++) { LUA_PushUserdata(L, &PolyObjects[i], META_POLYOBJ); return 1; } return 0; } static int lib_PolyObject_getfornum(lua_State *L) { INT32 id = (INT32)luaL_checkinteger(L, 1); if (!numPolyObjects) return 0; // if there's no PolyObjects then bail out here LUA_PushUserdata(L, Polyobj_GetForNum(id), META_POLYOBJ); return 1; } static int lib_getPolyObject(lua_State *L) { const char *field; INT32 i; // find PolyObject by number if (lua_type(L, 2) == LUA_TNUMBER) { i = luaL_checkinteger(L, 2); if (i < 0 || i >= numPolyObjects) return luaL_error(L, "polyobjects[] index %d out of range (0 - %d)", i, numPolyObjects-1); LUA_PushUserdata(L, &PolyObjects[i], META_POLYOBJ); return 1; } field = luaL_checkstring(L, 2); // special function iterate if (fastcmp(field,"iterate")) { lua_pushcfunction(L, lib_iteratePolyObjects); return 1; } // find PolyObject by ID else if (fastcmp(field,"GetForNum")) // name could probably be better { lua_pushcfunction(L, lib_PolyObject_getfornum); return 1; } return 0; } static int lib_numPolyObjects(lua_State *L) { lua_pushinteger(L, numPolyObjects); return 1; } int LUA_PolyObjLib(lua_State *L) { LUA_RegisterUserdataMetatable(L, META_POLYOBJVERTICES, polyobjvertices_get, NULL, polyobjvertices_num); LUA_RegisterUserdataMetatable(L, META_POLYOBJLINES, polyobjlines_get, NULL, polyobjlines_num); LUA_RegisterUserdataMetatable(L, META_POLYOBJ, polyobj_get, polyobj_set, polyobj_num); LUA_RegisterGlobalUserdata(L, "polyobjects", lib_getPolyObject, NULL, lib_numPolyObjects); return 0; }