diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 7a3738157..4abc18251 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -262,6 +262,10 @@ consvar_t cv_allowteamchange = CVAR_INIT ("allowteamchange", "Yes", CV_NETVAR, C static CV_PossibleValue_t maxplayers_cons_t[] = {{1, "MIN"}, {MAXPLAYERS, "MAX"}, {0, NULL}}; consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_NETVAR, maxplayers_cons_t, NULL); +static CV_PossibleValue_t spectatorreentry_cons_t[] = {{0, "MIN"}, {10*60, "MAX"}, {0, NULL}}; +consvar_t cv_spectatorreentry = CVAR_INIT ("spectatorreentry", "30", CV_NETVAR, spectatorreentry_cons_t, NULL); +consvar_t cv_duelspectatorreentry = CVAR_INIT ("duelspectatorreentry", "180", CV_NETVAR, spectatorreentry_cons_t, NULL); + consvar_t cv_startinglives = CVAR_INIT ("startinglives", "3", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, startingliveslimit_cons_t, NULL); static CV_PossibleValue_t respawntime_cons_t[] = {{1, "MIN"}, {30, "MAX"}, {0, "Off"}, {0, NULL}}; @@ -788,6 +792,8 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_restrictskinchange); CV_RegisterVar(&cv_allowteamchange); CV_RegisterVar(&cv_maxplayers); + CV_RegisterVar(&cv_spectatorreentry); + CV_RegisterVar(&cv_duelspectatorreentry); CV_RegisterVar(&cv_respawntime); // d_clisrv @@ -1594,6 +1600,35 @@ static void FinalisePlaystateChange(INT32 playernum) // Clear player score and rings if a spectator. if (players[playernum].spectator) { + // To attempt to discourage rage-spectators, we delay any rejoining. + // If you're engaging in a DUEL and quit early, in addition to the + // indignity of losing your PWR, you get a special extra-long delay. + if (netgame) + { + UINT8 pcount = 0; + + if (cv_duelspectatorreentry.value > cv_spectatorreentry.value) + { + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + if (++pcount < 2) + continue; + break; + } + } + + players[playernum].spectatorReentry = + (pcount == 1) + ? (cv_duelspectatorreentry.value * TICRATE) + : (cv_spectatorreentry.value * TICRATE); + + //CONS_Printf("player %u got re-entry of %u\n", playernum, players[playernum].spectatorReentry); + } + if (gametyperules & GTR_POINTLIMIT) // SRB2kart { players[playernum].roundscore = 0; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 5bfccb5f6..7635296df 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -75,6 +75,7 @@ extern consvar_t cv_mute; extern consvar_t cv_pause; extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_maxplayers, cv_respawntime; +extern consvar_t cv_spectatorreentry, cv_duelspectatorreentry; // SRB2kart items extern consvar_t cv_items[NUMKARTRESULTS-1]; diff --git a/src/d_player.h b/src/d_player.h index d0313aaed..b339851f5 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -691,6 +691,8 @@ struct player_t tic_t jointime; // Timer when player joins game to change skin/color + tic_t spectatorReentry; + UINT8 typing_timer; // Counts down while keystrokes are not emitted UINT8 typing_duration; // How long since resumed timer diff --git a/src/g_game.c b/src/g_game.c index 03346abc3..6a917aee2 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2476,6 +2476,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) tic_t jointime; + tic_t spectatorReentry; + UINT8 splitscreenindex; boolean spectator; boolean bot; @@ -2654,6 +2656,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) saveroundconditions = true; } + spectatorReentry = (betweenmaps ? 0 : players[player].spectatorReentry); + if (!betweenmaps) { follower = players[player].follower; @@ -2734,6 +2738,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->botvars.rubberband = FRACUNIT; p->botvars.controller = UINT16_MAX; + p->spectatorReentry = spectatorReentry; + memcpy(&p->itemRoulette, &itemRoulette, sizeof (p->itemRoulette)); memcpy(&p->respawn, &respawn, sizeof (p->respawn)); diff --git a/src/k_kart.c b/src/k_kart.c index fa8828057..545143438 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -11385,10 +11385,16 @@ void K_CheckSpectateStatus(void) { if (!playeringame[i]) continue; + if (players[i].spectator && (players[i].pflags & PF_WANTSTOJOIN)) players[i].spectatewait++; else players[i].spectatewait = 0; + + if (gamestate != GS_LEVEL) + players[i].spectatorReentry = 0; + else if (players[i].spectatorReentry > 0) + players[i].spectatorReentry--; } // No one's allowed to join @@ -11404,22 +11410,45 @@ void K_CheckSpectateStatus(void) if (!players[i].spectator) { numingame++; - if (cv_maxplayers.value && numingame >= cv_maxplayers.value) // DON'T allow if you've hit the in-game player cap + + // DON'T allow if you've hit the in-game player cap + if (cv_maxplayers.value && numingame >= cv_maxplayers.value) return; - if (gamestate != GS_LEVEL) // Allow if you're not in a level + + // Allow if you're not in a level + if (gamestate != GS_LEVEL) continue; - if (players[i].exiting) // DON'T allow if anyone's exiting + + // DON'T allow if anyone's exiting + if (players[i].exiting) return; - if (numingame < 2 || leveltime < starttime || mapreset) // Allow if the match hasn't started yet + + // Allow if the match hasn't started yet + if (numingame < 2 || leveltime < starttime || mapreset) continue; - if (leveltime > (starttime + 20*TICRATE)) // DON'T allow if the match is 20 seconds in + + // DON'T allow if the match is 20 seconds in + if (leveltime > (starttime + 20*TICRATE)) return; - if ((gametyperules & GTR_CIRCUIT) && players[i].laps >= 2) // DON'T allow if the race is at 2 laps + + // DON'T allow if the race is at 2 laps + if ((gametyperules & GTR_CIRCUIT) && players[i].laps >= 2) return; + continue; } - else if (players[i].bot || !(players[i].pflags & PF_WANTSTOJOIN)) + + if (players[i].bot) + { + // Spectating bots are controlled by other mechanisms. continue; + } + + if (!(players[i].pflags & PF_WANTSTOJOIN)) + { + // This spectator does not want to join. + continue; + } respawnlist[numjoiners++] = i; } @@ -11428,8 +11457,8 @@ void K_CheckSpectateStatus(void) if (!numjoiners) return; - // Organize by spectate wait timer - if (cv_maxplayers.value) + // Organize by spectate wait timer (if there's more than one to sort) + if (cv_maxplayers.value && numjoiners > 1) { UINT8 oldrespawnlist[MAXPLAYERS]; memcpy(oldrespawnlist, respawnlist, numjoiners); @@ -11454,17 +11483,25 @@ void K_CheckSpectateStatus(void) } // Finally, we can de-spectate everyone! - for (i = 0; i < numjoiners; i++) + for (i = 0, j = 0; i < numjoiners; i++) { - if (cv_maxplayers.value && numingame+i >= cv_maxplayers.value) // Hit the in-game player cap while adding people? - break; + // This person has their reentry cooldown active. + if (netgame && players[respawnlist[i]].spectatorReentry > 0 && numingame > 0) + continue; + //CONS_Printf("player %s is joining on tic %d\n", player_names[respawnlist[i]], leveltime); + P_SpectatorJoinGame(&players[respawnlist[i]]); + j++; // j is being used as the number of players added + + // Hit the in-game player cap while adding people? + if (cv_maxplayers.value && numingame+j >= cv_maxplayers.value) + break; } // Reset the match when 2P joins 1P, DUEL mode // Reset the match when 3P joins 1P and 2P, DUEL mode must be disabled - if (!mapreset && gamestate == GS_LEVEL && (numingame < 3 && numingame+i >= 2)) // use previous i value + if (j > 0 && !mapreset && gamestate == GS_LEVEL && (numingame < 3 && numingame+j >= 2)) { S_ChangeMusicInternal("chalng", false); // COME ON mapreset = 3*TICRATE; // Even though only the server uses this for game logic, set for everyone for HUD diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 6e93fd974..6ab5946ea 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -500,6 +500,8 @@ static int player_get(lua_State *L) lua_pushboolean(L, plr->bot); else if (fastcmp(field,"jointime")) lua_pushinteger(L, plr->jointime); + else if (fastcmp(field,"spectatorReentry")) + lua_pushinteger(L, plr->spectatorReentry); else if (fastcmp(field,"splitscreenindex")) lua_pushinteger(L, plr->splitscreenindex); #ifdef HWRENDER @@ -880,6 +882,8 @@ static int player_set(lua_State *L) return NOSET; else if (fastcmp(field,"jointime")) return NOSET; + else if (fastcmp(field,"spectatorReentry")) + plr->spectatorReentry = (UINT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"splitscreenindex")) return NOSET; #ifdef HWRENDER diff --git a/src/p_mobj.c b/src/p_mobj.c index a69078b4a..1ee62a5ff 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11682,12 +11682,7 @@ void P_SpawnPlayer(INT32 playernum) else if (netgame && p->jointime <= 1 && pcount) { p->spectator = true; - -#if 0 - if (pcount == 1 || leveltime < starttime) - p->pflags |= PF_WANTSTOJOIN; - p->jointime = 2; -#endif + p->spectatorReentry = 0; } else if (multiplayer && !netgame) { @@ -11701,6 +11696,7 @@ void P_SpawnPlayer(INT32 playernum) // Spawn as a spectator, // yes even in splitscreen mode p->spectator = true; + if (playernum&1) p->skincolor = skincolor_redteam; else p->skincolor = skincolor_blueteam; @@ -11722,7 +11718,9 @@ void P_SpawnPlayer(INT32 playernum) { // Fix stupid non spectator spectators. if (!p->spectator && !p->ctfteam) + { p->spectator = true; + } // Fix team colors. // This code isn't being done right somewhere else. Oh well. diff --git a/src/p_saveg.c b/src/p_saveg.c index 3a09afd0f..edbb9b768 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -195,6 +195,8 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT32(save->p, players[i].jointime); + WRITEUINT32(save->p, players[i].spectatorReentry); + WRITEUINT8(save->p, players[i].splitscreenindex); if (players[i].awayview.mobj) @@ -598,6 +600,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].jointime = READUINT32(save->p); + players[i].spectatorReentry = READUINT32(save->p); + players[i].splitscreenindex = READUINT8(save->p); flags = READUINT16(save->p);