diff --git a/src/d_main.c b/src/d_main.c index 22a676255..24c70843a 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1737,14 +1737,15 @@ void D_SRB2Main(void) // Prevent warping to nonexistent levels if (W_CheckNumForName(G_BuildMapName(pstartmap)) == LUMPERROR) I_Error("Could not warp to %s (map not found)\n", G_BuildMapName(pstartmap)); - // Prevent warping to locked levels - // ... unless you're in a dedicated server. Yes, technically this means you can view any level by - // running a dedicated server and joining it yourself, but that's better than making dedicated server's - // lives hell. - else if (!dedicated && M_MapLocked(pstartmap, serverGamedata)) - I_Error("You need to unlock this level before you can warp to it!\n"); else { + if (M_CampaignWarpIsCheat(gametype, pstartmap, serverGamedata)) + { + // If you're warping via command line, you know what you're doing. + // No need to I_Error over this. + G_SetUsedCheats(false); + } + D_MapChange(pstartmap, gametype, ultimatemode, true, 0, false, false); } } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 77a271825..6df79c7bd 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1902,8 +1902,8 @@ static void Command_Map_f(void) size_t option_gametype; const char *gametypename; boolean newresetplayers; - - boolean wouldSetCheats; + boolean prevent_cheat; + boolean set_cheated; INT32 newmapnum; @@ -1924,21 +1924,33 @@ static void Command_Map_f(void) option_gametype = COM_CheckPartialParm("-g"); newresetplayers = ! COM_CheckParm("-noresetplayers"); - wouldSetCheats = - !( netgame || multiplayer ) && - !( usedCheats ); + prevent_cheat = !( usedCheats ) && !( option_force || cv_debug ); - if (wouldSetCheats && !option_force) + if (!( netgame || multiplayer )) { - /* May want to be more descriptive? */ - CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n")); - return; + if (prevent_cheat) + { + /* May want to be more descriptive? */ + CONS_Printf(M_GetText("Cheats must be enabled to level change in single player.\n")); + return; + } + else + { + set_cheated = true; + } } - if (!newresetplayers && !cv_debug) + if (!newresetplayers) { - CONS_Printf(M_GetText("DEVMODE must be enabled.\n")); - return; + if (prevent_cheat) + { + CONS_Printf(M_GetText("Cheats must be enabled to use -noresetplayers.\n")); + return; + } + else + { + set_cheated = true; + } } if (option_gametype) @@ -1946,7 +1958,7 @@ static void Command_Map_f(void) if (!multiplayer) { CONS_Printf(M_GetText( - "You can't switch gametypes in single player!\n")); + "You can't switch gametypes in single player!\n")); return; } else if (COM_Argc() < option_gametype + 2)/* no argument after? */ @@ -1959,7 +1971,9 @@ static void Command_Map_f(void) } if (!( first_option = COM_FirstOption() )) + { first_option = COM_Argc(); + } if (first_option < 2) { @@ -1982,11 +1996,6 @@ static void Command_Map_f(void) return; } - if (wouldSetCheats && option_force) - { - G_SetUsedCheats(false); - } - // new gametype value // use current one by default if (option_gametype) @@ -2028,15 +2037,13 @@ static void Command_Map_f(void) } // don't use a gametype the map doesn't support - if (cv_debug || option_force || cv_skipmapcheck.value) - fromlevelselect = false; // The player wants us to trek on anyway. Do so. // G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer - else + if (!( + mapheaderinfo[newmapnum-1] && + mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype) + )) { - if (!( - mapheaderinfo[newmapnum-1] && - mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype) - )) + if (prevent_cheat && !cv_skipmapcheck.value) { CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum), (multiplayer ? gametype_cons_t[newgametype].strvalue : "Single Player")); @@ -2046,23 +2053,33 @@ static void Command_Map_f(void) } else { - fromlevelselect = - ( netgame || multiplayer ) && - newgametype == gametype && - gametypedefaultrules[newgametype] & GTR_CAMPAIGN; + // The player wants us to trek on anyway. Do so. + fromlevelselect = false; + set_cheated = ((gametypedefaultrules[newgametype] & GTR_CAMPAIGN) == GTR_CAMPAIGN); } } + else + { + fromlevelselect = + ( netgame || multiplayer ) && + newgametype == gametype && + (gametypedefaultrules[newgametype] & GTR_CAMPAIGN); + } // Prevent warping to locked levels - // ... unless you're in a dedicated server. Yes, technically this means you can view any level by - // running a dedicated server and joining it yourself, but that's better than making dedicated server's - // lives hell. - if (!dedicated && M_MapLocked(newmapnum, serverGamedata)) + if (M_CampaignWarpIsCheat(newgametype, newmapnum, serverGamedata)) { - CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n")); - Z_Free(realmapname); - Z_Free(mapname); - return; + if (prevent_cheat) + { + CONS_Alert(CONS_NOTICE, M_GetText("Cheats must be enabled to warp to a locked level!\n")); + Z_Free(realmapname); + Z_Free(mapname); + return; + } + else + { + set_cheated = true; + } } // Ultimate Mode only in SP via menu @@ -2079,6 +2096,11 @@ static void Command_Map_f(void) } tutorialmode = false; // warping takes us out of tutorial mode + if (set_cheated && !usedCheats) + { + G_SetUsedCheats(false); + } + D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, fromlevelselect); Z_Free(realmapname); @@ -4555,25 +4577,37 @@ 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 (!(netgame || (multiplayer && gametype != GT_COOP)) && !cv_debug) - CONS_Printf(M_GetText("This only works in a netgame.\n")); - else if (!(server || (IsPlayerAdmin(consoleplayer)))) + 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 - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + D_SendExitLevel(true); } static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) { - (void)cp; + boolean cheat = false; + + cheat = (boolean)READUINT8(*cp); // Ignore duplicate XD_EXITLEVEL commands. if (gameaction == ga_completed) + { return; + } if (playernum != serverplayer && !IsPlayerAdmin(playernum)) { @@ -4583,6 +4617,11 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) return; } + if (G_CoopGametype() && cheat) + { + G_SetUsedCheats(false); + } + G_ExitLevel(); } diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 26bf4d5c6..8bbc801d0 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -201,6 +201,7 @@ 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); diff --git a/src/f_finale.c b/src/f_finale.c index d03795dc4..162e87d29 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1644,7 +1644,7 @@ void F_GameEvaluationTicker(void) sparklloop = 0; } - if (finalecount == 5*TICRATE) + if (G_CoopGametype() && !stagefailed && finalecount == 5*TICRATE) { serverGamedata->timesBeaten++; clientGamedata->timesBeaten++; diff --git a/src/g_game.c b/src/g_game.c index 04b1ffe4d..24a771372 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2137,7 +2137,7 @@ boolean G_Responder(event_t *ev) if (! netgame) F_StartGameEvaluation(); else if (server || IsPlayerAdmin(consoleplayer)) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + D_SendExitLevel(false); return true; } } @@ -3861,7 +3861,7 @@ static INT16 RandMap(UINT32 tolflags, INT16 pprevmap) for (ix = 0; ix < NUMMAPS; ix++) if (mapheaderinfo[ix] && (mapheaderinfo[ix]->typeoflevel & tolflags) == tolflags && ix != pprevmap // Don't pick the same map. - && (dedicated || !M_MapLocked(ix+1, serverGamedata)) // Don't pick locked maps. + && (!M_MapLocked(ix+1, serverGamedata)) // Don't pick locked maps. ) okmaps[numokmaps++] = ix; diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 2e3bb9c68..63a866606 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -1487,7 +1487,6 @@ void LUA_SetHudHook(int hook, huddrawlist_h list) break; case HUD_HOOK(intermission): - lua_pushboolean(gL, intertype == int_spec && - stagefailed); + lua_pushboolean(gL, stagefailed); } } diff --git a/src/m_cond.c b/src/m_cond.c index 13a483ea2..3dfb1dceb 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -467,6 +467,15 @@ UINT8 M_SecretUnlocked(INT32 type, gamedata_t *data) UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data) { + if (dedicated) + { + // If you're in a dedicated server, every level is unlocked. + // Yes, technically this means you can view any level by + // running a dedicated server and joining it yourself, but + // that's better than making dedicated server's lives hell. + return false; + } + if (!mapheaderinfo[mapnum-1] || mapheaderinfo[mapnum-1]->unlockrequired < 0) { return false; @@ -480,6 +489,48 @@ UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data) return false; } +UINT8 M_CampaignWarpIsCheat(INT32 gt, INT32 mapnum, gamedata_t *data) +{ + if (M_MapLocked(mapnum, data) == true) + { + // Warping to locked maps is definitely always a cheat + return true; + } + + if ((gametypedefaultrules[gt] & GTR_CAMPAIGN) == 0) + { + // Not a campaign, do whatever you want. + return false; + } + + if (G_IsSpecialStage(mapnum)) + { + // Warping to special stages is a cheat + return true; + } + + if (mapheaderinfo[mapnum-1]->menuflags & LF2_HIDEINMENU) + { + // You're never allowed to warp to this level. + return true; + } + + if (mapheaderinfo[mapnum-1]->menuflags & LF2_NOVISITNEEDED) + { + // You're always allowed to warp to this level. + return false; + } + + if (mapnum == spstage_start) + { + // Warping to the first level is never a cheat + return false; + } + + // It's only a cheat if you've never been there. + return (!(data->mapvisited[mapnum])); +} + INT32 M_CountEmblems(gamedata_t *data) { INT32 found = 0, i; diff --git a/src/m_cond.h b/src/m_cond.h index 2be8d564b..2491a384c 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -252,6 +252,7 @@ void M_SilentUpdateSkinAvailabilites(void); UINT8 M_AnySecretUnlocked(gamedata_t *data); UINT8 M_SecretUnlocked(INT32 type, gamedata_t *data); UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data); +UINT8 M_CampaignWarpIsCheat(INT32 gt, INT32 mapnum, gamedata_t *data); INT32 M_CountEmblems(gamedata_t *data); // Emblem shit diff --git a/src/p_inter.c b/src/p_inter.c index 4d22ba343..74ca6972f 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2235,7 +2235,7 @@ void P_CheckTimeLimit(void) } if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + D_SendExitLevel(false); } //Optional tie-breaker for Match/CTF @@ -2298,11 +2298,11 @@ void P_CheckTimeLimit(void) } } if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + D_SendExitLevel(false); } if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + D_SendExitLevel(false); } /** Checks if a player's score is over the pointlimit and the round should end. @@ -2331,7 +2331,7 @@ void P_CheckPointLimit(void) if ((UINT32)cv_pointlimit.value <= redscore || (UINT32)cv_pointlimit.value <= bluescore) { if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + D_SendExitLevel(false); } } else @@ -2344,7 +2344,7 @@ void P_CheckPointLimit(void) if ((UINT32)cv_pointlimit.value <= players[i].score) { if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + D_SendExitLevel(false); return; } } @@ -2388,7 +2388,7 @@ void P_CheckSurvivors(void) { CONS_Printf(M_GetText("The IT player has left the game.\n")); if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + D_SendExitLevel(false); return; } @@ -2408,7 +2408,7 @@ void P_CheckSurvivors(void) { CONS_Printf(M_GetText("All players have been tagged!\n")); if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + D_SendExitLevel(false); } return; @@ -2420,7 +2420,7 @@ void P_CheckSurvivors(void) { CONS_Printf(M_GetText("There are no players able to become IT.\n")); if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + D_SendExitLevel(false); } return; @@ -2432,7 +2432,7 @@ void P_CheckSurvivors(void) { CONS_Printf(M_GetText("All players have been tagged!\n")); if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + D_SendExitLevel(false); } } diff --git a/src/p_setup.c b/src/p_setup.c index cbb817d83..a1440c0ce 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8238,7 +8238,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) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + D_SendExitLevel(false); } return true; diff --git a/src/p_user.c b/src/p_user.c index cf4818298..84d0c0abf 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -11758,7 +11758,7 @@ void P_PlayerThink(player_t *player) if (!total || ((4*exiting)/total) >= numneeded) { if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + D_SendExitLevel(false); } else player->exiting = 3; @@ -11766,7 +11766,7 @@ void P_PlayerThink(player_t *player) else { if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + D_SendExitLevel(false); } } diff --git a/src/y_inter.c b/src/y_inter.c index 5e071171f..796ced8b8 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -998,8 +998,7 @@ void Y_Ticker(void) if (paused || P_AutoPause()) return; - LUA_HookBool(intertype == int_spec && stagefailed, - HOOK(IntermissionThinker)); + LUA_HookBool(stagefailed, HOOK(IntermissionThinker)); intertic++;