diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 775f2225..5773d590 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -236,6 +236,9 @@ static consvar_t cv_dummyconsvar = {"dummyconsvar", "Off", CV_CALL|CV_NOSHOWHELP consvar_t cv_restrictskinchange = {"restrictskinchange", "No", CV_NETVAR|CV_CHEAT, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_allowteamchange = {"allowteamchange", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t ingamecap_cons_t[] = {{0, "MIN"}, {MAXPLAYERS-1, "MAX"}, {0, NULL}}; +consvar_t cv_ingamecap = {"ingamecap", "0", CV_NETVAR, ingamecap_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + consvar_t cv_startinglives = {"startinglives", "3", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, startingliveslimit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t respawntime_cons_t[] = {{0, "MIN"}, {30, "MAX"}, {0, NULL}}; @@ -642,6 +645,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_allowexitlevel); CV_RegisterVar(&cv_restrictskinchange); CV_RegisterVar(&cv_allowteamchange); + CV_RegisterVar(&cv_ingamecap); CV_RegisterVar(&cv_respawntime); CV_RegisterVar(&cv_killingdead); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 2269996f..c590eee6 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -93,7 +93,7 @@ extern consvar_t cv_mute; extern consvar_t cv_killingdead; extern consvar_t cv_pause; -extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_respawntime; +extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_ingamecap, cv_respawntime; /*extern consvar_t cv_teleporters, cv_superring, cv_supersneakers, cv_invincibility; extern consvar_t cv_jumpshield, cv_watershield, cv_ringshield, cv_forceshield, cv_bombshield; diff --git a/src/d_player.h b/src/d_player.h index 27fdef8d..58d2df4b 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -352,6 +352,7 @@ typedef enum k_itemblink, // Item flashing after roulette, prevents Hyudoro stealing AND serves as a mashing indicator k_itemblinkmode, // Type of flashing: 0 = white (normal), 1 = red (mashing), 2 = rainbow (enhanced items) k_getsparks, // Disable drift sparks at low speed, JUST enough to give acceleration the actual headstart above speed + k_spectatewait, // How long have you been waiting as a spectator NUMKARTSTUFF } kartstufftype_t; diff --git a/src/dehacked.c b/src/dehacked.c index b9e29bc4..67080bd7 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8299,7 +8299,8 @@ static const char *const KARTSTUFF_LIST[] = { "ITEMBLINK", "ITEMBLINKMODE", - "GETSPARKS" + "GETSPARKS", + "SPECTATEWAIT" }; static const char *const HUDITEMS_LIST[] = { diff --git a/src/k_kart.c b/src/k_kart.c index dde4849e..8cb0941e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5744,9 +5744,22 @@ void K_CheckBumpers(void) void K_CheckSpectateStatus(void) { UINT8 respawnlist[MAXPLAYERS]; - UINT8 i, numingame = 0, numjoiners = 0; + UINT8 i, j, numingame = 0, numjoiners = 0; - if (!cv_allowteamchange.value) return; + // Maintain spectate wait timer + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + if (players[i].spectator && (players[i].pflags & PF_WANTSTOJOIN)) + players[i].kartstuff[k_spectatewait]++; + else + players[i].kartstuff[k_spectatewait] = 0; + } + + // No one's allowed to join + if (!cv_allowteamchange.value) + return; // Get the number of players in game, and the players to be de-spectated. for (i = 0; i < MAXPLAYERS; i++) @@ -5757,16 +5770,18 @@ void K_CheckSpectateStatus(void) if (!players[i].spectator) { numingame++; + if (cv_ingamecap.value && numingame >= cv_ingamecap.value) // DON'T allow if you've hit the in-game player cap + return; if (gamestate != GS_LEVEL) // Allow if you're not in a level - continue; + continue; if (players[i].exiting) // DON'T allow if anyone's exiting return; if (numingame < 2 || leveltime < starttime || mapreset) // Allow if the match hasn't started yet - continue; + continue; if (leveltime > (starttime + 20*TICRATE)) // DON'T allow if the match is 20 seconds in - return; - if (G_RaceGametype() && players[i].laps) // DON'T allow if the race is at 2 laps - return; + return; + if (G_RaceGametype() && players[i].laps) // DON'T allow if the race is at 2 laps + return; continue; } else if (!(players[i].pflags & PF_WANTSTOJOIN)) @@ -5779,16 +5794,43 @@ void K_CheckSpectateStatus(void) if (!numjoiners) return; - // Reset the match if you're in an empty server - if (!mapreset && gamestate == GS_LEVEL && leveltime >= starttime && (numingame < 2 && numingame+numjoiners >= 2)) + // Organize by spectate wait timer { - S_ChangeMusicInternal("chalng", false); // COME ON - mapreset = 3*TICRATE; // Even though only the server uses this for game logic, set for everyone for HUD in the future + UINT8 oldrespawnlist[MAXPLAYERS]; + + for (i = 0; i < numjoiners; i++) // copy old table before modifying + oldrespawnlist[i] = respawnlist[i]; + + for (i = 0; i < numjoiners; i++) + { + UINT8 pos = 0; + + for (j = 0; j < numjoiners; j++) + { + if (j == i) + continue; + if (players[oldrespawnlist[j]].kartstuff[k_spectatewait] > players[oldrespawnlist[i]].kartstuff[k_spectatewait]) + pos++; + } + + respawnlist[pos] = oldrespawnlist[i]; + } } // Finally, we can de-spectate everyone! for (i = 0; i < numjoiners; i++) + { + if (cv_ingamecap.value && numingame+i >= cv_ingamecap.value) // Hit the in-game player cap while adding people? + break; P_SpectatorJoinGame(&players[respawnlist[i]]); + } + + // Reset the match if you're in an empty server + if (!mapreset && gamestate == GS_LEVEL && leveltime >= starttime && (numingame < 2 && numingame+i >= 2)) // use previous i value + { + 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/p_user.c b/src/p_user.c index d7423d80..0978a0da 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8763,6 +8763,7 @@ boolean P_SpectatorJoinGame(player_t *player) } player->spectator = false; player->pflags &= ~PF_WANTSTOJOIN; + player->kartstuff[k_spectatewait] = 0; player->ctfteam = changeto; player->playerstate = PST_REBORN; @@ -8787,6 +8788,7 @@ boolean P_SpectatorJoinGame(player_t *player) } player->spectator = false; player->pflags &= ~PF_WANTSTOJOIN; + player->kartstuff[k_spectatewait] = 0; player->playerstate = PST_REBORN; //Reset away view diff --git a/src/st_stuff.c b/src/st_stuff.c index 8ebd2132..36a658ae 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1952,31 +1952,38 @@ static void ST_overlayDrawer(void) #endif ) { + const char *itemtxt = M_GetText("Item - Join Game"); + + if (stplyr->powers[pw_flashing]) + itemtxt = M_GetText("Item - . . ."); + else if (stplyr->pflags & PF_WANTSTOJOIN) + itemtxt = M_GetText("Item - Cancel Join"); + else if (G_GametypeHasTeams()) + itemtxt = M_GetText("Item - Join Team"); + + if (cv_ingamecap.value) + { + UINT8 numingame = 0; + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && !players[i].spectator) + numingame++; + + itemtxt = va("%s (%s: %d)", itemtxt, M_GetText("Slots left"), max(0, cv_ingamecap.value - numingame)); + } + // SRB2kart: changed positions & text if (splitscreen) { INT32 splitflags = K_calcSplitFlags(0); V_DrawThinString(2, (BASEVIDHEIGHT/2)-20, V_YELLOWMAP|V_HUDTRANSHALF|splitflags, M_GetText("- SPECTATING -")); - if (stplyr->powers[pw_flashing]) - V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, M_GetText("Item - . . .")); - else if (stplyr->pflags & PF_WANTSTOJOIN) - V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, M_GetText("Item - Cancel Join")); - /*else if (G_GametypeHasTeams()) - V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, M_GetText("Item - Join Team"));*/ - else - V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, M_GetText("Item - Join Game")); + V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, itemtxt); } else { V_DrawString(2, BASEVIDHEIGHT-40, V_HUDTRANSHALF|V_YELLOWMAP, M_GetText("- SPECTATING -")); - if (stplyr->powers[pw_flashing]) - V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF, M_GetText("Item - . . .")); - else if (stplyr->pflags & PF_WANTSTOJOIN) - V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF, M_GetText("Item - Cancel Join")); - /*else if (G_GametypeHasTeams()) - V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF, M_GetText("Item - Join Team"));*/ - else - V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF, M_GetText("Item - Join Game")); + V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF, itemtxt); V_DrawString(2, BASEVIDHEIGHT-20, V_HUDTRANSHALF, M_GetText("Accelerate - Float")); V_DrawString(2, BASEVIDHEIGHT-10, V_HUDTRANSHALF, M_GetText("Brake - Sink")); }