Merge branch 'exitfix' into 'next'

Only Consider ExitLevel a Cheat When Used to Cheat

See merge request STJr/SRB2!2159
This commit is contained in:
Logan Aerl Arias 2024-01-02 03:05:24 +00:00
commit 4dd2415262
7 changed files with 183 additions and 148 deletions

View file

@ -2151,7 +2151,7 @@ boolean G_Responder(event_t *ev)
if (! netgame)
F_StartGameEvaluation();
else if (server || IsPlayerAdmin(consoleplayer))
D_SendExitLevel(false);
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
return true;
}
}
@ -4000,14 +4000,137 @@ static void G_HandleSaveLevel(void)
}
}
//
// G_GetNextMap
//
INT16 G_GetNextMap(boolean ignoretokens, boolean silent)
{
INT32 i;
INT16 newmapnum;
boolean spec = G_IsSpecialStage(gamemap);
// go to next level
// newmapnum is 0-based, unlike gamemap
if (nextmapoverride != 0)
newmapnum = (INT16)(nextmapoverride-1);
else if (marathonmode && mapheaderinfo[gamemap-1]->marathonnext)
newmapnum = (INT16)(mapheaderinfo[gamemap-1]->marathonnext-1);
else
{
newmapnum = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1);
if (marathonmode && newmapnum == spmarathon_start-1)
newmapnum = 1100-1; // No infinite loop for you
}
INT16 gametype_to_use;
if (nextgametype >= 0 && nextgametype < gametypecount)
gametype_to_use = nextgametype;
else
gametype_to_use = gametype;
// If newmapnum is actually going to get used, make sure it points to
// a map of the proper gametype -- skip levels that don't support
// the current gametype. (Helps avoid playing boss levels in Race,
// for instance).
if (!spec || nextmapoverride)
{
if (newmapnum >= 0 && newmapnum < NUMMAPS)
{
INT16 cm = newmapnum;
UINT32 tolflag = G_TOLFlag(gametype_to_use);
UINT8 visitedmap[(NUMMAPS+7)/8];
memset(visitedmap, 0, sizeof (visitedmap));
while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag))
{
visitedmap[cm/8] |= (1<<(cm&7));
if (!mapheaderinfo[cm])
cm = -1; // guarantee error execution
else if (marathonmode && mapheaderinfo[cm]->marathonnext)
cm = (INT16)(mapheaderinfo[cm]->marathonnext-1);
else
cm = (INT16)(mapheaderinfo[cm]->nextlevel-1);
if (cm >= NUMMAPS || cm < 0) // out of range (either 1100ish or error)
{
cm = newmapnum; //Start the loop again so that the error checking below is executed.
//Make sure the map actually exists before you try to go to it!
if ((W_CheckNumForName(G_BuildMapName(cm + 1)) == LUMPERROR))
{
if (!silent)
CONS_Alert(CONS_ERROR, M_GetText("Next map given (MAP %d) doesn't exist! Reverting to MAP01.\n"), cm+1);
cm = 0;
break;
}
}
if (visitedmap[cm/8] & (1<<(cm&7))) // smells familiar
{
// We got stuck in a loop, came back to the map we started on
// without finding one supporting the current gametype.
// Thus, print a warning, and just use this map anyways.
if (!silent)
CONS_Alert(CONS_WARNING, M_GetText("Can't find a compatible map after map %d; using map %d anyway\n"), prevmap+1, cm+1);
break;
}
}
newmapnum = cm;
}
// wrap around in race
if (newmapnum >= 1100-1 && newmapnum <= 1102-1 && !(gametyperules & GTR_CAMPAIGN))
newmapnum = (INT16)(spstage_start-1);
if (newmapnum < 0 || (newmapnum >= NUMMAPS && newmapnum < 1100-1) || newmapnum > 1103-1)
I_Error("Followed map %d to invalid map %d\n", prevmap + 1, newmapnum + 1);
if (!spec)
lastmap = newmapnum; // Remember last map for when you come out of the special stage.
}
if (!ignoretokens && (gottoken = ((gametyperules & GTR_SPECIALSTAGES) && token)))
{
token--;
// if (!nextmapoverride) // Having a token should pull the player into the special stage before going to the overridden map (Issue #933)
for (i = 0; i < 7; i++)
if (!(emeralds & (1<<i)))
{
newmapnum = ((netgame || multiplayer) ? smpstage_start : sstage_start) + i - 1; // to special stage!
break;
}
if (i == 7)
{
gottoken = false;
token = 0;
}
}
if (spec && (!gottoken || ignoretokens) && !nextmapoverride)
newmapnum = lastmap; // Exiting from a special stage? Go back to the game. Tails 08-11-2001
if (!(gametyperules & GTR_CAMPAIGN))
{
if (cv_advancemap.value == 0) // Stay on same map.
newmapnum = prevmap;
else if (cv_advancemap.value == 2) // Go to random map.
newmapnum = RandMap(G_TOLFlag(gametype_to_use), prevmap);
}
return newmapnum;
}
//
// G_DoCompleted
//
static void G_DoCompleted(void)
{
INT32 i;
boolean spec = G_IsSpecialStage(gamemap);
tokenlist = 0; // Reset the list
if (modeattacking && pausedelay)
@ -4032,120 +4155,12 @@ static void G_DoCompleted(void)
S_StopSounds();
//Get and set prevmap/nextmap
prevmap = (INT16)(gamemap-1);
// go to next level
// nextmap is 0-based, unlike gamemap
if (nextmapoverride != 0)
nextmap = (INT16)(nextmapoverride-1);
else if (marathonmode && mapheaderinfo[gamemap-1]->marathonnext)
nextmap = (INT16)(mapheaderinfo[gamemap-1]->marathonnext-1);
else
{
nextmap = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1);
if (marathonmode && nextmap == spmarathon_start-1)
nextmap = 1100-1; // No infinite loop for you
}
INT16 gametype_to_use;
if (nextgametype >= 0 && nextgametype < gametypecount)
gametype_to_use = nextgametype;
else
gametype_to_use = gametype;
// If nextmap is actually going to get used, make sure it points to
// a map of the proper gametype -- skip levels that don't support
// the current gametype. (Helps avoid playing boss levels in Race,
// for instance).
if (!spec || nextmapoverride)
{
if (nextmap >= 0 && nextmap < NUMMAPS)
{
INT16 cm = nextmap;
UINT32 tolflag = G_TOLFlag(gametype_to_use);
UINT8 visitedmap[(NUMMAPS+7)/8];
memset(visitedmap, 0, sizeof (visitedmap));
while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag))
{
visitedmap[cm/8] |= (1<<(cm&7));
if (!mapheaderinfo[cm])
cm = -1; // guarantee error execution
else if (marathonmode && mapheaderinfo[cm]->marathonnext)
cm = (INT16)(mapheaderinfo[cm]->marathonnext-1);
else
cm = (INT16)(mapheaderinfo[cm]->nextlevel-1);
if (cm >= NUMMAPS || cm < 0) // out of range (either 1100ish or error)
{
cm = nextmap; //Start the loop again so that the error checking below is executed.
//Make sure the map actually exists before you try to go to it!
if ((W_CheckNumForName(G_BuildMapName(cm + 1)) == LUMPERROR))
{
CONS_Alert(CONS_ERROR, M_GetText("Next map given (MAP %d) doesn't exist! Reverting to MAP01.\n"), cm+1);
cm = 0;
break;
}
}
if (visitedmap[cm/8] & (1<<(cm&7))) // smells familiar
{
// We got stuck in a loop, came back to the map we started on
// without finding one supporting the current gametype.
// Thus, print a warning, and just use this map anyways.
CONS_Alert(CONS_WARNING, M_GetText("Can't find a compatible map after map %d; using map %d anyway\n"), prevmap+1, cm+1);
break;
}
}
nextmap = cm;
}
// wrap around in race
if (nextmap >= 1100-1 && nextmap <= 1102-1 && !(gametyperules & GTR_CAMPAIGN))
nextmap = (INT16)(spstage_start-1);
if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1103-1)
I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1);
if (!spec)
lastmap = nextmap; // Remember last map for when you come out of the special stage.
}
if ((gottoken = ((gametyperules & GTR_SPECIALSTAGES) && token)))
{
token--;
// if (!nextmapoverride) // Having a token should pull the player into the special stage before going to the overridden map (Issue #933)
for (i = 0; i < 7; i++)
if (!(emeralds & (1<<i)))
{
nextmap = ((netgame || multiplayer) ? smpstage_start : sstage_start) + i - 1; // to special stage!
break;
}
if (i == 7)
{
gottoken = false;
token = 0;
}
}
if (spec && !gottoken && !nextmapoverride)
nextmap = lastmap; // Exiting from a special stage? Go back to the game. Tails 08-11-2001
nextmap = G_GetNextMap(false, false);
automapactive = false;
if (!(gametyperules & GTR_CAMPAIGN))
{
if (cv_advancemap.value == 0) // Stay on same map.
nextmap = prevmap;
else if (cv_advancemap.value == 2) // Go to random map.
nextmap = RandMap(G_TOLFlag(gametype_to_use), prevmap);
}
// We are committed to this map now.
// We may as well allocate its header if it doesn't exist
// (That is, if it's a real map)

