2020-09-02 22:29:17 +00:00
/*
* * cheats . cpp
* * Common cheat code
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Copyright 1999-2016 Randy Heit
// Copyright 2002-2020 Christoph Oelckers
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OFf
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
2020-09-02 23:32:51 +00:00
# include "build.h"
2020-09-02 22:29:17 +00:00
# include "gamestruct.h"
# include "printf.h"
# include "c_cvars.h"
# include "cheathandler.h"
# include "c_dispatch.h"
# include "d_net.h"
# include "gamestate.h"
# include "gstrings.h"
2020-09-03 21:10:28 +00:00
# include "gamecontrol.h"
2021-04-15 22:50:13 +00:00
# include "screenjob.h"
2020-09-03 21:10:28 +00:00
# include "mapinfo.h"
2021-07-17 07:42:59 +00:00
# include "statistics.h"
2020-09-02 22:29:17 +00:00
2022-10-01 07:10:13 +00:00
CVAR ( Bool , sv_cheats , false , CVAR_ARCHIVE | CVAR_SERVERINFO )
2020-09-02 22:29:17 +00:00
CVAR ( Bool , cl_blockcheats , false , 0 )
2020-09-03 21:10:28 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-09-02 22:29:17 +00:00
bool CheckCheatmode ( bool printmsg , bool sponly )
{
if ( ( sponly & & netgame ) | | gamestate ! = GS_LEVEL )
{
if ( printmsg ) Printf ( " Not in a singleplayer game. \n " ) ;
return true ;
}
else if ( ( netgame /*|| deathmatch*/ ) & & ( ! sv_cheats ) )
{
if ( printmsg ) Printf ( " sv_cheats must be true to enable this command. \n " ) ;
return true ;
}
else if ( cl_blockcheats ! = 0 )
{
if ( printmsg & & cl_blockcheats = = 1 ) Printf ( " cl_blockcheats is turned on and disabled this command. \n " ) ;
return true ;
}
else
{
const char * gamemsg = gi - > CheckCheatMode ( ) ; // give the game anopportuity to add its own blocks.
if ( printmsg & & gamemsg )
{
Printf ( " %s \n " , gamemsg ) ;
return true ;
}
return false ;
}
}
2020-09-03 21:10:28 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-09-02 22:29:17 +00:00
void genericCheat ( int player , uint8_t * * stream , bool skip )
{
int cheat = ReadByte ( stream ) ;
if ( skip ) return ;
const char * msg = gi - > GenericCheat ( player , cheat ) ;
if ( ! msg | | ! * msg ) // Don't print blank lines.
return ;
if ( player = = myconnectindex )
2020-09-25 20:41:17 +00:00
Printf ( PRINT_NOTIFY , " %s \n " , msg ) ;
2020-09-02 22:29:17 +00:00
else
{
FString message = GStrings ( " TXT_X_CHEATS " ) ;
//message.Substitute("%s", player->userinfo.GetName()); // fixme - make globally accessible
2020-09-25 20:41:17 +00:00
Printf ( PRINT_NOTIFY , " %s: %s \n " , message . GetChars ( ) , msg ) ;
2020-09-02 22:29:17 +00:00
}
}
2020-09-03 21:10:28 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-09-02 22:29:17 +00:00
2020-09-25 01:26:28 +00:00
CCMD ( fly )
{
if ( ! CheckCheatmode ( true , true ) )
{
Net_WriteByte ( DEM_GENERICCHEAT ) ;
Net_WriteByte ( CHT_FLY ) ;
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-09-02 22:29:17 +00:00
CCMD ( god )
{
if ( ! CheckCheatmode ( true , true ) ) // Right now the god cheat is a global setting in some games and not a player property. This should be changed.
{
Net_WriteByte ( DEM_GENERICCHEAT ) ;
Net_WriteByte ( CHT_GOD ) ;
}
}
CCMD ( godon )
{
if ( ! CheckCheatmode ( true , true ) )
{
Net_WriteByte ( DEM_GENERICCHEAT ) ;
Net_WriteByte ( CHT_GODON ) ;
}
}
CCMD ( godoff )
{
if ( ! CheckCheatmode ( true , true ) )
{
Net_WriteByte ( DEM_GENERICCHEAT ) ;
Net_WriteByte ( CHT_GODOFF ) ;
}
}
2020-09-02 22:41:32 +00:00
CCMD ( noclip )
{
if ( ! CheckCheatmode ( true , true ) )
{
Net_WriteByte ( DEM_GENERICCHEAT ) ;
Net_WriteByte ( CHT_NOCLIP ) ;
}
}
2020-09-03 21:10:28 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-09-03 14:31:31 +00:00
CCMD ( give )
{
2020-09-22 06:40:33 +00:00
static const char * type [ ] = { " ALL " , " AMMO " , " ARMOR " , " HEALTH " , " INVENTORY " , " ITEMS " , " KEYS " , " WEAPONS " } ;
2020-09-03 14:31:31 +00:00
if ( argv . argc ( ) < 2 )
{
Printf ( " give <all|health|weapons|ammo|armor|keys|inventory>: gives requested item \n " ) ;
return ;
}
2021-10-08 17:06:41 +00:00
2020-09-03 14:31:31 +00:00
for ( size_t i = 0 ; i < countof ( type ) ; i + + )
{
if ( ! stricmp ( argv [ 1 ] , type [ i ] ) )
{
2021-10-08 17:06:41 +00:00
if ( ! CheckCheatmode ( true , true ) )
{
Net_WriteByte ( DEM_GIVE ) ;
Net_WriteByte ( ( uint8_t ) i ) ;
}
return ;
2020-09-03 14:31:31 +00:00
}
}
2021-10-08 17:06:41 +00:00
Printf ( " Unable to give %s \n " , argv [ 1 ] ) ;
2020-09-03 21:10:28 +00:00
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void CompleteLevel ( MapRecord * map )
{
gameaction = ga_completed ;
g_nextmap = ! currentLevel | | ! ( currentLevel - > flags & MI_FORCEEOG ) ? map : nullptr ;
g_nextskill = - 1 ; // This does not change the skill
2021-08-13 20:25:13 +00:00
g_bossexit = false ;
2020-09-03 21:10:28 +00:00
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void changeMap ( int player , uint8_t * * stream , bool skip )
{
int skill = ( int8_t ) ReadByte ( stream ) ;
2021-08-13 20:25:13 +00:00
int bossexit = ( int8_t ) ReadByte ( stream ) ;
2020-09-03 21:10:28 +00:00
auto mapname = ReadStringConst ( stream ) ;
if ( skip ) return ;
auto map = FindMapByName ( mapname ) ;
if ( map | | * mapname = = 0 ) // mapname = "" signals end of episode
{
gameaction = ga_completed ;
g_nextmap = map ;
g_nextskill = skill ;
2021-08-13 20:25:13 +00:00
g_bossexit = bossexit ;
2020-09-03 21:10:28 +00:00
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-04-15 22:50:13 +00:00
void endScreenJob ( int player , uint8_t * * stream , bool skip )
{
2021-04-27 23:12:07 +00:00
if ( ! skip ) gameaction = ga_endscreenjob ;
2021-04-15 22:50:13 +00:00
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-08-13 20:25:13 +00:00
void ChangeLevel ( MapRecord * map , int skill , bool bossexit )
2020-09-03 21:10:28 +00:00
{
Net_WriteByte ( DEM_CHANGEMAP ) ;
Net_WriteByte ( skill ) ;
2021-08-13 20:25:13 +00:00
Net_WriteByte ( bossexit ) ;
2023-10-07 21:53:49 +00:00
Net_WriteString ( map ? map - > labelName . GetChars ( ) : nullptr ) ;
2020-09-03 21:10:28 +00:00
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-07-20 08:41:39 +00:00
void DeferredStartGame ( MapRecord * map , int skill , bool nostopsound )
2020-09-03 21:10:28 +00:00
{
g_nextmap = map ;
g_nextskill = skill ;
2021-08-13 20:25:13 +00:00
g_bossexit = false ;
2020-10-10 11:30:23 +00:00
gameaction = nostopsound ? ga_newgamenostopsound : ga_newgame ;
2020-09-03 21:10:28 +00:00
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static MapRecord * levelwarp_common ( FCommandLine & argv , const char * cmdname , const char * t2 )
{
int numparm = g_gameType & ( GAMEFLAG_SW | GAMEFLAG_PSEXHUMED ) ? 1 : 2 ; // Handle games with episodic and non-episodic level order.
2021-05-02 08:35:43 +00:00
if ( numparm = = 2 & & argv . argc ( ) = = 2 ) numparm = 1 ;
2020-09-03 21:10:28 +00:00
if ( argv . argc ( ) < = numparm )
{
if ( numparm = = 2 ) Printf ( PRINT_BOLD , " %s <e> <m>: %s episode 'e' and map 'm' \n " , cmdname , t2 ) ;
else Printf ( PRINT_BOLD , " %s <m>: %s map 'm' \n " , cmdname , t2 ) ;
return nullptr ;
}
// Values are one-based.
2020-09-04 22:00:55 +00:00
int e = numparm = = 2 ? atoi ( argv [ 1 ] ) : 1 ;
2020-09-03 21:10:28 +00:00
int m = atoi ( numparm = = 2 ? argv [ 2 ] : argv [ 1 ] ) ;
if ( e < = 0 | | m < = 0 )
{
Printf ( PRINT_BOLD , " Invalid level! Numbers must be > 0 \n " ) ;
return nullptr ;
}
2021-05-02 08:35:43 +00:00
auto map = FindMapByIndex ( e , m ) ;
2020-09-03 21:10:28 +00:00
if ( ! map )
{
if ( numparm = = 2 ) Printf ( PRINT_BOLD , " Level E%s L%s not found! \n " , argv [ 1 ] , argv [ 2 ] ) ;
else Printf ( PRINT_BOLD , " Level %s not found! \n " , argv [ 1 ] ) ;
return nullptr ;
}
2023-10-07 21:53:49 +00:00
if ( fileSystem . FindFile ( map - > fileName . GetChars ( ) ) < 0 )
2020-09-04 18:47:40 +00:00
{
Printf ( PRINT_BOLD , " %s: map file not found \n " , map - > fileName . GetChars ( ) ) ;
}
2020-09-03 21:10:28 +00:00
return map ;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
CCMD ( levelwarp )
{
2020-11-29 14:22:54 +00:00
if ( ! gi - > CanSave ( ) )
2020-09-03 21:10:28 +00:00
{
Printf ( " Use the startgame command when not in a game. \n " ) ;
return ;
}
#if 0
if ( /*!players[consoleplayer].settings_controller &&*/ netgame )
{
Printf ( " Only setting controllers can change the map. \n " ) ;
return ;
}
# endif
auto map = levelwarp_common ( argv , " levelwarp " , " warp to " ) ;
if ( map )
{
2021-07-20 08:51:34 +00:00
ChangeLevel ( map , g_nextskill ) ;
2020-09-03 21:10:28 +00:00
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-09-05 12:02:07 +00:00
CCMD ( levelstart )
2020-09-03 21:10:28 +00:00
{
if ( netgame )
{
2020-09-05 12:02:07 +00:00
Printf ( " Use " TEXTCOLOR_BOLD " levelwarp " TEXTCOLOR_NORMAL " instead. " TEXTCOLOR_BOLD " levelstart "
2020-09-03 21:10:28 +00:00
TEXTCOLOR_NORMAL " is for single-player only. \n " ) ;
return ;
}
auto map = levelwarp_common ( argv , " start game " , " start new game at " ) ;
if ( map )
{
2021-07-20 08:51:34 +00:00
DeferredStartGame ( map , g_nextskill ) ;
2020-09-03 21:10:28 +00:00
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
CCMD ( changemap )
{
if ( argv . argc ( ) < 2 )
{
Printf ( PRINT_BOLD , " changemap <mapname>: warp to the given map, identified by its name. \n " ) ;
return ;
}
2020-11-29 14:22:54 +00:00
if ( ! gi - > CanSave ( ) )
2020-09-03 21:10:28 +00:00
{
Printf ( " Use the map command when not in a game. \n " ) ;
return ;
}
#if 0
if ( /*!players[consoleplayer].settings_controller &&*/ netgame )
{
Printf ( " Only setting controllers can change the map. \n " ) ;
return ;
}
# endif
FString mapname = argv [ 1 ] ;
2023-10-07 21:53:49 +00:00
auto map = FindMapByName ( mapname . GetChars ( ) ) ;
2020-09-03 21:10:28 +00:00
if ( map = = nullptr )
{
// got a user map
Printf ( PRINT_BOLD , " %s: Map not defined. \n " , mapname . GetChars ( ) ) ;
return ;
}
if ( map - > flags & MI_USERMAP )
{
// got a user map
Printf ( PRINT_BOLD , " %s: Cannot warp to user maps. \n " , mapname . GetChars ( ) ) ;
return ;
}
2023-10-07 21:53:49 +00:00
if ( fileSystem . FindFile ( map - > fileName . GetChars ( ) ) < 0 )
2020-09-04 18:47:40 +00:00
{
Printf ( PRINT_BOLD , " %s: map file not found \n " , map - > fileName . GetChars ( ) ) ;
}
2021-07-20 08:51:34 +00:00
ChangeLevel ( map , g_nextskill ) ;
2020-09-03 21:10:28 +00:00
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-12-27 09:33:29 +00:00
CCMD ( map )
2020-09-03 21:10:28 +00:00
{
2021-12-27 09:33:29 +00:00
if ( argv . argc ( ) < 2 )
{
Printf ( PRINT_BOLD , " map <mapname>: start new game at the given map, identified by its name. \n " ) ;
return ;
}
if ( netgame )
{
Printf ( " Use " TEXTCOLOR_BOLD " changemap " TEXTCOLOR_NORMAL " instead. " TEXTCOLOR_BOLD " map "
TEXTCOLOR_NORMAL " is for single-player only. \n " ) ;
return ;
}
FString mapfilename = argv [ 1 ] ;
2020-09-03 21:10:28 +00:00
DefaultExtension ( mapfilename , " .map " ) ;
// Check if the map is already defined.
2021-12-27 09:33:29 +00:00
auto map = FindMapByName ( argv [ 1 ] ) ;
2020-09-03 21:10:28 +00:00
if ( map = = nullptr )
{
2023-10-07 21:53:49 +00:00
map = SetupUserMap ( mapfilename . GetChars ( ) , g_gameType & GAMEFLAG_DUKE ? " dethtoll.mid " : nullptr ) ;
2020-09-03 21:10:28 +00:00
}
if ( map )
{
2023-10-07 21:53:49 +00:00
if ( fileSystem . FindFile ( map - > fileName . GetChars ( ) ) < 0 )
2020-09-04 18:47:40 +00:00
{
Printf ( PRINT_BOLD , " %s: map file not found \n " , map - > fileName . GetChars ( ) ) ;
}
2021-07-20 08:51:34 +00:00
DeferredStartGame ( map , g_nextskill ) ;
2020-09-03 21:10:28 +00:00
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
CCMD ( restartmap )
{
if ( gamestate ! = GS_LEVEL | | currentLevel = = nullptr )
{
Printf ( " Must be in a game to restart a level. \n " ) ;
return ;
}
2021-07-20 08:51:34 +00:00
ChangeLevel ( currentLevel , g_nextskill ) ;
2020-09-03 21:10:28 +00:00
}
2020-10-15 23:31:11 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
CUSTOM_CVAR ( Float , i_timescale , 1.0f , CVAR_NOINITCALL )
{
if ( netgame )
{
Printf ( " Time scale cannot be changed in net games. \n " ) ;
self = 1.0f ;
}
else if ( self > = 0.05f )
{
I_FreezeTime ( true ) ;
TimeScale = self ;
I_FreezeTime ( false ) ;
}
else
{
Printf ( " Time scale must be at least 0.05! \n " ) ;
}
}
2021-07-17 07:42:59 +00:00
CCMD ( endofgame )
{
STAT_Update ( true ) ;
2021-07-20 08:51:34 +00:00
ChangeLevel ( nullptr , g_nextskill ) ;
2021-07-17 07:42:59 +00:00
}
2021-07-20 08:50:46 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
CCMD ( skill )
{
if ( gamestate = = GS_LEVEL )
{
auto argsCount = argv . argc ( ) ;
if ( argsCount < 2 )
{
auto currentSkill = gi - > GetCurrentSkill ( ) ;
if ( currentSkill > = 0 )
{
2023-10-07 21:53:49 +00:00
Printf ( " Current skill is %d (%s) \n " , currentSkill , GStrings . localize ( gSkillNames [ currentSkill ] . GetChars ( ) ) ) ;
2021-07-20 08:50:46 +00:00
}
else if ( currentSkill = = - 1 )
{
2021-10-08 17:06:41 +00:00
Printf ( " Current skill is not set \n " ) ;
2021-07-20 08:50:46 +00:00
}
else if ( currentSkill = = - 2 )
{
Printf ( " This game has no skill settings. \n " ) ;
}
else
{
2021-10-08 17:06:41 +00:00
Printf ( " Current skill is an unknown/unsupported value (%d) \n " , currentSkill ) ;
2021-07-20 08:50:46 +00:00
}
}
else if ( argsCount = = 2 )
{
2021-11-16 10:46:43 +00:00
// Get maximum valid skills for loaded game.
auto maxvalidskills = 0 ;
for ( auto i = 0 ; i < MAXSKILLS ; i + + )
{
if ( gSkillNames [ i ] . IsNotEmpty ( ) )
{
maxvalidskills + + ;
}
}
// Test and set skill if its legal.
2021-07-20 08:50:46 +00:00
auto newSkill = atoi ( argv [ 1 ] ) ;
2021-11-16 10:46:43 +00:00
if ( newSkill > = 0 & & newSkill < maxvalidskills )
2021-07-20 08:50:46 +00:00
{
g_nextskill = newSkill ;
Printf ( " Skill will be changed for next game. \n " ) ;
}
else
{
2021-11-16 10:46:43 +00:00
Printf ( " Please specify a skill level between 0 and %d \n " , maxvalidskills - 1 ) ;
for ( auto i = 0 ; i < maxvalidskills ; i + + )
{
2023-10-07 21:53:49 +00:00
Printf ( " %d = '%s' \n " , i , GStrings . localize ( gSkillNames [ i ] . GetChars ( ) ) ) ;
2021-11-16 10:46:43 +00:00
}
2021-07-20 08:50:46 +00:00
}
}
else if ( argsCount > 2 )
{
2021-11-16 10:46:43 +00:00
Printf ( PRINT_BOLD , " skill <level>: returns the current skill level, and optionally sets the skill level for the next game if provided and is valid. \n " ) ;
2021-07-20 08:50:46 +00:00
}
}
else
{
Printf ( " Currently not in a game. \n " ) ;
}
}