diff --git a/src/Sourcefile b/src/Sourcefile index 7d9d18c75..67158b82f 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -111,6 +111,7 @@ lua_hudlib.c lua_hudlib_drawlist.c lua_followerlib.c lua_terrainlib.c +lua_waypointslib.c k_kart.c k_collide.c k_color.c diff --git a/src/deh_tables.c b/src/deh_tables.c index 8427269d5..9040f1097 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -29,6 +29,8 @@ #include "k_follower.h" // followermode_t (for lua) #include "g_input.h" // Game controls (for lua) #include "k_kart.h" // awardscaledrings_t +#include "k_waypoint.h" // waypoint values (for lua) + #include "deh_tables.h" #include "fastcmp.h" @@ -1573,6 +1575,8 @@ struct int_const_s const INT_CONST[] = { {"KARTINVIN_LEGACY", KARTINVIN_LEGACY}, {"KARTINVIN_ALTERN", KARTINVIN_ALTERN}, + // k_waypoint.h values + {"DEFAULT_WAYPOINT_RADIUS",DEFAULT_WAYPOINT_RADIUS}, {NULL,0} }; diff --git a/src/k_waypoint.cpp b/src/k_waypoint.cpp index f3a1196da..8f9f72574 100644 --- a/src/k_waypoint.cpp +++ b/src/k_waypoint.cpp @@ -152,6 +152,27 @@ boolean K_GetWaypointIsEnabled(waypoint_t *waypoint) return waypointisenabled; } +/*-------------------------------------------------- + * boolean K_SetWaypointIsEnabled(waypoint_t *waypoint, boolean enabled) + * + * See header file for description. + * --------------------------------------------------*/ +void K_SetWaypointIsEnabled(waypoint_t *waypoint, boolean enabled) +{ + if (waypoint == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_SetWaypointIsEnabled.\n"); + } + else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == true)) + { + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_SetWaypointIsEnabled.\n"); + } + else + { + waypoint->mobj->extravalue1 = enabled ? 1 : 0; + } +} + /*-------------------------------------------------- boolean K_GetWaypointIsSpawnpoint(waypoint_t *waypoint) @@ -163,11 +184,11 @@ boolean K_GetWaypointIsSpawnpoint(waypoint_t *waypoint) if (waypoint == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsEnabled.\n"); + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsSpawnpoint.\n"); } else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == true)) { - CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsEnabled.\n"); + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsSpawnpoint.\n"); } else { @@ -521,7 +542,7 @@ size_t K_GetWaypointHeapIndex(waypoint_t *waypoint) if (waypoint == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointID.\n"); + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointHeapIndex.\n"); } else { diff --git a/src/k_waypoint.h b/src/k_waypoint.h index f0e1474a7..c9b618089 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -125,6 +125,17 @@ boolean K_GetWaypointIsShortcut(waypoint_t *waypoint); boolean K_GetWaypointIsEnabled(waypoint_t *waypoint); +/*-------------------------------------------------- + * boolean K_SetWaypointIsEnabled(waypoint_t *waypoint, boolean enabled) + * + * Sets whether the waypoint is enabled or not. + * + * Input Arguments:- + * waypoint - The waypoint to set its enabled status to. + * enabled - Boolean that sets the waypoint's enabled status. + * --------------------------------------------------*/ + +void K_SetWaypointIsEnabled(waypoint_t *waypoint, boolean enabled); /*-------------------------------------------------- boolean K_GetWaypointIsSpawnpoint(waypoint_t *waypoint) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index b523ed873..25bb67cc3 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -32,6 +32,7 @@ #include "k_collide.h" #include "k_color.h" #include "k_hud.h" +#include "k_waypoint.h" #include "d_netcmd.h" // IsPlayerAdmin #include "m_menu.h" // Player Setup menu color stuff #include "p_spec.h" // P_StartQuake @@ -4250,6 +4251,345 @@ static int lib_kAwardScaledPlayerRings(lua_State *L) return 0; } +static int lib_kNextRespawnWaypointIndex(lua_State *L) +{ + waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + + INLEVEL + if (!waypoint) + return LUA_ErrInvalid(L, "waypoint_t"); + + lua_pushinteger(L, K_NextRespawnWaypointIndex(waypoint)); + return 1; +} + +static int lib_kGetFinishLineWaypoint(lua_State *L) +{ + INLEVEL + LUA_PushUserdata(L, K_GetFinishLineWaypoint(), META_WAYPOINT); + return 1; +} + +static int lib_kGetStartingWaypoint(lua_State *L) +{ + INLEVEL + LUA_PushUserdata(L, K_GetStartingWaypoint(), META_WAYPOINT); + return 1; +} + +static int lib_kGetWaypointIsFinishline(lua_State *L) +{ + waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + INLEVEL + + if (!waypoint) + return LUA_ErrInvalid(L, "waypoint_t"); + + lua_pushboolean(L, K_GetWaypointIsFinishline(waypoint)); + return 1; +} + +static int lib_kGetWaypointIsShortcut(lua_State *L) +{ + waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + INLEVEL + + if (!waypoint) + return LUA_ErrInvalid(L, "waypoint_t"); + + lua_pushboolean(L, K_GetWaypointIsShortcut(waypoint)); + return 1; +} + +static int lib_kGetWaypointIsEnabled(lua_State *L) +{ + waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + INLEVEL + + if (!waypoint) + return LUA_ErrInvalid(L, "waypoint_t"); + + lua_pushboolean(L, K_GetWaypointIsEnabled(waypoint)); + return 1; +} + +static int lib_kSetWaypointIsEnabled(lua_State *L) +{ + waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + boolean enabled = luaL_checkboolean(L, 2); + INLEVEL + + if (!waypoint) + return LUA_ErrInvalid(L, "waypoint_t"); + + K_SetWaypointIsEnabled(waypoint, enabled); + return 0; +} + +static int lib_kGetWaypointIsSpawnpoint(lua_State *L) +{ + waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + INLEVEL + + if (!waypoint) + return LUA_ErrInvalid(L, "waypoint_t"); + + lua_pushboolean(L, K_GetWaypointIsSpawnpoint(waypoint)); + return 1; +} + +static int lib_kGetWaypointNextID(lua_State *L) +{ + waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + INLEVEL + + if (!waypoint) + return LUA_ErrInvalid(L, "waypoint_t"); + + lua_pushinteger(L, K_GetWaypointNextID(waypoint)); + return 1; +} + +static int lib_kGetWaypointID(lua_State *L) +{ + waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + INLEVEL + + if (!waypoint) + return LUA_ErrInvalid(L, "waypoint_t"); + + lua_pushinteger(L, K_GetWaypointID(waypoint)); + return 1; +} + +static int lib_kGetWaypointFromID(lua_State *L) +{ + INT32 waypointId = luaL_checkinteger(L, 1); + INLEVEL + + LUA_PushUserdata(L, K_GetWaypointFromID(waypointId), META_WAYPOINT); + return 1; +} + +static int lib_kGetCircuitLength(lua_State *L) +{ + INLEVEL + lua_pushinteger(L, K_GetCircuitLength()); + return 1; +} + +static int lib_kGetTrackComplexity(lua_State *L) +{ + INLEVEL + lua_pushinteger(L, K_GetTrackComplexity()); + return 1; +} + +static int lib_kGetClosestWaypointToMobj(lua_State *L) +{ + mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + INLEVEL + if (!mobj) + return LUA_ErrInvalid(L, "mobj_t"); + + LUA_PushUserdata(L, K_GetClosestWaypointToMobj(mobj), META_WAYPOINT); + return 1; +} + +static int lib_kGetBestWaypointForMobj(lua_State *L) +{ + mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + waypoint_t *hint = NULL; + INLEVEL + if (!mobj) + return LUA_ErrInvalid(L, "mobj_t"); + // Optional waypoint parameter: + if (lua_isuserdata(L, 2) && lua_getmetatable(L, 2)) + { + lua_getfield(L, LUA_REGISTRYINDEX, META_WAYPOINT); + int result = lua_rawequal(L, -1, -2); + lua_pop(L, 2); + + if (!result) + { + return LUA_ErrInvalid(L, "waypoint_t"); + } + else + + + + + hint = *((waypoint_t **)lua_touserdata(L, 2)); + } + else if (!lua_isnoneornil(L, 2)) + { + // If we reach this point and it isn't an userdata, + // the scripter used a basic data type. Let them know + // they messed up. (Just use nil or nothing, please.) + return LUA_ErrInvalid(L, "waypoint_t"); + } + + LUA_PushUserdata(L, K_GetBestWaypointForMobj(mobj, hint), META_WAYPOINT); + return 1; +} + +/* + * JugadorXEI @ 01/11/2025 (MM/DD/AAAA) + * This was my way to work around giving path_t and pathfindnode_t objects + * to Lua, as usually these are dynamically allocated. We give them a deep + * copy of the values and then we free this memory after the fact. + * Lua can manage its own copy itself. + */ +static void pushDeepCopyOfPathTypeAsTable(lua_State *L, path_t *const path) +{ + lua_createtable(L, 0, 3); + + lua_pushinteger(L, path->numnodes); + lua_setfield(L, -2, "numnodes"); + + lua_createtable(L, path->numnodes, 0); + for (size_t i = 0; i < path->numnodes; i++) + { + lua_createtable(L, 0, 3); + + // It doesn't make sense for heap-related stuff to be exposed to Lua. + // lua_pushinteger(L, path->array[i].heapindex); + // lua_setfield(L, -2, "heapindex"); + + LUA_PushUserdata(L, (waypoint_t *)path->array[i].nodedata, META_WAYPOINT); + lua_setfield(L, -2, "nodedata"); + + lua_pushinteger(L, path->array[i].gscore); + lua_setfield(L, -2, "gscore"); + + lua_pushinteger(L, path->array[i].hscore); + lua_setfield(L, -2, "hscore"); + + lua_rawseti(L, -2, 1 + i); + } + lua_setfield(L, -2, "array"); + + lua_pushinteger(L, path->totaldist); + lua_setfield(L, -2, "totaldist"); +} + +static int lib_kPathfindToWaypoint(lua_State *L) +{ + waypoint_t *sourcewaypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + waypoint_t *destinationwaypoint = *((waypoint_t **)luaL_checkudata(L, 2, META_WAYPOINT)); + boolean useshortcuts = lua_optboolean(L, 3); + boolean huntbackwards = lua_optboolean(L, 4); + + INLEVEL + if (!sourcewaypoint || !destinationwaypoint) + return LUA_ErrInvalid(L, "waypoint_t"); + + path_t returnpath = {0}; + boolean success = K_PathfindToWaypoint(sourcewaypoint, destinationwaypoint, &returnpath, useshortcuts, huntbackwards); + + lua_pushboolean(L, success); + if (success) + { + pushDeepCopyOfPathTypeAsTable(L, &returnpath); + } + else + lua_pushnil(L); + + Z_Free(returnpath.array); + return 2; +} + +static int lib_kPathfindThruCircuit(lua_State *L) +{ + waypoint_t *sourcewaypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + fixed_t traveldistance = luaL_checkfixed(L, 2); + boolean useshortcuts = lua_optboolean(L, 3); + boolean huntbackwards = lua_optboolean(L, 4); + + INLEVEL + if (!sourcewaypoint) + return LUA_ErrInvalid(L, "waypoint_t"); + + path_t returnpath = {0}; + boolean success = K_PathfindThruCircuit(sourcewaypoint, traveldistance, &returnpath, useshortcuts, huntbackwards); + + lua_pushboolean(L, success); + if (success) + { + pushDeepCopyOfPathTypeAsTable(L, &returnpath); + } + else + lua_pushnil(L); + + Z_Free(returnpath.array); + return 2; +} + +static int lib_kPathfindThruCircuitSpawnable(lua_State *L) +{ + waypoint_t *sourcewaypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + fixed_t traveldistance = luaL_checkfixed(L, 2); + boolean useshortcuts = lua_optboolean(L, 3); + boolean huntbackwards = lua_optboolean(L, 4); + + INLEVEL + if (!sourcewaypoint) + return LUA_ErrInvalid(L, "waypoint_t"); + + path_t returnpath = {0}; + boolean success = K_PathfindThruCircuitSpawnable(sourcewaypoint, traveldistance, &returnpath, useshortcuts, huntbackwards); + + lua_pushboolean(L, success); + if (success) + { + pushDeepCopyOfPathTypeAsTable(L, &returnpath); + } + else + lua_pushnil(L); + + Z_Free(returnpath.array); + return 2; +} + +static int lib_kGetNextWaypointToDestination(lua_State *L) +{ + waypoint_t *sourcewaypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + waypoint_t *destinationwaypoint = *((waypoint_t **)luaL_checkudata(L, 2, META_WAYPOINT)); + boolean useshortcuts = lua_optboolean(L, 3); + boolean huntbackwards = lua_optboolean(L, 4); + + INLEVEL + if (!sourcewaypoint || !destinationwaypoint) + return LUA_ErrInvalid(L, "waypoint_t"); + + LUA_PushUserdata(L, K_GetNextWaypointToDestination(sourcewaypoint, destinationwaypoint, useshortcuts, huntbackwards), META_WAYPOINT); + return 1; +} + +static int lib_kSearchWaypointGraphForMobj(lua_State *L) +{ + mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + + INLEVEL + if (!mobj) + return LUA_ErrInvalid(L, "mobj_t"); + + LUA_PushUserdata(L, K_SearchWaypointGraphForMobj(mobj), META_WAYPOINT); + return 1; +} + +static int lib_kSearchWaypointHeapForMobj(lua_State *L) +{ + mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + + INLEVEL + if (!mobj) + return LUA_ErrInvalid(L, "mobj_t"); + + LUA_PushUserdata(L, K_SearchWaypointHeapForMobj(mobj), META_WAYPOINT); + return 1; +} + static int lib_getTimeMicros(lua_State *L) { lua_pushinteger(L, I_GetPreciseTime() / (I_GetPrecisePrecision() / 1000000)); @@ -4672,6 +5012,28 @@ static luaL_Reg lib[] = { {"K_AwardPlayerRings", lib_kAwardPlayerRings}, {"K_AwardScaledPlayerRings", lib_kAwardScaledPlayerRings}, + // k_waypoint + {"K_GetFinishLineWaypoint", lib_kGetFinishLineWaypoint}, + {"K_GetStartingWaypoint", lib_kGetStartingWaypoint}, + {"K_GetWaypointIsFinishline", lib_kGetWaypointIsFinishline}, + {"K_GetWaypointIsShortcut", lib_kGetWaypointIsShortcut}, + {"K_GetWaypointIsEnabled", lib_kGetWaypointIsEnabled}, + {"K_SetWaypointIsEnabled", lib_kSetWaypointIsEnabled}, + {"K_GetWaypointIsSpawnpoint", lib_kGetWaypointIsSpawnpoint}, + {"K_GetWaypointNextID", lib_kGetWaypointNextID}, + {"K_GetWaypointID", lib_kGetWaypointID}, + {"K_GetWaypointFromID", lib_kGetWaypointFromID}, + {"K_GetCircuitLength", lib_kGetCircuitLength}, + {"K_GetTrackComplexity", lib_kGetTrackComplexity}, + {"K_GetClosestWaypointToMobj", lib_kGetClosestWaypointToMobj}, + {"K_GetBestWaypointForMobj", lib_kGetBestWaypointForMobj}, + {"K_PathfindToWaypoint", lib_kPathfindToWaypoint}, + {"K_PathfindThruCircuit", lib_kPathfindThruCircuit}, + {"K_PathfindThruCircuitSpawnable", lib_kPathfindThruCircuitSpawnable}, + {"K_GetNextWaypointToDestination", lib_kGetNextWaypointToDestination}, + {"K_SearchWaypointGraphForMobj", lib_kSearchWaypointGraphForMobj}, + {"K_SearchWaypointHeapForMobj", lib_kSearchWaypointHeapForMobj}, + // k_boss {"K_InitBossHealthBar", lib_kInitBossHealthBar}, {"K_UpdateBossHealthBar", lib_kUpdateBossHealthBar}, diff --git a/src/lua_libs.h b/src/lua_libs.h index de58fd58b..d0d9f4b60 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -105,6 +105,7 @@ extern lua_State *gL; #define META_ACTIVATOR "ACTIVATOR_T*" #define META_FOLLOWER "FOLLOWER_T*" +#define META_WAYPOINT "WAYPOINT_T*" #define META_SPLASH "T_SPLASH_T*" #define META_FOOTSTEP "T_FOOTSTEP_T*" @@ -134,6 +135,7 @@ int LUA_BlockmapLib(lua_State *L); int LUA_HudLib(lua_State *L); int LUA_FollowerLib(lua_State *L); int LUA_TerrainLib(lua_State *L); +int LUA_WaypointLib(lua_State *L); #ifdef __cplusplus } // extern "C" diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 38310c681..92cd32d23 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -193,7 +193,10 @@ enum player_e player_nextcheck, player_distancetofinish, player_distancetofinishprev, + player_currentwaypoint, + player_nextwaypoint, player_airtime, + player_bigwaypointgap, player_flashing, player_spinouttimer, player_spinouttype, @@ -381,7 +384,10 @@ static const char *const player_opt[] = { "nextcheck", "distancetofinish", "distancetofinishprev", + "currentwaypoint", + "nextwaypoint", "airtime", + "bigwaypointgap", "flashing", "spinouttimer", "spinouttype", @@ -650,6 +656,9 @@ static int player_get(lua_State *L) case player_airtime: lua_pushinteger(L, plr->airtime); break; + case player_bigwaypointgap: + lua_pushinteger(L, plr->bigwaypointgap); + break; case player_flashing: lua_pushinteger(L, plr->flashing); break; @@ -1295,12 +1304,23 @@ static int player_set(lua_State *L) plr->nextcheck = luaL_checkinteger(L, 3); break; case player_distancetofinish: - return NOSET; + plr->distancetofinish = luaL_checkfixed(L, 3); + break; case player_distancetofinishprev: - return NOSET; + plr->distancetofinishprev = luaL_checkfixed(L, 3); + break; + case player_currentwaypoint: + NOSET; + break; + case player_nextwaypoint: + NOSET; + break; case player_airtime: plr->airtime = luaL_checkinteger(L, 3); break; + case player_bigwaypointgap: + plr->bigwaypointgap = luaL_checkinteger(L, 3); + break; case player_flashing: plr->flashing = luaL_checkinteger(L, 3); break; diff --git a/src/lua_script.c b/src/lua_script.c index 43fb73b97..4d187cae5 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -61,6 +61,7 @@ static lua_CFunction liblist[] = { LUA_HudLib, // HUD stuff LUA_FollowerLib, // follower_t, followers[] LUA_TerrainLib, // t_splash_t, t_footstep_t, t_overlay_t, terrain_t + LUA_WaypointLib, // waypoint_t NULL }; @@ -1031,6 +1032,10 @@ void LUA_InvalidateLevel(void) LUA_InvalidateUserdata(nodes[i].children); } #endif + for (i = 0; i < K_GetNumWaypoints(); i++) + { + LUA_InvalidateUserdata(K_GetWaypointFromIndex(i)); + } } void LUA_InvalidateMapthings(void) diff --git a/src/lua_waypointslib.c b/src/lua_waypointslib.c new file mode 100644 index 000000000..b45158600 --- /dev/null +++ b/src/lua_waypointslib.c @@ -0,0 +1,294 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2024 by Kart Krew. +// Copyright (C) 2020 by Sonic Team Junior. +// Copyright (C) 2016 by John "JTE" Muniz. +// +// 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_waypointslib.c +/// \brief wapoint structure library for Lua scripting + +#include "doomdef.h" +#include "fastcmp.h" + +#include "lua_script.h" +#include "lua_libs.h" +#include "p_local.h" + +enum waypointvars { + waypointvars_valid = 0, + waypointvars_mobj, + waypointvars_x, + waypointvars_y, + waypointvars_z, + waypointvars_onaline, + waypointvars_nextwaypoints, + waypointvars_prevwaypoints, + waypointvars_nextwaypointdistances, + waypointvars_prevwaypointdistances, + waypointvars_numnextwaypoints, + waypointvars_numprevwaypoints, +}; + +static const char *const waypointvars_opt[] = { + "valid", + "mobj", + "x", + "y", + "z", + "onaline", + "nextwaypoints", + "prevwaypoints", + "nextwaypointdistances", + "prevwaypointdistances", + "numnextwaypoints", + "numprevwaypoints", + NULL +}; + +#define RNOFIELD luaL_error(L, LUA_QL("waypoint_t") " has no field named " LUA_QS, field) +#define RNOSET luaL_error(L, LUA_QL("waypoint_t") " field " LUA_QS " cannot be set.", field) +#define RNOGET luaL_error(L, LUA_QL("waypoint_t") " field " LUA_QS " cannot be get.", field) + +static int waypoint_get(lua_State *L) +{ + waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + enum waypointvars field = luaL_checkoption(L, 2, NULL, waypointvars_opt); + + if (!waypoint) + { + switch (field) + { + case waypointvars_valid: + lua_pushboolean(L, false); + return 1; + default: + return LUA_ErrInvalid(L, "waypoint_t"); + } + } + + // It could totally happen that some scripter just deletes the mobj, + // which would have us check a null pointer, so we're checking against that + // just in case. + mobj_t *waypointMobj = NULL; + if (waypoint->mobj != NULL && P_MobjWasRemoved(waypoint->mobj) == false) + waypointMobj = waypoint->mobj; + + switch (field) + { + case waypointvars_valid: + lua_pushboolean(L, true); + break; + case waypointvars_mobj: + return RNOGET; + case waypointvars_x: + if (waypointMobj) + lua_pushfixed(L, waypointMobj->x); + else + lua_pushnil(L); + break; + case waypointvars_y: + if (waypointMobj) + lua_pushinteger(L, waypointMobj->y); + else + lua_pushnil(L); + break; + case waypointvars_z: + if (waypointMobj) + lua_pushinteger(L, waypointMobj->z); + else + lua_pushnil(L); + break; + case waypointvars_onaline: + lua_pushboolean(L, waypoint->onaline); + break; + case waypointvars_nextwaypoints: + lua_createtable(L, waypoint->numnextwaypoints, 0); + for (size_t i = 0; i < waypoint->numnextwaypoints; i++) + { + LUA_PushUserdata(L, waypoint->nextwaypoints[i], META_WAYPOINT); + lua_rawseti(L, -2, 1 + i); + } + break; + case waypointvars_prevwaypoints: + lua_createtable(L, waypoint->numprevwaypoints, 0); + for (size_t i = 0; i < waypoint->numprevwaypoints; i++) + { + LUA_PushUserdata(L, waypoint->prevwaypoints[i], META_WAYPOINT); + lua_rawseti(L, -2, 1 + i); + } + break; + case waypointvars_nextwaypointdistances: + lua_createtable(L, waypoint->numnextwaypoints, 0); + for (size_t i = 0; i < waypoint->numnextwaypoints; i++) + { + lua_pushinteger(L, waypoint->nextwaypointdistances[i]); + lua_rawseti(L, -2, 1 + i); + } + break; + case waypointvars_prevwaypointdistances: + lua_createtable(L, waypoint->numprevwaypoints, 0); + for (size_t i = 0; i < waypoint->numprevwaypoints; i++) + { + lua_pushinteger(L, waypoint->prevwaypointdistances[i]); + lua_rawseti(L, -2, 1 + i); + } + break; + case waypointvars_numnextwaypoints: + lua_pushinteger(L, waypoint->numnextwaypoints); + break; + case waypointvars_numprevwaypoints: + lua_pushinteger(L, waypoint->numprevwaypoints); + break; + default: + return RNOFIELD; + } + + return 1; +} + +static int waypoint_set(lua_State *L) +{ + waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + enum waypointvars field = luaL_checkoption(L, 2, waypointvars_opt[0], waypointvars_opt); + + if (!waypoint) + return LUA_ErrInvalid(L, "waypoint_t"); + + INLEVEL + + switch (field) + { + case waypointvars_mobj: + return RNOSET; + case waypointvars_x: + return RNOSET; + case waypointvars_y: + return RNOSET; + case waypointvars_z: + return RNOSET; + case waypointvars_onaline: + return RNOSET; + // A function should be used to set these instead. + case waypointvars_nextwaypoints: + return RNOSET; + case waypointvars_prevwaypoints: + return RNOSET; + case waypointvars_nextwaypointdistances: + return RNOSET; + case waypointvars_prevwaypointdistances: + return RNOSET; + case waypointvars_numnextwaypoints: + return RNOSET; + case waypointvars_numprevwaypoints: + return RNOSET; + default: + return RNOFIELD; + } + + return 0; +} + +#undef RNOSET +#undef RNOFIELD + +static int waypoint_num(lua_State *L) +{ + waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); + + if (!waypoint) + return LUA_ErrInvalid(L, "waypoint_t"); + + lua_pushinteger(L, K_GetWaypointHeapIndex(waypoint)); + return 1; +} + +static int lib_iterateWaypoints(lua_State *L) +{ + size_t i; + + if (lua_gettop(L) < 2) + { + lua_pushcfunction(L, lib_iterateWaypoints); + return 1; + } + + lua_settop(L, 2); + lua_remove(L, 1); // state is unused. + + if (!lua_isnil(L, 1)) + i = K_GetWaypointHeapIndex(*(waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)) + 1; + else + i = 0; + + if (i < K_GetNumWaypoints()) + { + LUA_PushUserdata(L, K_GetWaypointFromIndex(i), META_WAYPOINT); + return 1; + } + + return 0; +} + +static int lib_getWaypoint(lua_State *L) +{ + const char *field; + size_t i; + + // find waypoint by number + if (lua_type(L, 2) == LUA_TNUMBER) + { + i = luaL_checkinteger(L, 2); + if (i >= K_GetNumWaypoints()) + return luaL_error(L, "waypoints[] index %d out of range (0 - %d)", i, K_GetNumWaypoints()-1); + LUA_PushUserdata(L, K_GetWaypointFromIndex(i), META_WAYPOINT); + return 1; + } + + field = luaL_checkstring(L, 2); + + // special function iterate + if (fastcmp(field,"iterate")) + { + lua_pushcfunction(L, lib_iterateWaypoints); + return 1; + } + + return 0; +} + +static int lib_numWaypoints(lua_State *L) +{ + lua_pushinteger(L, K_GetNumWaypoints()); + return 1; +} + + +int LUA_WaypointLib(lua_State *L) +{ + luaL_newmetatable(L, META_WAYPOINT); + lua_pushcfunction(L, waypoint_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, waypoint_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, waypoint_num); + lua_setfield(L, -2, "__len"); + lua_pop(L,1); + + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, lib_getWaypoint); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_numWaypoints); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "waypoints"); + + return 0; +}