View file

@ -216,6 +216,7 @@ boolean G_CoopGametype(void);
boolean G_TagGametype(void);
boolean G_CompetitionGametype(void);
boolean G_EnoughPlayersFinished(void);
INT16 G_GetNextMap(boolean ignoretokens, boolean silent);
void G_ExitLevel(void);
void G_NextLevel(void);
void G_Continue(void);

View file

@ -4495,37 +4495,62 @@ static void Command_Mapmd5_f(void)
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
}
void D_SendExitLevel(boolean cheat)
{
UINT8 buf[8];
UINT8 *buf_p = buf;
WRITEUINT8(buf_p, cheat);
SendNetXCmd(XD_EXITLEVEL, &buf, buf_p - buf);
}
static void Command_ExitLevel_f(void)
{
if (!(server || (IsPlayerAdmin(consoleplayer))))
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
else if (( gamestate != GS_LEVEL && gamestate != GS_CREDITS ) || demoplayback)
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
else if (usedCheats)
SendNetXCmd(XD_EXITLEVEL, NULL, 0); // Does it matter if it's a cheat if we've used one already?
else if (!(splitscreen || multiplayer || cv_debug))
CONS_Printf(M_GetText("Cheats must be enabled to force a level exit in single player.\n"));
else
D_SendExitLevel(true);
{
if (G_IsSpecialStage(gamemap) // If you wanna give up that emerald then go right ahead!
|| gamestate == GS_CREDITS) // Somebody hasn't heard of the credits skip button...
{
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
return;
}
// Allow exiting without cheating if at least one player beat the level
// Consistent with just setting playersforexit to one
if (splitscreen || multiplayer)
{
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator || players[i].bot)
continue;
if (players[i].quittime > 30 * TICRATE)
continue;
if (players[i].lives <= 0)
continue;
if ((players[i].pflags & PF_FINISHED) || players[i].exiting)
{
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
return;
}
}
}
// Only consider it a cheat if we're not allowed to go to the next map
if (M_CampaignWarpIsCheat(gametype, G_GetNextMap(true, true) + 1, serverGamedata))
CONS_Alert(CONS_NOTICE, M_GetText("Cheats must be enabled to force exit to a locked level!\n"));
else
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
}
static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
{
boolean cheat = false;
cheat = (boolean)READUINT8(*cp);
(void)cp;
// Ignore duplicate XD_EXITLEVEL commands.
if (gameaction == ga_completed)
{
return;
}
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
@ -4535,11 +4560,6 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
return;
}
if (G_CoopGametype() && cheat)
{
G_SetUsedCheats(false);
}
G_ExitLevel();
}

