Merge branch 'respawnWaypointsLib' into 'master'

Respawn/Waypoints library for Lua

Closes #12 and #311

See merge request KartKrew/RingRacers!81
This commit is contained in:
Eidolon 2025-08-23 00:14:38 +00:00
commit a3fc5829bc
12 changed files with 1020 additions and 98 deletions

View file

@ -125,6 +125,8 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
lua_hudlib.c
lua_hudlib_drawlist.c
lua_followerlib.c
lua_respawnvarslib.c
lua_waypointslib.c
lua_profile.cpp
k_kart.c
k_respawn.c

View file

@ -26,6 +26,8 @@
#include "k_boss.h" // spottype_t (for lua)
#include "k_follower.h" // followermode_t (for lua)
#include "music.h" // tune flags (for lua)
#include "k_respawn.h" // respawn values (for lua)
#include "k_waypoint.h" // waypoint values (for lua)
#include "deh_tables.h"
@ -5214,6 +5216,16 @@ struct int_const_s const INT_CONST[] = {
{"TN_NIGHTCOREABLE",TN_NIGHTCOREABLE},
{"TN_CHANGEPITCH",TN_CHANGEPITCH},
{"TN_LOOPING",TN_LOOPING},
// k_respawn.h values
{"RESPAWN_DIST",RESPAWN_DIST},
{"RESPAWN_TIME",RESPAWN_TIME},
{"RESPAWNST_NONE",RESPAWNST_NONE},
{"RESPAWNST_MOVE",RESPAWNST_MOVE},
{"RESPAWNST_DROP",RESPAWNST_DROP},
// k_waypoint.h values
{"DEFAULT_WAYPOINT_RADIUS",DEFAULT_WAYPOINT_RADIUS},
// k_bot.h constants
{"MAXBOTDIFFICULTY",MAXBOTDIFFICULTY},

View file

@ -61,18 +61,11 @@ static void K_FudgeRespawn(player_t *player, const waypoint_t *const waypoint)
}
/*--------------------------------------------------
static void K_RespawnAtWaypoint(player_t *player, waypoint_t *waypoint)
void K_RespawnAtWaypoint(player_t *player, waypoint_t *waypoint)
Updates a player's respawn variables to go to the provided waypoint.
Input Arguments:-
player - Player to preform for.
waypoint - Waypoint to respawn to.
Return:-
None
See header file for description.
--------------------------------------------------*/
static void K_RespawnAtWaypoint(player_t *player, waypoint_t *waypoint)
void K_RespawnAtWaypoint(player_t *player, waypoint_t *waypoint)
{
if (player == NULL || player->mo == NULL || P_MobjWasRemoved(player->mo))
{

View file

@ -42,6 +42,19 @@ extern "C" {
fixed_t K_RespawnOffset(player_t *player, boolean flip);
/*--------------------------------------------------
void K_RespawnAtWaypoint(player_t *player, waypoint_t *waypoint)
Updates a player's respawn variables to go to the provided waypoint.
Input Arguments:-
player - Player to preform for.
waypoint - Waypoint to respawn to.
Return:-
None
--------------------------------------------------*/
void K_RespawnAtWaypoint(player_t *player, waypoint_t *waypoint);
/*--------------------------------------------------
void K_DoFault(player_t *player);

View file

@ -149,6 +149,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)
@ -160,11 +181,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
{
@ -514,7 +535,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
{

View file

@ -115,6 +115,18 @@ 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)

View file

@ -33,6 +33,8 @@
#include "k_color.h"
#include "k_endcam.h"
#include "k_hud.h"
#include "k_waypoint.h"
#include "k_respawn.h"
#include "k_specialstage.h"
#include "d_netcmd.h" // IsPlayerAdmin
#include "k_menu.h" // Player Setup menu color stuff
@ -5480,6 +5482,392 @@ static int lib_kRemoveBot(lua_State *L)
return 0;
}
static int lib_kRespawnOffset(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
boolean flip = lua_optboolean(L, 2);
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
lua_pushfixed(L, K_RespawnOffset(player, flip));
return 1;
}
static int lib_kRespawnAtWaypoint(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
waypoint_t *wp = *((waypoint_t **)luaL_checkudata(L, 2, META_WAYPOINT));
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
if (!wp)
return LUA_ErrInvalid(L, "waypoint_t");
K_RespawnAtWaypoint(player, wp);
return 0;
}
static int lib_kDoFault(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
K_DoFault(player);
return 0;
}
static int lib_kDoIngameRespawn(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
K_DoIngameRespawn(player);
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));
@ -5993,6 +6381,27 @@ static luaL_Reg lib[] = {
{"VS_PredictAroundArena", lib_vsPredictAroundArena},
{"VS_RandomPointOnArena", lib_vsRandomPointOnArena},
// k_respawn
{"K_RespawnOffset", lib_kRespawnOffset},
{"K_RespawnAtWaypoint", lib_kRespawnAtWaypoint},
{"K_DoFault", lib_kDoFault},
{"K_DoIngameRespawn", lib_kDoIngameRespawn},
{"K_NextRespawnWaypointIndex", lib_kNextRespawnWaypointIndex},
// 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_bot
{"K_PlayerUsesBotMovement", lib_kPlayerUsesBotMovement},
{"K_BotCanTakeCut", lib_kBotCanTakeCut},
@ -6005,6 +6414,14 @@ static luaL_Reg lib[] = {
{"K_SetNameForBot", lib_kSetNameForBot},
// Lua-only function to allow safely removing bots.
{"K_RemoveBot", lib_kRemoveBot},
{"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},
// hu_stuff technically?
{"HU_DoTitlecardCEcho", lib_startTitlecardCecho},

View file

@ -103,6 +103,7 @@ extern lua_State *gL;
#define META_ACTIVATOR "ACTIVATOR_T*"
#define META_FOLLOWER "FOLLOWER_T*"
#define META_WAYPOINT "WAYPOINT_T*"
#define META_SONICLOOPVARS "SONICLOOPVARS_T*"
#define META_SONICLOOPCAMVARS "SONICLOOPCAMVARS_T*"
@ -136,6 +137,8 @@ int LUA_HudLib(lua_State *L);
int LUA_FollowerLib(lua_State *L);
int LUA_BotVarsLib(lua_State *L);
int LUA_TerrainLib(lua_State *L);
int LUA_RespawnVarsLib(lua_State *L);
int LUA_WaypointLib(lua_State *L);
#ifdef __cplusplus
} // extern "C"

View file

@ -219,10 +219,16 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->lastpickupdistance);
else if (fastcmp(field,"lastpickuptype"))
lua_pushinteger(L, plr->lastpickuptype);
else if (fastcmp(field,"currentwaypoint"))
LUA_PushUserdata(L, plr->currentwaypoint, META_WAYPOINT);
else if (fastcmp(field,"nextwaypoint"))
LUA_PushUserdata(L, plr->nextwaypoint, META_WAYPOINT);
else if (fastcmp(field,"airtime"))
lua_pushinteger(L, plr->airtime);
else if (fastcmp(field,"lastairtime"))
lua_pushinteger(L, plr->lastairtime);
else if (fastcmp(field,"bigwaypointgap"))
lua_pushinteger(L, plr->bigwaypointgap);
else if (fastcmp(field,"flashing"))
lua_pushinteger(L, plr->flashing);
else if (fastcmp(field,"spinouttimer"))
@ -283,6 +289,8 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->gateSound);
else if (fastcmp(field,"startboost"))
lua_pushinteger(L, plr->startboost);
else if (fastcmp(field,"dropdashboost"))
lua_pushinteger(L, plr->dropdashboost);
else if (fastcmp(field,"aizdriftstrat"))
lua_pushinteger(L, plr->aizdriftstrat);
else if (fastcmp(field,"aizdriftextend"))
@ -788,15 +796,17 @@ static int player_set(lua_State *L)
else if (fastcmp(field,"positiondelay"))
plr->positiondelay = luaL_checkinteger(L, 3);
else if (fastcmp(field,"distancetofinish"))
return NOSET;
plr->distancetofinish = luaL_checkfixed(L, 3);
else if (fastcmp(field,"distancetofinishprev"))
return NOSET;
plr->distancetofinishprev = luaL_checkfixed(L, 3);
else if (fastcmp(field,"lastpickupdistance"))
plr->lastpickupdistance = luaL_checkinteger(L, 3);
else if (fastcmp(field,"airtime"))
plr->airtime = luaL_checkinteger(L, 3);
else if (fastcmp(field,"lastairtime"))
plr->lastairtime = luaL_checkinteger(L, 3);
else if (fastcmp(field,"bigwaypointgap"))
plr->bigwaypointgap = luaL_checkinteger(L, 3);
else if (fastcmp(field,"flashing"))
plr->flashing = luaL_checkinteger(L, 3);
else if (fastcmp(field,"spinouttimer"))
@ -857,6 +867,8 @@ static int player_set(lua_State *L)
plr->gateSound = luaL_checkinteger(L, 3);
else if (fastcmp(field,"startboost"))
plr->startboost = luaL_checkinteger(L, 3);
else if (fastcmp(field,"dropdashboost"))
plr->dropdashboost = luaL_checkinteger(L, 3);
else if (fastcmp(field,"aizdriftstrat"))
plr->aizdriftstrat = luaL_checkinteger(L, 3);
else if (fastcmp(field,"aizdrifttilt"))
@ -1385,81 +1397,6 @@ static int ticcmd_set(lua_State *L)
#undef NOFIELD
// Same shit for player.respawn variable... Why is everything in different sub-variables again now???
#define RNOFIELD luaL_error(L, LUA_QL("respawnvars_t") " has no field named " LUA_QS, field)
#define RUNIMPLEMENTED luaL_error(L, LUA_QL("respawnvars_t") " unimplemented field " LUA_QS " cannot be read or set.", field)
// @TODO: Waypoints in Lua possibly maybe? No don't count on me to do it...
static int respawn_get(lua_State *L)
{
respawnvars_t *rsp = *((respawnvars_t **)luaL_checkudata(L, 1, META_RESPAWN));
const char *field = luaL_checkstring(L, 2);
if (!rsp)
return LUA_ErrInvalid(L, "player_t");
if (fastcmp(field,"state"))
lua_pushinteger(L, rsp->state);
else if (fastcmp(field,"waypoint"))
return RUNIMPLEMENTED;
else if (fastcmp(field,"pointx"))
lua_pushfixed(L, rsp->pointx);
else if (fastcmp(field,"pointy"))
lua_pushfixed(L, rsp->pointy);
else if (fastcmp(field,"pointz"))
lua_pushfixed(L, rsp->pointz);
else if (fastcmp(field,"flip"))
lua_pushboolean(L, rsp->flip);
else if (fastcmp(field,"timer"))
lua_pushinteger(L, rsp->timer);
else if (fastcmp(field,"distanceleft"))
lua_pushinteger(L, rsp->distanceleft); // Can't possibly foresee any problem when pushing UINT32 to Lua's INT32 hahahahaha, get ready for dumb hacky shit on high distances.
else if (fastcmp(field,"dropdash"))
lua_pushinteger(L, rsp->dropdash);
else
return RNOFIELD;
return 1;
}
static int respawn_set(lua_State *L)
{
respawnvars_t *rsp = *((respawnvars_t **)luaL_checkudata(L, 1, META_RESPAWN));
const char *field = luaL_checkstring(L, 2);
if (!rsp)
return LUA_ErrInvalid(L, "respawnvars_t");
if (hud_running)
return luaL_error(L, "Do not alter player_t in HUD rendering code!");
if (hook_cmd_running)
return luaL_error(L, "Do not alter player_t in CMD building code!");
if (fastcmp(field,"state"))
rsp->state = (UINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"waypoint"))
return RUNIMPLEMENTED;
else if (fastcmp(field,"pointx"))
rsp->pointx = luaL_checkfixed(L, 3);
else if (fastcmp(field,"pointy"))
rsp->pointy = luaL_checkfixed(L, 3);
else if (fastcmp(field,"pointz"))
rsp->pointz = luaL_checkfixed(L, 3);
else if (fastcmp(field,"flip"))
rsp->flip = luaL_checkboolean(L, 3);
else if (fastcmp(field,"timer"))
rsp->timer = (tic_t)luaL_checkinteger(L, 3);
else if (fastcmp(field,"distanceleft"))
rsp->distanceleft = (UINT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"dropdash"))
rsp->dropdash = (tic_t)luaL_checkinteger(L, 3);
else
return RNOFIELD;
return 0;
}
#undef RNOFIELD
#undef RUNIMPLEMENTED
enum sonicloopvars {
sonicloopvars_radius = 0,
sonicloopvars_revolution,
@ -1641,14 +1578,6 @@ int LUA_PlayerLib(lua_State *L)
lua_setfield(L, -2, "__len");
lua_pop(L,1);
luaL_newmetatable(L, META_RESPAWN);
lua_pushcfunction(L, respawn_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, respawn_set);
lua_setfield(L, -2, "__newindex");
lua_pop(L,1);
luaL_newmetatable(L, META_TICCMD);
lua_pushcfunction(L, ticcmd_get);
lua_setfield(L, -2, "__index");

221
src/lua_respawnvarslib.c Normal file
View file

@ -0,0 +1,221 @@
// 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_respawnvarslib.c
/// \brief player respawn variables library for Lua scripting
#include "doomdef.h"
#include "fastcmp.h"
#include "lua_script.h"
#include "lua_libs.h"
#include "lua_hud.h" // hud_running errors
#include "lua_hook.h" // hook_cmd_running errors
enum respawnvars {
respawnvars_state = 0,
respawnvars_waypoint,
respawnvars_pointx,
respawnvars_pointy,
respawnvars_pointz,
respawnvars_pointangle,
respawnvars_flip,
respawnvars_timer,
respawnvars_airtimer,
respawnvars_distanceleft,
respawnvars_dropdash,
respawnvars_truedeath,
respawnvars_manual,
respawnvars_fromringshooter,
respawnvars_init,
respawnvars_fast,
respawnvars_returnspeed,
};
static const char *const respawnvars_opt[] = {
"state",
"waypoint",
"pointx",
"pointy",
"pointz",
"pointangle",
"flip",
"timer",
"airtimer",
"distanceleft",
"dropdash",
"truedeath",
"manual",
"fromringshooter",
"init",
"fast",
"returnspeed",
NULL
};
#define RNOFIELD luaL_error(L, LUA_QL("respawnvars_t") " has no field named " LUA_QS, field)
#define RUNIMPLEMENTED luaL_error(L, LUA_QL("respawnvars_t") " unimplemented field " LUA_QS " cannot be read or set.", field)
static int respawn_get(lua_State *L)
{
respawnvars_t *rsp = *((respawnvars_t **)luaL_checkudata(L, 1, META_RESPAWN));
enum respawnvars field = luaL_checkoption(L, 2, NULL, respawnvars_opt);
if (!rsp)
return LUA_ErrInvalid(L, "player_t");
switch (field)
{
case respawnvars_state:
lua_pushinteger(L, rsp->state);
break;
case respawnvars_waypoint:
LUA_PushUserdata(L, rsp->wp, META_WAYPOINT);
break;
case respawnvars_pointx:
lua_pushfixed(L, rsp->pointx);
break;
case respawnvars_pointy:
lua_pushfixed(L, rsp->pointy);
break;
case respawnvars_pointz:
lua_pushfixed(L, rsp->pointz);
break;
case respawnvars_pointangle:
lua_pushangle(L, rsp->pointangle);
break;
case respawnvars_flip:
lua_pushboolean(L, rsp->flip);
break;
case respawnvars_timer:
lua_pushinteger(L, rsp->timer);
break;
case respawnvars_airtimer:
lua_pushinteger(L, rsp->airtimer);
break;
case respawnvars_distanceleft:
lua_pushinteger(L, rsp->distanceleft);
break;
case respawnvars_dropdash:
lua_pushinteger(L, rsp->dropdash);
break;
case respawnvars_truedeath:
lua_pushboolean(L, rsp->truedeath);
break;
case respawnvars_manual:
lua_pushboolean(L, rsp->manual);
break;
case respawnvars_fromringshooter:
lua_pushboolean(L, rsp->fromRingShooter);
break;
case respawnvars_init:
lua_pushboolean(L, rsp->init);
break;
case respawnvars_fast:
lua_pushboolean(L, rsp->fast);
break;
case respawnvars_returnspeed:
lua_pushfixed(L, rsp->returnspeed);
break;
default:
return RNOFIELD;
}
return 1;
}
static int respawn_set(lua_State *L)
{
respawnvars_t *rsp = *((respawnvars_t **)luaL_checkudata(L, 1, META_RESPAWN));
enum respawnvars field = luaL_checkoption(L, 2, respawnvars_opt[0], respawnvars_opt);
if (!rsp)
return LUA_ErrInvalid(L, "respawnvars_t");
if (hud_running)
return luaL_error(L, "Do not alter player_t in HUD rendering code!");
if (hook_cmd_running)
return luaL_error(L, "Do not alter player_t in CMD building code!");
switch (field)
{
case respawnvars_state:
rsp->state = (UINT8)luaL_checkinteger(L, 3);
break;
case respawnvars_waypoint:
rsp->wp = *((waypoint_t **)luaL_checkudata(L, 3, META_WAYPOINT));
break;
case respawnvars_pointx:
rsp->pointx = luaL_checkfixed(L, 3);
break;
case respawnvars_pointy:
rsp->pointy = luaL_checkfixed(L, 3);
break;
case respawnvars_pointz:
rsp->pointz = luaL_checkfixed(L, 3);
break;
case respawnvars_pointangle:
rsp->pointangle = luaL_checkangle(L, 3);
break;
case respawnvars_flip:
rsp->flip = luaL_checkboolean(L, 3);
break;
case respawnvars_timer:
rsp->timer = (tic_t)luaL_checkinteger(L, 3);
break;
case respawnvars_airtimer:
rsp->airtimer = (tic_t)luaL_checkinteger(L, 3);
break;
case respawnvars_distanceleft:
rsp->distanceleft = (UINT32)luaL_checkinteger(L, 3);
break;
case respawnvars_dropdash:
rsp->dropdash = (tic_t)luaL_checkinteger(L, 3);
break;
case respawnvars_truedeath:
rsp->truedeath = luaL_checkboolean(L, 3);
break;
case respawnvars_manual:
rsp->manual = luaL_checkboolean(L, 3);
break;
case respawnvars_fromringshooter:
rsp->fromRingShooter = luaL_checkboolean(L, 3);
break;
case respawnvars_init:
rsp->init = luaL_checkboolean(L, 3);
break;
case respawnvars_fast:
rsp->fast = luaL_checkboolean(L, 3);
break;
case respawnvars_returnspeed:
rsp->returnspeed = luaL_checkfixed(L, 3);
break;
default:
return RNOFIELD;
}
return 0;
}
#undef RNOFIELD
#undef RUNIMPLEMENTED
int LUA_RespawnVarsLib(lua_State *L)
{
luaL_newmetatable(L, META_RESPAWN);
lua_pushcfunction(L, respawn_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, respawn_set);
lua_setfield(L, -2, "__newindex");
lua_pop(L,1);
return 0;
}

View file

@ -63,6 +63,8 @@ static lua_CFunction liblist[] = {
LUA_FollowerLib, // follower_t, followers[]
LUA_BotVarsLib, // botvars_t
LUA_TerrainLib, // t_splash_t, t_footstep_t, t_overlay_t, terrain_t
LUA_RespawnVarsLib, // respawnvars_t
LUA_WaypointLib, // waypoint_t
NULL
};
@ -899,6 +901,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)

293
src/lua_waypointslib.c Normal file
View file

@ -0,0 +1,293 @@
// 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"
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;
}