From ff62a0732a7f8d9f61b885dd3a3771507ca7d0d8 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Fri, 15 Nov 2019 15:35:28 +0100 Subject: [PATCH 1/5] Let players move after completing the level This only takes effect in co-op, and can be disabled with "exitmove off" --- src/d_netcmd.c | 16 ++++++++++++++++ src/d_netcmd.h | 2 +- src/d_player.h | 1 + src/dehacked.c | 1 + src/g_game.c | 25 +++++++++++++++++++++++++ src/g_game.h | 1 + src/lua_baselib.c | 20 ++++++++++++++++++++ src/p_local.h | 1 + src/p_spec.c | 2 +- src/p_user.c | 35 ++++++++++++++++++++++++++++++++--- 10 files changed, 99 insertions(+), 5 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b14f92b33..a5191331a 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -87,6 +87,7 @@ static void JoinTimeout_OnChange(void); static void CoopStarposts_OnChange(void); static void CoopLives_OnChange(void); +static void ExitMove_OnChange(void); static void Ringslinger_OnChange(void); static void Gravity_OnChange(void); @@ -355,9 +356,12 @@ consvar_t cv_cooplives = {"cooplives", "Avoid Game Over", CV_NETVAR|CV_CALL|CV_C static CV_PossibleValue_t advancemap_cons_t[] = {{0, "Off"}, {1, "Next"}, {2, "Random"}, {0, NULL}}; consvar_t cv_advancemap = {"advancemap", "Next", CV_NETVAR, advancemap_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + static CV_PossibleValue_t playersforexit_cons_t[] = {{0, "One"}, {1, "1/4"}, {2, "Half"}, {3, "3/4"}, {4, "All"}, {0, NULL}}; consvar_t cv_playersforexit = {"playersforexit", "All", CV_NETVAR, playersforexit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_exitmove = {"exitmove", "On", CV_NETVAR|CV_CALL, CV_OnOff, ExitMove_OnChange, 0, NULL, NULL, 0, 0, NULL}; + consvar_t cv_runscripts = {"runscripts", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_pause = {"pausepermission", "Server", CV_NETVAR, pause_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -511,6 +515,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_inttime); CV_RegisterVar(&cv_advancemap); CV_RegisterVar(&cv_playersforexit); + CV_RegisterVar(&cv_exitmove); CV_RegisterVar(&cv_timelimit); CV_RegisterVar(&cv_playbackspeed); CV_RegisterVar(&cv_forceskin); @@ -3634,6 +3639,17 @@ static void CoopLives_OnChange(void) } } +static void ExitMove_OnChange(void) +{ + if (!(netgame || multiplayer) || gametype != GT_COOP) + return; + + if (cv_exitmove.value) + CONS_Printf(M_GetText("Players can now move after completing the level.\n")); + else + CONS_Printf(M_GetText("Players can no longer move after completing the level.\n")); +} + UINT32 timelimitintics = 0; /** Deals with a timelimit change by printing the change to the console. diff --git a/src/d_netcmd.h b/src/d_netcmd.h index e789e5b50..d3d73602c 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -96,7 +96,7 @@ extern consvar_t cv_recycler; extern consvar_t cv_itemfinder; -extern consvar_t cv_inttime, cv_coopstarposts, cv_cooplives, cv_advancemap, cv_playersforexit; +extern consvar_t cv_inttime, cv_coopstarposts, cv_cooplives, cv_advancemap, cv_playersforexit, cv_exitmove; extern consvar_t cv_overtime; extern consvar_t cv_startinglives; diff --git a/src/d_player.h b/src/d_player.h index f2fdda050..3ec46c352 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -150,6 +150,7 @@ typedef enum /*** misc ***/ PF_FORCESTRAFE = 1<<28, // Turning inputs are translated into strafing inputs PF_CANCARRY = 1<<29, // Can carry another player? + PF_FINISHED = 1<<30, // The player finished the level. NOT the same as exiting // up to 1<<31 is free } pflags_t; diff --git a/src/dehacked.c b/src/dehacked.c index 34ee1f170..39e3dafe8 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8298,6 +8298,7 @@ static const char *const PLAYERFLAG_LIST[] = { /*** misc ***/ "FORCESTRAFE", // Translate turn inputs into strafe inputs "CANCARRY", // Can carry? + "FINISHED", NULL // stop loop here. }; diff --git a/src/g_game.c b/src/g_game.c index e2f43e4f2..9efebcd68 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2831,6 +2831,31 @@ void G_AddPlayer(INT32 playernum) P_DoPlayerExit(p); } +boolean G_EnoughPlayersFinished(void) +{ + UINT8 numneeded = (G_IsSpecialStage(gamemap) ? 4 : cv_playersforexit.value); + INT32 total = 0; + INT32 exiting = 0; + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || players[i].bot) + continue; + if (players[i].lives <= 0) + continue; + + total++; + if (players[i].pflags & PF_FINISHED) + exiting++; + } + + if (exiting) + return exiting * 4 / total >= numneeded; + else + return false; +} + void G_ExitLevel(void) { if (gamestate == GS_LEVEL) diff --git a/src/g_game.h b/src/g_game.h index 198cbc396..f96d83c33 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -186,6 +186,7 @@ boolean G_GametypeHasSpectators(void); boolean G_RingSlingerGametype(void); boolean G_PlatformGametype(void); boolean G_TagGametype(void); +boolean G_EnoughPlayersFinished(void); void G_ExitLevel(void); void G_NextLevel(void); void G_Continue(void); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 8f173e32e..45021b5ad 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -1160,6 +1160,17 @@ static int lib_pElementalFire(lua_State *L) return 0; } +static int lib_pDoPlayerFinish(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + NOHUD + INLEVEL + if (!player) + return LUA_ErrInvalid(L, "player_t"); + P_DoPlayerFinish(player); + return 0; +} + static int lib_pDoPlayerExit(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); @@ -2632,6 +2643,13 @@ static int lib_gSetCustomExitVars(lua_State *L) return 0; } +static int lib_gEnoughPlayersFinished(lua_State *L) +{ + INLEVEL + lua_pushboolean(L, G_EnoughPlayersFinished()); + return 1; +} + static int lib_gExitLevel(lua_State *L) { int n = lua_gettop(L); // Num arguments @@ -2827,6 +2845,7 @@ static luaL_Reg lib[] = { {"P_DoBubbleBounce",lib_pDoBubbleBounce}, {"P_BlackOw",lib_pBlackOw}, {"P_ElementalFire",lib_pElementalFire}, + {"P_DoPlayerFinish",lib_pDoPlayerFinish}, {"P_DoPlayerExit",lib_pDoPlayerExit}, {"P_InstaThrust",lib_pInstaThrust}, {"P_ReturnThrustX",lib_pReturnThrustX}, @@ -2938,6 +2957,7 @@ static luaL_Reg lib[] = { {"G_BuildMapName",lib_gBuildMapName}, {"G_DoReborn",lib_gDoReborn}, {"G_SetCustomExitVars",lib_gSetCustomExitVars}, + {"G_EnoughPlayersFinished",lib_gEnoughPlayersFinished}, {"G_ExitLevel",lib_gExitLevel}, {"G_IsSpecialStage",lib_gIsSpecialStage}, {"G_GametypeUsesLives",lib_gGametypeUsesLives}, diff --git a/src/p_local.h b/src/p_local.h index 02f497850..fd41d2bfa 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -172,6 +172,7 @@ void P_ElementalFire(player_t *player, boolean cropcircle); void P_DoPityCheck(player_t *player); void P_PlayerThink(player_t *player); void P_PlayerAfterThink(player_t *player); +void P_DoPlayerFinish(player_t *player); void P_DoPlayerExit(player_t *player); void P_NightserizePlayer(player_t *player, INT32 ptime); diff --git a/src/p_spec.c b/src/p_spec.c index 7b23ecbe7..5e22696b3 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4645,7 +4645,7 @@ DoneSection2: { INT32 lineindex; - P_DoPlayerExit(player); + P_DoPlayerFinish(player); P_SetupSignExit(player); // important: use sector->tag on next line instead of player->mo->subsector->tag diff --git a/src/p_user.c b/src/p_user.c index 561183cd5..12d91204e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2124,6 +2124,30 @@ void P_SpawnSpinMobj(player_t *player, mobjtype_t type) P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do } +/** Called when \p player finishes the level. + * + * Only use for cases where the player should be able to move + * while waiting for others to finish. Otherwise, use P_DoPlayerExit(). + * + * In single player or if ::cv_exitmove is disabled, this will also cause + * P_PlayerThink() to call P_DoPlayerExit(), so you do not need to + * make a special cases for those. + * + * \param player The player who finished the level. + * \sa P_DoPlayerExit + * + */ +void P_DoPlayerFinish(player_t *player) +{ + if (player->pflags & PF_FINISHED) + return; + + player->pflags |= PF_FINISHED; + + if (netgame) + CONS_Printf(M_GetText("%s has completed the level.\n"), player_names[player-players]); +} + // // P_DoPlayerExit // @@ -2161,9 +2185,6 @@ void P_DoPlayerExit(player_t *player) player->powers[pw_underwater] = 0; player->powers[pw_spacetime] = 0; P_RestoreMusic(player); - - if (playeringame[player-players] && netgame && !circuitmap) - CONS_Printf(M_GetText("%s has completed the level.\n"), player_names[player-players]); } #define SPACESPECIAL 12 @@ -11285,6 +11306,14 @@ void P_PlayerThink(player_t *player) } } + if (player->pflags & PF_FINISHED) + { + if (cv_exitmove.value && !G_EnoughPlayersFinished()) + player->exiting = 0; + else + P_DoPlayerExit(player); + } + // check water content, set stuff in mobj P_MobjCheckWater(player->mo); From 6c98a150be8cf1dc16119d1f380f0f4b0342f36d Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Sun, 17 Nov 2019 23:44:48 +0100 Subject: [PATCH 2/5] Show the exit icon for finished players --- src/hu_stuff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 428656bf2..9b7c281c8 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2443,7 +2443,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I V_DrawSmallScaledPatch(x-32, y-4, 0, tagico); } - if (players[tab[i].num].exiting) + if (players[tab[i].num].exiting || (players[tab[i].num].pflags & PF_FINISHED)) V_DrawSmallScaledPatch(x - SHORT(exiticon->width)/2 - 1, y-3, 0, exiticon); if (gametype == GT_RACE) @@ -2747,7 +2747,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT) V_DrawSmallScaledPatch(x-28, y-4, 0, tagico); - if (players[tab[i].num].exiting) + if (players[tab[i].num].exiting || (players[tab[i].num].pflags & PF_FINISHED)) V_DrawSmallScaledPatch(x - SHORT(exiticon->width)/2 - 1, y-3, 0, exiticon); // Draw emeralds From 688bf0158d11dcd427c247cac99b6dbb430e22df Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Sun, 17 Nov 2019 23:46:34 +0100 Subject: [PATCH 3/5] Disable Tails carrying for players who completed the level To avoid trolls... --- src/p_map.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/p_map.c b/src/p_map.c index bb56a50b1..82403b947 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -592,6 +592,9 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails) if (!(tails->pflags & PF_CANCARRY)) return; + if (sonic->pflags & PF_FINISHED) + return; + if (tails->bot == 1) return; From 24744d07e401b8783810e7aa83799f4b0371964a Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Sun, 17 Nov 2019 23:52:51 +0100 Subject: [PATCH 4/5] Disable exitmove by default --- src/d_netcmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 124bbb9b4..eac90f658 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -360,7 +360,7 @@ consvar_t cv_advancemap = {"advancemap", "Next", CV_NETVAR, advancemap_cons_t, N static CV_PossibleValue_t playersforexit_cons_t[] = {{0, "One"}, {1, "1/4"}, {2, "Half"}, {3, "3/4"}, {4, "All"}, {0, NULL}}; consvar_t cv_playersforexit = {"playersforexit", "All", CV_NETVAR, playersforexit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_exitmove = {"exitmove", "On", CV_NETVAR|CV_CALL, CV_OnOff, ExitMove_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_exitmove = {"exitmove", "Off", CV_NETVAR|CV_CALL, CV_OnOff, ExitMove_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_runscripts = {"runscripts", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; From b4987636fedb68476ca8eb9a2fc36086b2d35411 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Mon, 18 Nov 2019 03:27:39 +0100 Subject: [PATCH 5/5] Add missing checks for PF_FINISHED in HUD code --- src/st_stuff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index 3fbee27e4..0a79da12c 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -2106,7 +2106,7 @@ static void ST_drawTextHUD(void) textHUDdraw(M_GetText("\x82""FIRE:""\x80 Enter game")) } - if (gametype == GT_COOP && (!stplyr->spectator || (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))) && stplyr->exiting) + if (gametype == GT_COOP && (!stplyr->spectator || (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))) && (stplyr->exiting || (stplyr->pflags & PF_FINISHED))) { UINT8 numneeded = (G_IsSpecialStage(gamemap) ? 4 : cv_playersforexit.value); if (numneeded) @@ -2121,7 +2121,7 @@ static void ST_drawTextHUD(void) continue; total++; - if (players[i].exiting) + if (players[i].exiting || (players[i].pflags & PF_FINISHED)) exiting++; }