View file

@ -201,7 +201,6 @@ void D_SendPlayerConfig(void);
void Command_ExitGame_f(void);
void Command_Retry_f(void);
void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore
void D_SendExitLevel(boolean cheat);
void D_MapChange(INT32 pmapnum, INT32 pgametype, boolean pultmode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pfromlevelselect);
boolean IsPlayerAdmin(INT32 playernum);
void SetAdminPlayer(INT32 playernum);

View file

@ -2244,7 +2244,7 @@ void P_CheckTimeLimit(void)
}
if (server)
D_SendExitLevel(false);
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
//Optional tie-breaker for Match/CTF
@ -2307,11 +2307,11 @@ void P_CheckTimeLimit(void)
}
}
if (server)
D_SendExitLevel(false);
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
if (server)
D_SendExitLevel(false);
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
/** Checks if a player's score is over the pointlimit and the round should end.
@ -2340,7 +2340,7 @@ void P_CheckPointLimit(void)
if ((UINT32)cv_pointlimit.value <= redscore || (UINT32)cv_pointlimit.value <= bluescore)
{
if (server)
D_SendExitLevel(false);
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
}
else
@ -2353,7 +2353,7 @@ void P_CheckPointLimit(void)
if ((UINT32)cv_pointlimit.value <= players[i].score)
{
if (server)
D_SendExitLevel(false);
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
return;
}
}
@ -2397,7 +2397,7 @@ void P_CheckSurvivors(void)
{
CONS_Printf(M_GetText("The IT player has left the game.\n"));
if (server)
D_SendExitLevel(false);
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
return;
}
@ -2417,7 +2417,7 @@ void P_CheckSurvivors(void)
{
CONS_Printf(M_GetText("All players have been tagged!\n"));
if (server)
D_SendExitLevel(false);
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
return;
@ -2429,7 +2429,7 @@ void P_CheckSurvivors(void)
{
CONS_Printf(M_GetText("There are no players able to become IT.\n"));
if (server)
D_SendExitLevel(false);
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
return;
@ -2441,7 +2441,7 @@ void P_CheckSurvivors(void)
{
CONS_Printf(M_GetText("All players have been tagged!\n"));
if (server)
D_SendExitLevel(false);
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
}

View file

@ -8315,7 +8315,7 @@ static boolean P_LoadAddon(UINT16 numlumps)
{
CONS_Printf(M_GetText("Current map %d replaced by added file, ending the level to ensure consistency.\n"), gamemap);
if (server)
D_SendExitLevel(false);
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
return true;

View file

@ -11904,7 +11904,7 @@ void P_PlayerThink(player_t *player)
if (!total || ((4*exiting)/total) >= numneeded)
{
if (server)
D_SendExitLevel(false);
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
else
player->exiting = 3;
@ -11912,7 +11912,7 @@ void P_PlayerThink(player_t *player)
else
{
if (server)
D_SendExitLevel(false);
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
}