diff --git a/src/command.c b/src/command.c index 6d9c86d3..dff83c2c 100644 --- a/src/command.c +++ b/src/command.c @@ -56,7 +56,7 @@ static void CV_EnforceExecVersion(void); static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr); static boolean CV_Command(void); -static consvar_t *CV_FindVar(const char *name); +consvar_t *CV_FindVar(const char *name); static const char *CV_StringValue(const char *var_name); static consvar_t *consvar_vars; // list of registered console variables @@ -1027,7 +1027,7 @@ static const char *cv_null_string = ""; * \return Pointer to the variable if found, or NULL. * \sa CV_FindNetVar */ -static consvar_t *CV_FindVar(const char *name) +consvar_t *CV_FindVar(const char *name) { consvar_t *cvar; diff --git a/src/command.h b/src/command.h index 6b5d513e..484cd426 100644 --- a/src/command.h +++ b/src/command.h @@ -173,4 +173,7 @@ void CV_ResetCheatNetVars(void); boolean CV_IsSetToDefault(consvar_t *v); UINT8 CV_CheatsEnabled(void); +// Returns cvar by name. Exposed here for Lua. +consvar_t *CV_FindVar(const char *name); + #endif // __COMMAND_H__ diff --git a/src/dehacked.c b/src/dehacked.c index b311a860..d6d54b6f 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -9799,8 +9799,7 @@ static inline int lib_getenum(lua_State *L) if (mathlib) return luaL_error(L, "constant '%s' could not be parsed.\n", word); // DYNAMIC variables too!! - // Try not to add anything that would break netgames or timeattack replays here. - // You know, like consoleplayer, displayplayers, or gametime. + if (fastcmp(word,"gamemap")) { lua_pushinteger(L, gamemap); return 1; @@ -9881,24 +9880,23 @@ static inline int lib_getenum(lua_State *L) return 0; LUA_PushUserdata(L, &players[serverplayer], META_PLAYER); return 1; + } else if (fastcmp(word,"consoleplayer")) { // Player controlling the console, basically our local player + if (consoleplayer < 0 || !playeringame[consoleplayer]) + return 0; + LUA_PushUserdata(L, &players[consoleplayer], META_PLAYER); + return 1; /*} else if (fastcmp(word,"admin")) { LUA_Deprecated(L, "admin", "IsPlayerAdmin(player)"); if (!playeringame[adminplayers[0]] || IsPlayerAdmin(serverplayer)) return 0; LUA_PushUserdata(L, &players[adminplayers[0]], META_PLAYER); return 1;*/ - } else if (fastcmp(word,"emeralds")) { - lua_pushinteger(L, emeralds); - return 1; } else if (fastcmp(word,"gravity")) { lua_pushinteger(L, gravity); return 1; } else if (fastcmp(word,"VERSIONSTRING")) { lua_pushstring(L, VERSIONSTRING); return 1; - } else if (fastcmp(word, "token")) { - lua_pushinteger(L, token); - return 1; } else if (fastcmp(word,"gamespeed")) { lua_pushinteger(L, gamespeed); return 1; @@ -9932,6 +9930,12 @@ static inline int lib_getenum(lua_State *L) } else if (fastcmp(word,"numlaps")) { lua_pushinteger(L, cv_numlaps.value); return 1; + } else if (fastcmp(word,"racecountdown")) { + lua_pushinteger(L, countdown); + return 1; + } else if (fastcmp(word,"exitcountdown")) { + lua_pushinteger(L, countdown2); // This name is pretty dumb. Hence why we'll prefer more descriptive names at least in Lua... + return 1; } return 0; } diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 6700d5af..672b4574 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -2641,6 +2641,41 @@ static int lib_kGetItemPatch(lua_State *L) return 1; } +// sets the remaining time before players blow up +static int lib_kSetRaceCountdown(lua_State *L) +{ + tic_t c = (tic_t)luaL_checkinteger(L, 1); + countdown = c; + return 0; +} + +// sets the remaining time before the race ends after everyone finishes +static int lib_kSetExitCountdown(lua_State *L) +{ + tic_t c = (tic_t)luaL_checkinteger(L, 1); + NOHUD + countdown2 = c; + return 0; +} + +// Sets the item cooldown before another shrink / SPB can be rolled +static int lib_kSetIndirectItemCountdown(lua_State *L) +{ + tic_t c = (tic_t)luaL_checkinteger(L, 1); + NOHUD + indirectitemcooldown = c; + return 0; +} + +// Sets the item cooldown before another shrink / SPB can be rolled +static int lib_kSetHyuCountdown(lua_State *L) +{ + tic_t c = (tic_t)luaL_checkinteger(L, 1); + NOHUD + hyubgone = c; + return 0; +} + static luaL_Reg lib[] = { {"print", lib_print}, {"chatprint", lib_chatprint}, @@ -2868,6 +2903,10 @@ static luaL_Reg lib[] = { {"K_GetKartAccel",lib_kGetKartAccel}, {"K_GetKartFlashing",lib_kGetKartFlashing}, {"K_GetItemPatch",lib_kGetItemPatch}, + {"K_SetRaceCountdown",lib_kSetRaceCountdown}, + {"K_SetExitCountdown",lib_kSetExitCountdown}, + {"K_SetIndirectItemCooldown",lib_kSetIndirectItemCountdown}, + {"K_SetHyudoroCooldown",lib_kSetHyuCountdown}, {NULL, NULL} }; diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c index 299870e0..0c73459c 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -413,6 +413,30 @@ static int lib_cvRegisterVar(lua_State *L) return 1; } +// For some reason I couldn't cherry pick this. +// Credits for this function go to james. All hail birb. -Lat' + +static int lib_cvFindVar(lua_State *L) +{ + consvar_t *cv; + if (( cv = CV_FindVar(luaL_checkstring(L,1)) )) + { + lua_settop(L,1);/* We only want one argument in the stack. */ + lua_pushlightuserdata(L, cv);/* Now the second value on stack. */ + luaL_getmetatable(L, META_CVAR); + /* + The metatable is the last value on the stack, so this + applies it to the second value, which is the cvar. + */ + lua_setmetatable(L,2); + lua_pushvalue(L,2); + return 1; + } + else + return 0; +} + + // CONS_Printf for a single player // Use 'print' in baselib for a global message. static int lib_consPrintf(lua_State *L) @@ -452,6 +476,7 @@ static luaL_Reg lib[] = { {"COM_BufInsertText", lib_comBufInsertText}, {"CV_RegisterVar", lib_cvRegisterVar}, {"CONS_Printf", lib_consPrintf}, + {"CV_FindVar", lib_cvFindVar}, {NULL, NULL} }; diff --git a/src/lua_hook.h b/src/lua_hook.h index e61acdf1..a12998bd 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -52,6 +52,7 @@ enum hook { hook_PlayerSquish, //SRB2KART hook_PlayerCmd, //SRB2KART hook_IntermissionThinker, //SRB2KART + hook_VoteThinker, //SRB2KART hook_MAX // last hook }; @@ -101,5 +102,6 @@ boolean LUAh_PlayerSquish(player_t *player, mobj_t *inflictor, mobj_t *source); boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd); // Allows to write to player cmd before the game does anything with them. void LUAh_IntermissionThinker(void); // Hook for Y_Ticker +void LUAh_VoteThinker(void); // Hook for Y_VoteTicker #endif diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index c15d13a0..009a16dd 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -63,6 +63,7 @@ const char *const hookNames[hook_MAX+1] = { "PlayerSquish", "PlayerCmd", "IntermissionThinker", + "VoteThinker", NULL }; @@ -442,6 +443,27 @@ void LUAh_IntermissionThinker(void) } } +// Hook for Y_VoteTicker +void LUAh_VoteThinker(void) +{ + hook_p hookp; + if (!gL || !(hooksAvailable[hook_VoteThinker/8] & (1<<(hook_VoteThinker%8)))) + return; + + for (hookp = roothook; hookp; hookp = hookp->next) + if (hookp->type == hook_VoteThinker) + { + lua_pushfstring(gL, FMT_HOOKID, hookp->id); + lua_gettable(gL, LUA_REGISTRYINDEX); + if (lua_pcall(gL, 0, 0, 0)) { + if (!hookp->error || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + hookp->error = true; + } + } +} + // Hook for mobj collisions UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 3cca1f91..d9766513 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -81,6 +81,88 @@ static int lib_lenPlayer(lua_State *L) return 1; } +// Same deal as the three functions above but for displayplayers + +static int lib_iterateDisplayplayers(lua_State *L) +{ + INT32 i = -1; + INT32 temp = -1; + INT32 iter = 0; + + if (lua_gettop(L) < 2) + { + //return luaL_error(L, "Don't call displayplayers.iterate() directly, use it as 'for player in displayplayers.iterate do end'."); + lua_pushcfunction(L, lib_iterateDisplayplayers); + return 1; + } + lua_settop(L, 2); + lua_remove(L, 1); // state is unused. + if (!lua_isnil(L, 1)) + { + temp = (INT32)(*((player_t **)luaL_checkudata(L, 1, META_PLAYER)) - players); // get the player # of the last iterated player. + + // @FIXME: + // I didn't quite find a better way for this; Here, we go back to which player in displayplayers we last iterated to resume the for loop below for this new function call + // I don't understand enough about how the Lua stacks work to get this to work in possibly a single line. + // So anyone feel free to correct this! + + for (; iter < MAXSPLITSCREENPLAYERS; iter++) + { + if (displayplayers[iter] == temp) + { + i = iter; + break; + } + } + } + + for (i++; i < MAXSPLITSCREENPLAYERS; i++) + { + if (!playeringame[displayplayers[i]] || i > splitscreen) + return 0; // Stop! There are no more players for us to go through. There will never be a player gap in displayplayers. + + if (!players[displayplayers[i]].mo) + continue; + LUA_PushUserdata(L, &players[displayplayers[i]], META_PLAYER); + lua_pushinteger(L, i); // push this to recall what number we were on for the next function call. I suppose this also means you can retrieve the splitscreen player number with 'for p, n in displayplayers.iterate'! + return 2; + } + return 0; +} + +static int lib_getDisplayplayers(lua_State *L) +{ + const char *field; + // i -> players[i] + if (lua_type(L, 2) == LUA_TNUMBER) + { + lua_Integer i = luaL_checkinteger(L, 2); + if (i < 0 || i >= MAXSPLITSCREENPLAYERS) + return luaL_error(L, "displayplayers[] index %d out of range (0 - %d)", i, MAXSPLITSCREENPLAYERS-1); + if (!playeringame[displayplayers[i]]) + return 0; + if (!players[displayplayers[i]].mo) + return 0; + LUA_PushUserdata(L, &players[displayplayers[i]], META_PLAYER); + return 1; + } + + field = luaL_checkstring(L, 2); + if (fastcmp(field,"iterate")) + { + lua_pushcfunction(L, lib_iterateDisplayplayers); + return 1; + } + return 0; +} + +// #displayplayers -> MAXSPLITSCREENPLAYERS +static int lib_lenDisplayplayers(lua_State *L) +{ + lua_pushinteger(L, MAXSPLITSCREENPLAYERS); + return 1; +} + static int player_get(lua_State *L) { player_t *plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); @@ -777,6 +859,18 @@ int LUA_PlayerLib(lua_State *L) lua_setfield(L, -2, "__len"); lua_setmetatable(L, -2); lua_setglobal(L, "players"); + + // push displayplayers in the same fashion + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, lib_getDisplayplayers); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_lenDisplayplayers); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "displayplayers"); + return 0; } diff --git a/src/y_inter.c b/src/y_inter.c index 379694a1..29b681f7 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1251,6 +1251,10 @@ void Y_VoteTicker(void) if (paused || P_AutoPause() || !voteclient.loaded) return; +#ifdef HAVE_BLUA + LUAh_VoteThinker(); +#endif + votetic++; if (votetic == voteendtic)