diff --git a/src/d_clisrv.c b/src/d_clisrv.c index d0b6f13e..925bf2e4 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -644,6 +644,10 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->onconveyor = LONG(players[i].onconveyor); rsp->jointime = (tic_t)LONG(players[i].jointime); + rsp->spectatorreentry = (tic_t)LONG(players[i].spectatorreentry); + + rsp->grieftime = (tic_t)LONG(players[i].grieftime); + rsp->griefstrikes = players[i].griefstrikes; rsp->splitscreenindex = players[i].splitscreenindex; @@ -767,6 +771,10 @@ static void resynch_read_player(resynch_pak *rsp) players[i].onconveyor = LONG(rsp->onconveyor); players[i].jointime = (tic_t)LONG(rsp->jointime); + players[i].spectatorreentry = (tic_t)LONG(rsp->spectatorreentry); + + players[i].grieftime = (tic_t)LONG(rsp->grieftime); + players[i].griefstrikes = rsp->griefstrikes; players[i].splitscreenindex = rsp->splitscreenindex; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index b7fc56ec..7a8272b2 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -276,6 +276,10 @@ typedef struct INT32 onconveyor; tic_t jointime; + tic_t spectatorreentry; + + tic_t grieftime; + UINT8 griefstrikes; UINT8 splitscreenindex; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 84c1045a..7fe1aa8d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -247,6 +247,12 @@ consvar_t cv_allowteamchange = {"allowteamchange", "Yes", CV_NETVAR, CV_YesNo, N 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}; +static CV_PossibleValue_t spectatorreentry_cons_t[] = {{0, "MIN"}, {10*60, "MAX"}, {0, NULL}}; +consvar_t cv_spectatorreentry = {"spectatorreentry", "60", CV_NETVAR, spectatorreentry_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + +static CV_PossibleValue_t antigrief_cons_t[] = {{20, "MIN"}, {60, "MAX"}, {0, "Off"}, {0, NULL}}; +consvar_t cv_antigrief = {"antigrief", "30", CV_NETVAR, antigrief_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}}; @@ -679,6 +685,8 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_restrictskinchange); CV_RegisterVar(&cv_allowteamchange); CV_RegisterVar(&cv_ingamecap); + CV_RegisterVar(&cv_spectatorreentry); + CV_RegisterVar(&cv_antigrief); CV_RegisterVar(&cv_respawntime); CV_RegisterVar(&cv_killingdead); @@ -3707,12 +3715,15 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) // Clear player score and rings if a spectator. if (players[playernum].spectator) { + players[playernum].spectatorreentry = (cv_spectatorreentry.value * TICRATE); + if (G_BattleGametype()) // SRB2kart { players[playernum].marescore = 0; if (K_IsPlayerWanted(&players[playernum])) K_CalculateBattleWanted(); } + players[playernum].health = 1; if (players[playernum].mo) players[playernum].mo->health = 1; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 1e158808..1348af03 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -94,6 +94,7 @@ extern consvar_t cv_killingdead; extern consvar_t cv_pause; extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_ingamecap, cv_respawntime; +extern consvar_t cv_spectatorreentry, cv_antigrief; /*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 114674ff..0f9358ed 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -520,6 +520,10 @@ typedef struct player_s UINT8 bot; tic_t jointime; // Timer when player joins game to change skin/color + tic_t spectatorreentry; + + tic_t grieftime; + UINT8 griefstrikes; UINT8 splitscreenindex; #ifdef HWRENDER diff --git a/src/g_game.c b/src/g_game.c index c51f63cd..91bd11fa 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2603,6 +2603,9 @@ void G_PlayerReborn(INT32 player) INT32 wanted; INT32 respawnflip; boolean songcredit = false; + tic_t spectatorreentry; + tic_t grieftime; + UINT8 griefstrikes; score = players[player].score; marescore = players[player].marescore; @@ -2644,7 +2647,7 @@ void G_PlayerReborn(INT32 player) pity = players[player].pity; // SRB2kart - if (leveltime <= starttime) + if (leveltime <= starttime || spectator == true) { itemroulette = 0; roulettetype = 0; @@ -2655,6 +2658,14 @@ void G_PlayerReborn(INT32 player) comebackpoints = 0; wanted = 0; starpostwp = 0; + + starposttime = 0; + starpostx = 0; + starposty = 0; + starpostz = 0; + starpostnum = 0; + respawnflip = 0; + starpostangle = 0; } else { @@ -2685,6 +2696,11 @@ void G_PlayerReborn(INT32 player) wanted = players[player].kartstuff[k_wanted]; } + spectatorreentry = players[player].spectatorreentry; + + grieftime = players[player].grieftime; + griefstrikes = players[player].griefstrikes; + p = &players[player]; memset(p, 0, sizeof (*p)); @@ -2738,6 +2754,10 @@ void G_PlayerReborn(INT32 player) p->kartstuff[k_eggmanblame] = -1; p->kartstuff[k_starpostflip] = respawnflip; + p->spectatorreentry = spectatorreentry; + p->grieftime = grieftime; + p->griefstrikes = griefstrikes; + // Don't do anything immediately p->pflags |= PF_USEDOWN; p->pflags |= PF_ATTACKDOWN; diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 1d0f8b4c..f209e222 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -759,7 +759,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) char *tempchar = NULL; // player is a spectator? - if (players[playernum].spectator) + if (players[playernum].spectator) { cstart = "\x86"; // grey name textcolor = "\x86"; diff --git a/src/k_kart.c b/src/k_kart.c index 538d3f33..1c99a43e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6267,16 +6267,23 @@ void K_CheckSpectateStatus(void) { UINT8 respawnlist[MAXPLAYERS]; UINT8 i, j, numingame = 0, numjoiners = 0; + UINT8 previngame = 0; // 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; + + if (gamestate != GS_LEVEL) + players[i].spectatorreentry = 0; + else if (players[i].spectatorreentry > 0) + players[i].spectatorreentry--; } // No one's allowed to join @@ -6292,22 +6299,38 @@ 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 + + // DON'T allow if you've hit the in-game player cap + if (cv_ingamecap.value && numingame >= cv_ingamecap.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 (G_RaceGametype() && players[i].laps) // DON'T allow if the race is at 2 laps + + // DON'T allow if the race is on the second lap + if (G_RaceGametype() && players[i].laps) return; + continue; } else if (!(players[i].pflags & PF_WANTSTOJOIN)) + { + // This spectator does not want to join. continue; + } respawnlist[numjoiners++] = i; } @@ -6317,7 +6340,9 @@ void K_CheckSpectateStatus(void) return; // Organize by spectate wait timer +#if 0 if (cv_ingamecap.value) +#endif { UINT8 oldrespawnlist[MAXPLAYERS]; memcpy(oldrespawnlist, respawnlist, numjoiners); @@ -6341,16 +6366,25 @@ void K_CheckSpectateStatus(void) } } - // Finally, we can de-spectate everyone! + // Finally, we can de-spectate everyone in the list! + previngame = numingame; + for (i = 0; i < numjoiners; i++) { - if (cv_ingamecap.value && numingame+i >= cv_ingamecap.value) // Hit the in-game player cap while adding people? + // Hit the in-game player cap while adding people? Get out of here + if (cv_ingamecap.value > 0 && numingame >= cv_ingamecap.value) break; + + // This person has their reentry cooldown active. + if (players[i].spectatorreentry > 0 && numingame > 0) + continue; + P_SpectatorJoinGame(&players[respawnlist[i]]); + numingame++; } // 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 + if (!mapreset && gamestate == GS_LEVEL && leveltime >= starttime && (previngame < 2 && numingame >= 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 5f034c29..e00af595 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -383,6 +383,12 @@ static int player_get(lua_State *L) lua_pushinteger(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,"grieftime")) + lua_pushinteger(L, plr->grieftime); + else if (fastcmp(field,"griefstrikes")) + lua_pushinteger(L, plr->griefstrikes); else if (fastcmp(field,"splitscreenindex")) lua_pushinteger(L, plr->splitscreenindex); #ifdef HWRENDER @@ -647,6 +653,12 @@ static int player_set(lua_State *L) return NOSET; else if (fastcmp(field,"jointime")) plr->jointime = (tic_t)luaL_checkinteger(L, 3); + else if (fastcmp(field,"spectatorreentry")) + plr->spectatorreentry = (tic_t)luaL_checkinteger(L, 3); + else if (fastcmp(field,"grieftime")) + plr->grieftime = (tic_t)luaL_checkinteger(L, 3); + else if (fastcmp(field,"griefstrikes")) + plr->griefstrikes = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"splitscreenindex")) return NOSET; #ifdef HWRENDER diff --git a/src/p_inter.c b/src/p_inter.c index c5981230..24e0a314 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1471,7 +1471,15 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) { // blatant reuse of a variable that's normally unused in circuit if (!player->tossdelay) + { S_StartSound(toucher, sfx_s26d); + + if (netgame && cv_antigrief.value) + { + player->grieftime += TICRATE; + } + } + player->tossdelay = 3; return; } @@ -1495,6 +1503,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) player->starpostangle = special->angle; player->starpostnum = special->health; player->kartstuff[k_starpostflip] = special->spawnpoint->options & MTF_OBJECTFLIP; // store flipping + player->grieftime = 0; //S_StartSound(toucher, special->info->painsound); return; diff --git a/src/p_mobj.c b/src/p_mobj.c index aa5df9d4..91b15a7a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10663,10 +10663,7 @@ void P_SpawnPlayer(INT32 playernum) else if (netgame && p->jointime <= 1 && pcount) { p->spectator = true; - // Oni doesn't want this - /*if (pcount == 1 || leveltime < starttime) - p->pflags |= PF_WANTSTOJOIN; - p->jointime = 2;*/ + p->spectatorreentry = (cv_spectatorreentry.value * TICRATE); } else if (multiplayer && !netgame) { @@ -10680,6 +10677,8 @@ void P_SpawnPlayer(INT32 playernum) // Spawn as a spectator, // yes even in splitscreen mode p->spectator = true; + p->spectatorreentry = (cv_spectatorreentry.value * TICRATE); + if (playernum&1) p->skincolor = skincolor_redteam; else p->skincolor = skincolor_blueteam; @@ -10699,7 +10698,10 @@ void P_SpawnPlayer(INT32 playernum) { // Fix stupid non spectator spectators. if (!p->spectator && !p->ctfteam) + { p->spectator = true; + p->spectatorreentry = (cv_spectatorreentry.value * TICRATE); + } // Fix team colors. // This code isn't being done right somewhere else. Oh well. @@ -10741,6 +10743,8 @@ void P_SpawnPlayer(INT32 playernum) // Spawn with a pity shield if necessary. //P_DoPityCheck(p); + p->grieftime = 0; + if (G_BattleGametype()) // SRB2kart { mobj_t *overheadarrow = P_SpawnMobj(mobj->x, mobj->y, mobj->z + P_GetPlayerHeight(p)+16*FRACUNIT, MT_PLAYERARROW); diff --git a/src/p_saveg.c b/src/p_saveg.c index f587c6e6..3a29269b 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -246,6 +246,10 @@ static void P_NetArchivePlayers(void) WRITEINT32(save_p, players[i].onconveyor); WRITEUINT32(save_p, players[i].jointime); + WRITEUINT32(save_p, players[i].spectatorreentry); + + WRITEUINT32(save_p, players[i].grieftime); + WRITEUINT8(save_p, players[i].griefstrikes); WRITEUINT8(save_p, players[i].splitscreenindex); @@ -411,6 +415,10 @@ static void P_NetUnArchivePlayers(void) players[i].onconveyor = READINT32(save_p); players[i].jointime = READUINT32(save_p); + players[i].spectatorreentry = READUINT32(save_p); + + players[i].grieftime = READUINT32(save_p); + players[i].griefstrikes = READUINT8(save_p); players[i].splitscreenindex = READUINT8(save_p); diff --git a/src/p_spec.c b/src/p_spec.c index 671c49cf..67d05f2d 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4316,12 +4316,21 @@ DoneSection2: nospectategrief = nump; thwompsactive = true; // Lap 2 effects + player->grieftime = 0; } else if (player->starpostnum) { // blatant reuse of a variable that's normally unused in circuit if (!player->tossdelay) + { S_StartSound(player->mo, sfx_s26d); + + if (netgame && cv_antigrief.value) + { + player->grieftime += TICRATE; + } + } + player->tossdelay = 3; } diff --git a/src/p_user.c b/src/p_user.c index 8d553633..12e12187 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1698,6 +1698,9 @@ void P_DoPlayerExit(player_t *player) if (P_IsLocalPlayer(player) && (!player->spectator && !demo.playback)) legitimateexit = true; + if (player->griefstrikes > 0) + player->griefstrikes--; // Remove a strike for finishing a race normally + if (G_RaceGametype()) // If in Race Mode, allow { player->exiting = raceexittime+2; @@ -8501,6 +8504,55 @@ void P_PlayerThink(player_t *player) } } + if (netgame && cv_antigrief.value != 0) + { + if (!player->spectator && !player->exiting && !(player->pflags & PF_TIMEOVER)) + { + const tic_t griefval = cv_antigrief.value * TICRATE; + const UINT8 n = player - players; + + if (n != serverplayer && !IsPlayerAdmin(n)) + { + if (player->grieftime > griefval) + { + player->griefstrikes++; + player->grieftime = 0; + + if (server) + { + if (player->griefstrikes > 2) + { + // Send kick + XBOXSTATIC UINT8 buf[2]; + + buf[0] = n; + buf[1] = KICK_MSG_CON_FAIL; + SendNetXCmd(XD_KICK, &buf, 2); + } + else + { + // Send spectate + changeteam_union NetPacket; + UINT16 usvalue; + + NetPacket.value.l = NetPacket.value.b = 0; + NetPacket.packet.newteam = 0; + NetPacket.packet.playernum = n; + NetPacket.packet.verification = true; + + usvalue = SHORT(NetPacket.value.l|NetPacket.value.b); + SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue)); + } + } + } + else if (player->powers[pw_flashing] == 0) + { + player->grieftime++; + } + } + } + } + if ((netgame || multiplayer) && player->spectator && cmd->buttons & BT_ATTACK && !player->powers[pw_flashing]) { player->pflags ^= PF_WANTSTOJOIN;