2019-12-14 19:15:15 +00:00
//-------------------------------------------------------------------------
/*
Copyright ( C ) 2019 Christoph Oelckers
This is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation ; either version 2
of the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
See the GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*/
//-------------------------------------------------------------------------
2020-01-12 13:54:43 +00:00
# include <stdexcept>
2019-10-25 22:32:49 +00:00
# include "gamecontrol.h"
# include "tarray.h"
# include "zstring.h"
# include "name.h"
# include "sc_man.h"
2019-10-26 11:16:32 +00:00
# include "c_cvars.h"
2019-10-26 17:24:46 +00:00
# include "gameconfigfile.h"
2019-10-26 21:45:55 +00:00
# include "gamecvars.h"
2019-10-26 11:16:32 +00:00
# include "build.h"
2019-10-28 05:47:49 +00:00
# include "inputstate.h"
2019-10-28 21:19:50 +00:00
# include "m_argv.h"
# include "rts.h"
2019-11-01 18:25:42 +00:00
# include "printf.h"
2019-11-03 19:24:50 +00:00
# include "c_bind.h"
2019-11-05 19:07:16 +00:00
# include "v_font.h"
# include "c_console.h"
# include "c_dispatch.h"
# include "i_specialpaths.h"
2020-04-12 06:07:48 +00:00
# include "raze_music.h"
2019-11-14 20:07:43 +00:00
# include "statistics.h"
2019-11-21 21:31:46 +00:00
# include "menu.h"
2019-11-23 11:38:38 +00:00
# include "gstrings.h"
2019-12-08 23:33:14 +00:00
# include "quotemgr.h"
2019-12-09 23:01:45 +00:00
# include "mapinfo.h"
2020-04-12 06:09:38 +00:00
# include "raze_sound.h"
2019-12-23 18:37:40 +00:00
# include "i_system.h"
# include "inputstate.h"
# include "v_video.h"
# include "st_start.h"
2019-12-24 11:59:26 +00:00
# include "s_music.h"
2019-12-24 17:53:29 +00:00
# include "i_video.h"
2020-01-11 16:05:25 +00:00
# include "v_text.h"
# include "resourcefile.h"
2019-12-28 11:59:19 +00:00
# include "c_dispatch.h"
2019-12-24 17:53:29 +00:00
# include "glbackend/glbackend.h"
2020-04-11 21:50:43 +00:00
# include "engineerrors.h"
2020-03-29 13:22:07 +00:00
# include "mmulti.h"
2020-04-11 22:04:02 +00:00
# include "gamestate.h"
2020-04-11 22:11:50 +00:00
# include "gstrings.h"
2020-05-24 21:13:08 +00:00
# include "texturemanager.h"
2020-04-23 19:18:40 +00:00
# include "i_interface.h"
# include "x86.h"
# include "startupinfo.h"
2020-06-14 17:20:04 +00:00
# include "mapinfo.h"
# include "menustate.h"
2020-07-19 10:48:31 +00:00
# include "screenjob.h"
2020-04-23 19:18:40 +00:00
CVAR ( Bool , autoloadlights , true , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
CVAR ( Bool , autoloadbrightmaps , true , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
2020-04-11 22:11:50 +00:00
CUSTOM_CVAR ( String , language , " auto " , CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG )
{
GStrings . UpdateLanguage ( self ) ;
}
2020-03-29 13:22:07 +00:00
2020-04-23 19:18:40 +00:00
CUSTOM_CVAR ( Int , mouse_capturemode , 1 , CVAR_GLOBALCONFIG | CVAR_ARCHIVE )
{
if ( self < 0 )
{
self = 0 ;
}
else if ( self > 2 )
{
self = 2 ;
}
}
2020-03-29 13:22:07 +00:00
// The last remains of sdlayer.cpp
double g_beforeSwapTime ;
GameInterface * gi ;
int myconnectindex , numplayers ;
int connecthead , connectpoint2 [ MAXMULTIPLAYERS ] ;
2020-04-12 03:40:16 +00:00
int32_t xres = - 1 , yres = - 1 , bpp = 0 ;
2020-03-29 13:22:07 +00:00
auto vsnprintfptr = vsnprintf ; // This is an inline in Visual Studio but we need an address for it to satisfy the MinGW compiled libraries.
2020-07-03 19:44:57 +00:00
glcycle_t thinktime , actortime , gameupdatetime , drawtime ;
2020-06-23 19:12:15 +00:00
2020-04-11 22:04:02 +00:00
gamestate_t gamestate = GS_STARTUP ;
2020-04-11 21:54:33 +00:00
FILE * hashfile ;
2020-04-23 19:18:40 +00:00
FStartupInfo GameStartupInfo ;
2019-12-24 17:53:29 +00:00
FMemArena dump ; // this is for memory blocks than cannot be deallocated without some huge effort. Put them in here so that they do not register on shutdown.
2019-10-27 23:24:09 +00:00
InputState inputState ;
2019-10-31 22:25:21 +00:00
int ShowStartupWindow ( TArray < GrpEntry > & ) ;
2020-01-11 16:05:25 +00:00
FString GetGameFronUserFiles ( ) ;
2019-11-01 18:25:42 +00:00
void InitFileSystem ( TArray < GrpEntry > & ) ;
2020-01-11 16:05:25 +00:00
void I_SetWindowTitle ( const char * caption ) ;
2020-02-03 20:14:35 +00:00
void InitENet ( ) ;
void ShutdownENet ( ) ;
2020-04-12 06:07:48 +00:00
void S_ParseSndInfo ( ) ;
2020-05-31 08:53:11 +00:00
void I_DetectOS ( void ) ;
2020-06-14 17:20:04 +00:00
void LoadScripts ( ) ;
2020-05-31 08:53:11 +00:00
2020-04-12 06:07:48 +00:00
2020-01-08 00:00:57 +00:00
bool AppActive ;
2019-10-27 23:24:09 +00:00
2019-10-31 23:32:56 +00:00
FString currentGame ;
2019-10-28 22:46:15 +00:00
FString LumpFilter ;
2019-11-28 00:02:45 +00:00
TMap < FName , int32_t > NameToTileIndex ; // for assigning names to tiles. The menu accesses this list. By default it gets everything from the dynamic tile map in Duke Nukem and Redneck Rampage.
// Todo: Add additional definition file for the other games or textures not in that list so that the menu does not have to rely on indices.
2019-10-26 11:16:32 +00:00
2019-12-24 14:28:00 +00:00
CVAR ( Bool , queryiwad , true , CVAR_ARCHIVE | CVAR_GLOBALCONFIG ) ;
CVAR ( String , defaultiwad , " " , CVAR_ARCHIVE | CVAR_GLOBALCONFIG ) ;
CVAR ( Bool , disableautoload , false , CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG )
//CVAR(Bool, autoloadbrightmaps, false, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG) // hopefully this is an option for later
//CVAR(Bool, autoloadlights, false, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG)
2019-10-26 11:16:32 +00:00
2020-01-22 12:53:26 +00:00
extern int hud_size_max ;
2020-05-28 23:22:45 +00:00
int paused ;
bool pausedWithKey ;
2020-04-11 22:11:50 +00:00
CUSTOM_CVAR ( Int , cl_gender , 0 , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
{
if ( self < 0 | | self > 3 ) self = 0 ;
}
int StrTable_GetGender ( )
{
return cl_gender ;
}
bool validFilter ( const char * str ) ;
static StringtableCallbacks stblcb =
{
validFilter ,
StrTable_GetGender
} ;
2020-05-17 06:51:49 +00:00
extern int chatmodeon ;
2020-04-11 22:11:50 +00:00
2020-04-23 19:18:40 +00:00
bool System_WantGuiCapture ( )
{
bool wantCapt ;
if ( menuactive = = MENU_Off )
{
wantCapt = ConsoleState = = c_down | | ConsoleState = = c_falling | | chatmodeon ;
}
else
{
wantCapt = ( menuactive = = MENU_On | | menuactive = = MENU_OnNoPause ) ;
}
return wantCapt ;
}
bool System_WantLeftButton ( )
{
return false ; // (gamestate == GS_DEMOSCREEN || gamestate == GS_TITLELEVEL);
}
bool System_NetGame ( )
{
return false ; // fixme later. For now there is no netgame support.
}
bool System_WantNativeMouse ( )
{
return false ;
}
static bool System_CaptureModeInGame ( )
{
return true ;
}
2020-01-11 16:05:25 +00:00
2020-05-30 22:01:00 +00:00
static bool System_DisableTextureFilter ( )
{
return hw_useindexedcolortextures ;
}
2020-04-28 20:55:37 +00:00
static IntRect System_GetSceneRect ( )
{
// Special handling so the view with a visible status bar displays properly
int height = windowxy2 . y - windowxy1 . y + 1 , width = windowxy2 . x - windowxy1 . x + 1 ;
IntRect mSceneViewport ;
mSceneViewport . left = windowxy1 . x ;
mSceneViewport . top = windowxy1 . y ;
mSceneViewport . width = width ;
mSceneViewport . height = height ;
return mSceneViewport ;
}
2020-01-08 00:00:57 +00:00
//==========================================================================
//
2020-04-23 19:18:40 +00:00
// DoomSpecificInfo
2020-01-08 00:00:57 +00:00
//
2020-04-23 19:18:40 +00:00
// Called by the crash logger to get application-specific information.
2020-01-08 00:00:57 +00:00
//
//==========================================================================
2020-04-23 19:18:40 +00:00
void System_CrashInfo ( char * buffer , size_t bufflen , const char * lfstr )
2020-01-08 00:00:57 +00:00
{
2020-04-23 19:18:40 +00:00
const char * arg ;
char * const buffend = buffer + bufflen - 2 ; // -2 for CRLF at end
int i ;
buffer + = mysnprintf ( buffer , buffend - buffer , GAMENAME " version %s (%s) " , GetVersionString ( ) , GetGitHash ( ) ) ;
buffer + = snprintf ( buffer , buffend - buffer , " %sCommand line: " , lfstr ) ;
for ( i = 0 ; i < Args - > NumArgs ( ) ; + + i )
{
buffer + = snprintf ( buffer , buffend - buffer , " %s " , Args - > GetArg ( i ) ) ;
}
for ( i = 0 ; ( arg = fileSystem . GetResourceFileName ( i ) ) ! = NULL ; + + i )
{
buffer + = mysnprintf ( buffer , buffend - buffer , " %sFile %d: %s " , lfstr , i , arg ) ;
}
buffer + = mysnprintf ( buffer , buffend - buffer , " %s " , lfstr ) ;
* buffer = 0 ;
2020-01-08 00:00:57 +00:00
}
2020-04-23 19:18:40 +00:00
2020-01-08 00:00:57 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-10-28 21:19:50 +00:00
UserConfig userConfig ;
void UserConfig : : ProcessOptions ( )
{
// -help etc are omitted
// -cfg / -setupfile refer to Build style config which are not supported.
if ( Args - > CheckParm ( " -cfg " ) | | Args - > CheckParm ( " -setupfile " ) )
{
2020-04-11 21:45:45 +00:00
Printf ( " Build-format config files not supported and will be ignored \n " ) ;
2019-10-28 21:19:50 +00:00
}
2019-12-11 22:41:05 +00:00
#if 0 // MP disabled pending evaluation
2019-10-28 21:19:50 +00:00
auto v = Args - > CheckValue ( " -port " ) ;
if ( v ) netPort = strtol ( v , nullptr , 0 ) ;
netServerMode = Args - > CheckParm ( " -server " ) ;
netServerAddress = Args - > CheckValue ( " -connect " ) ;
netPassword = Args - > CheckValue ( " -password " ) ;
2019-12-11 22:41:05 +00:00
# endif
2019-10-28 21:19:50 +00:00
2019-12-11 22:41:05 +00:00
auto v = Args - > CheckValue ( " -addon " ) ;
2019-10-28 21:19:50 +00:00
if ( v )
{
auto val = strtol ( v , nullptr , 0 ) ;
static const char * const addons [ ] = { " DUKE3D.GRP " , " DUKEDC.GRP " , " NWINTER.GRP " , " VACATION.GRP " } ;
2020-02-09 08:48:55 +00:00
if ( val > = 0 & & val < 4 ) gamegrp = addons [ val ] ;
2020-04-11 21:45:45 +00:00
else Printf ( " %s: Unknown Addon \n " , v ) ;
2019-10-28 21:19:50 +00:00
}
else if ( Args - > CheckParm ( " -nam " ) )
{
gamegrp = " NAM.GRP " ;
}
else if ( Args - > CheckParm ( " -napalm " ) )
{
gamegrp = " NAPALM.GRP " ;
}
else if ( Args - > CheckParm ( " -ww2gi " ) )
{
gamegrp = " WW2GI.GRP " ;
}
2019-12-11 22:41:05 +00:00
// Set up all needed content for these two mod which feature a very messy distribution.
// As an alternative they can be zipped up - the launcher will be able to detect and set up such versions automatically.
else if ( Args - > CheckParm ( " -route66 " ) )
{
gamegrp = " REDNECK.GRP " ;
DefaultCon = " GAME66.CON " ;
const char * argv [ ] = { " tilesa66.art " , " tilesb66.art " } ;
AddArt . reset ( new FArgs ( 2 , argv ) ) ;
2020-01-10 20:36:46 +00:00
toBeDeleted . Push ( " turd66.anm*turdmov.anm " ) ;
toBeDeleted . Push ( " turd66.voc*turdmov.voc " ) ;
toBeDeleted . Push ( " end66.anm*rr_outro.anm " ) ;
toBeDeleted . Push ( " end66.voc*rr_outro.voc " ) ;
2019-12-11 22:41:05 +00:00
}
else if ( Args - > CheckParm ( " -cryptic " ) )
{
gamegrp = " BLOOD.RFF " ;
DefaultCon = " CRYPTIC.INI " ;
const char * argv [ ] = { " cpart07.ar_ " , " cpart15.ar_ " } ;
AddArt . reset ( new FArgs ( 2 , argv ) ) ;
}
2019-10-28 21:19:50 +00:00
2019-11-01 18:25:42 +00:00
v = Args - > CheckValue ( " -gamegrp " ) ;
2019-10-28 21:19:50 +00:00
if ( v )
{
gamegrp = v ;
}
else
{
// This is to enable the use of Doom launchers. that are limited to -iwad for specifying the game's main resource.
v = Args - > CheckValue ( " -iwad " ) ;
if ( v )
{
gamegrp = v ;
}
}
Args - > CollectFiles ( " -rts " , " .rts " ) ;
auto rts = Args - > CheckValue ( " -rts " ) ;
if ( rts ) RTS_Init ( rts ) ;
Args - > CollectFiles ( " -map " , " .map " ) ;
CommandMap = Args - > CheckValue ( " -map " ) ;
static const char * defs [ ] = { " -def " , " -h " , nullptr } ;
Args - > CollectFiles ( " -def " , defs , " .def " ) ;
DefaultDef = Args - > CheckValue ( " -def " ) ;
2020-01-10 20:36:46 +00:00
if ( DefaultCon . IsEmpty ( ) )
{
static const char * cons [ ] = { " -con " , " -x " , nullptr } ;
Args - > CollectFiles ( " -con " , cons , " .con " ) ;
DefaultCon = Args - > CheckValue ( " -con " ) ;
if ( DefaultCon . IsEmpty ( ) ) DefaultCon = Args - > CheckValue ( " -ini " ) ;
}
2019-10-28 21:19:50 +00:00
static const char * demos [ ] = { " -playback " , " -d " , " -demo " , nullptr } ;
Args - > CollectFiles ( " -demo " , demos , " .dmo " ) ;
CommandDemo = Args - > CheckValue ( " -demo " ) ;
static const char * names [ ] = { " -pname " , " -name " , nullptr } ;
Args - > CollectFiles ( " -name " , names , " .--- " ) ; // this shouldn't collect any file names at all so use a nonsense extension
CommandName = Args - > CheckValue ( " -name " ) ;
static const char * nomos [ ] = { " -nomonsters " , " -nodudes " , nullptr } ;
Args - > CollectFiles ( " -nomonsters " , nomos , " .--- " ) ; // this shouldn't collect any file names at all so use a nonsense extension
nomonsters = Args - > CheckParm ( " -nomonsters " ) ;
static const char * acons [ ] = { " -addcon " , " -mx " , nullptr } ;
Args - > CollectFiles ( " -addcon " , acons , " .con " ) ;
AddCons . reset ( Args - > GatherFiles ( " -addcon " ) ) ;
static const char * adefs [ ] = { " -adddef " , " -mh " , nullptr } ;
Args - > CollectFiles ( " -adddef " , adefs , " .def " ) ;
AddDefs . reset ( Args - > GatherFiles ( " -adddef " ) ) ;
Args - > CollectFiles ( " -art " , " .art " ) ;
AddArt . reset ( Args - > GatherFiles ( " -art " ) ) ;
nologo = Args - > CheckParm ( " -nologo " ) | | Args - > CheckParm ( " -quick " ) ;
2020-04-12 06:07:48 +00:00
nosound = Args - > CheckParm ( " -nosfx " ) | | Args - > CheckParm ( " -nosound " ) ;
2019-12-24 14:28:00 +00:00
if ( Args - > CheckParm ( " -setup " ) ) queryiwad = 1 ;
else if ( Args - > CheckParm ( " -nosetup " ) ) queryiwad = 0 ;
2019-10-28 21:19:50 +00:00
if ( Args - > CheckParm ( " -file " ) )
{
// For file loading there's two modes:
// If -file is given, all content will be processed in order and the legacy options be ignored entirely.
//This allows mixing directories and GRP files in arbitrary order.
Args - > CollectFiles ( " -file " , NULL ) ;
AddFiles . reset ( Args - > GatherFiles ( " -file " ) ) ;
}
else
{
2019-11-01 18:25:42 +00:00
// Trying to emulate Build. This means to treat RFF files as lowest priority, then all GRPs and then all directories.
2019-10-28 21:19:50 +00:00
// This is only for people depending on lauchers. Since the semantics are so crappy it is strongly recommended to
2019-11-01 18:25:42 +00:00
// use -file instead which gives the user full control over the order in which things are added.
2019-10-28 21:19:50 +00:00
// For single mods this is no problem but don't even think about loading more stuff consistently...
static const char * grps [ ] = { " -g " , " -grp " , nullptr } ;
static const char * dirs [ ] = { " -game_dir " , " -j " , nullptr } ;
static const char * rffs [ ] = { " -rff " , " -snd " , nullptr } ;
static const char * twostep [ ] = { " -rff " , " -grp " , nullptr } ;
// Abuse the inner workings to get the files into proper order. This is not 100% accurate but should work fine for everything that doesn't intentionally fuck things up.
Args - > CollectFiles ( " -rff " , rffs , " .rff " ) ;
Args - > CollectFiles ( " -grp " , grps , nullptr ) ;
Args - > CollectFiles ( " -grp " , twostep , nullptr ) ; // The two previous calls have already brought the content in order so collecting it again gives us one list with everything.
AddFilesPre . reset ( Args - > GatherFiles ( " -grp " ) ) ;
Args - > CollectFiles ( " -game_dir " , dirs , nullptr ) ;
AddFiles . reset ( Args - > GatherFiles ( " -game_dir " ) ) ;
}
2019-12-23 19:55:12 +00:00
if ( Args - > CheckParm ( " -showcoords " ) | | Args - > CheckParm ( " -w " ) )
{
C_DoCommand ( " stat coord " ) ;
}
2019-10-28 21:19:50 +00:00
}
2020-07-15 10:34:42 +00:00
//==========================================================================
//
//
//
//==========================================================================
void CheckUserMap ( )
{
if ( userConfig . CommandMap . IsEmpty ( ) ) return ;
FString startupMap = userConfig . CommandMap ;
if ( startupMap . IndexOfAny ( " / \\ " ) < 0 ) startupMap . Insert ( 0 , " / " ) ;
DefaultExtension ( startupMap , " .map " ) ;
startupMap . Substitute ( " \\ " , " / " ) ;
NormalizeFileName ( startupMap ) ;
if ( fileSystem . FileExists ( startupMap ) )
{
Printf ( " Using level: \" %s \" . \n " , startupMap . GetChars ( ) ) ;
}
else
{
Printf ( " Level \" %s \" not found. \n " , startupMap . GetChars ( ) ) ;
startupMap = " " ;
}
userConfig . CommandMap = startupMap ;
}
2019-10-31 22:25:21 +00:00
//==========================================================================
//
//
//
//==========================================================================
2020-06-11 07:22:16 +00:00
namespace Duke3d
{
: : GameInterface * CreateInterface ( ) ;
}
2019-10-31 22:25:21 +00:00
namespace Blood
{
2019-11-03 11:32:58 +00:00
: : GameInterface * CreateInterface ( ) ;
2019-10-31 22:25:21 +00:00
}
namespace ShadowWarrior
{
2019-11-03 11:32:58 +00:00
: : GameInterface * CreateInterface ( ) ;
2019-10-31 22:25:21 +00:00
}
2019-12-12 23:19:34 +00:00
namespace Powerslave
{
: : GameInterface * CreateInterface ( ) ;
}
2019-10-31 22:25:21 +00:00
2020-06-20 08:46:51 +00:00
extern int MinFPSRate ; // this is a bit messy.
2020-02-12 19:25:59 +00:00
2019-10-31 22:25:21 +00:00
void CheckFrontend ( int flags )
{
if ( flags & GAMEFLAG_BLOOD )
{
2019-11-03 11:32:58 +00:00
gi = Blood : : CreateInterface ( ) ;
2019-10-31 22:25:21 +00:00
}
else if ( flags & GAMEFLAG_SW )
{
2020-06-20 08:46:51 +00:00
MinFPSRate = 40 ;
2019-11-03 11:32:58 +00:00
gi = ShadowWarrior : : CreateInterface ( ) ;
2019-10-31 22:25:21 +00:00
}
2019-12-12 23:19:34 +00:00
else if ( flags & GAMEFLAG_PSEXHUMED )
{
gi = Powerslave : : CreateInterface ( ) ;
}
2020-02-12 19:25:59 +00:00
else
{
2020-07-14 11:09:34 +00:00
gi = Duke3d : : CreateInterface ( ) ;
2020-02-12 19:25:59 +00:00
}
2019-10-31 22:25:21 +00:00
}
2019-12-14 16:15:17 +00:00
void I_StartupJoysticks ( ) ;
void I_ShutdownInput ( ) ;
int RunGame ( ) ;
2019-10-31 22:25:21 +00:00
2019-11-05 19:07:16 +00:00
int GameMain ( )
{
int r ;
2020-04-23 19:18:40 +00:00
static SystemCallbacks syscb =
{
System_WantGuiCapture ,
System_WantLeftButton ,
System_NetGame ,
System_WantNativeMouse ,
System_CaptureModeInGame ,
2020-05-30 22:01:00 +00:00
nullptr ,
nullptr ,
nullptr ,
System_DisableTextureFilter ,
2020-04-28 20:55:37 +00:00
nullptr ,
System_GetSceneRect ,
2020-04-23 19:18:40 +00:00
} ;
sysCallbacks = & syscb ;
2019-11-05 19:07:16 +00:00
try
{
2019-12-14 16:15:17 +00:00
r = RunGame ( ) ;
2019-11-05 19:07:16 +00:00
}
2020-04-11 21:50:43 +00:00
catch ( const CExitEvent & exit )
2019-11-05 19:07:16 +00:00
{
// Just let the rest of the function execute.
r = exit . Reason ( ) ;
}
2019-12-24 15:30:33 +00:00
catch ( const std : : exception & err )
{
// shut down critical systems before showing a message box.
I_ShowFatalError ( err . what ( ) ) ;
r = - 1 ;
}
2020-07-19 10:48:31 +00:00
DeleteScreenJob ( ) ;
2019-12-25 10:26:19 +00:00
M_ClearMenus ( true ) ;
if ( gi )
{
gi - > FreeGameData ( ) ; // Must be done before taking down any subsystems.
}
2019-12-24 15:30:33 +00:00
S_StopMusic ( true ) ;
if ( soundEngine ) delete soundEngine ;
2019-12-24 17:53:29 +00:00
soundEngine = nullptr ;
I_CloseSound ( ) ;
2019-12-24 15:30:33 +00:00
I_ShutdownInput ( ) ;
2019-11-10 18:42:26 +00:00
G_SaveConfig ( ) ;
2019-12-24 15:09:43 +00:00
C_DeinitConsole ( ) ;
2019-12-24 15:30:33 +00:00
V_ClearFonts ( ) ;
2019-12-25 07:57:58 +00:00
vox_deinit ( ) ;
2020-05-24 21:26:47 +00:00
TexMan . DeleteAll ( ) ;
TileFiles . CloseAll ( ) ; // delete the texture data before shutting down graphics.
2019-12-24 17:53:29 +00:00
GLInterface . Deinit ( ) ;
I_ShutdownGraphics ( ) ;
2019-12-24 19:06:55 +00:00
M_DeinitMenus ( ) ;
2019-12-25 10:26:19 +00:00
engineUnInit ( ) ;
2019-12-24 19:06:55 +00:00
if ( gi )
{
delete gi ;
gi = nullptr ;
}
2020-02-03 20:14:35 +00:00
InitENet ( ) ;
2019-12-24 15:30:33 +00:00
DeleteStartupScreen ( ) ;
2020-06-14 17:20:04 +00:00
PClass : : StaticShutdown ( ) ;
2019-12-24 15:09:43 +00:00
if ( Args ) delete Args ;
2019-11-05 19:07:16 +00:00
return r ;
}
2019-10-26 17:24:46 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-11-24 23:02:00 +00:00
void SetDefaultStrings ( )
{
2020-01-09 16:35:38 +00:00
if ( ( g_gameType & GAMEFLAG_DUKE ) & & fileSystem . FindFile ( " E4L1.MAP " ) < 0 )
2020-01-02 23:44:39 +00:00
{
2020-01-09 16:35:38 +00:00
// Pre-Atomic releases do not define this.
2020-01-02 23:44:39 +00:00
gVolumeNames [ 0 ] = " $L.A. Meltdown " ;
gVolumeNames [ 1 ] = " $Lunar Apocalypse " ;
gVolumeNames [ 2 ] = " $Shrapnel City " ;
2020-01-09 16:35:38 +00:00
if ( g_gameType & GAMEFLAG_SHAREWARE ) gVolumeNames [ 3 ] = " $The Birth " ;
2020-01-02 23:44:39 +00:00
gSkillNames [ 0 ] = " $Piece of Cake " ;
gSkillNames [ 1 ] = " $Let's Rock " ;
gSkillNames [ 2 ] = " $Come get Some " ;
gSkillNames [ 3 ] = " $Damn I'm Good " ;
}
2019-12-08 23:33:14 +00:00
// Blood hard codes its skill names, so we have to define them manually.
if ( g_gameType & GAMEFLAG_BLOOD )
2019-11-24 23:02:00 +00:00
{
2019-12-08 23:33:14 +00:00
gSkillNames [ 0 ] = " $STILL KICKING " ;
gSkillNames [ 1 ] = " $PINK ON THE INSIDE " ;
gSkillNames [ 2 ] = " $LIGHTLY BROILED " ;
gSkillNames [ 3 ] = " $WELL DONE " ;
gSkillNames [ 4 ] = " $EXTRA CRISPY " ;
2019-11-24 23:02:00 +00:00
}
2019-12-08 23:33:14 +00:00
//Set a few quotes which are used for common handling of a few status messages
quoteMgr . InitializeQuote ( 23 , " $MESSAGES: ON " ) ;
2019-12-09 01:01:30 +00:00
quoteMgr . InitializeQuote ( 24 , " $MESSAGES: OFF " ) ;
quoteMgr . InitializeQuote ( 83 , " $FOLLOW MODE OFF " ) ;
quoteMgr . InitializeQuote ( 84 , " $FOLLOW MODE ON " ) ;
2019-12-08 23:33:14 +00:00
quoteMgr . InitializeQuote ( 85 , " $AUTORUNOFF " ) ;
quoteMgr . InitializeQuote ( 86 , " $AUTORUNON " ) ;
2019-11-24 23:02:00 +00:00
}
2019-12-08 23:33:14 +00:00
2019-10-26 17:24:46 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-12-23 18:37:40 +00:00
static TArray < GrpEntry > SetupGame ( )
2019-10-26 11:16:32 +00:00
{
2019-10-29 18:53:46 +00:00
// Startup dialog must be presented here so that everything can be set up before reading the keybinds.
2019-10-30 23:41:56 +00:00
auto groups = GrpScan ( ) ;
2019-11-01 18:25:42 +00:00
if ( groups . Size ( ) = = 0 )
{
// Abort if no game data found.
2019-11-03 09:51:47 +00:00
G_SaveConfig ( ) ;
2019-11-01 18:25:42 +00:00
I_Error ( " Unable to find any game data. Please verify your settings. " ) ;
}
decltype ( groups ) usedgroups ;
int groupno = - 1 ;
// If the user has specified a file name, let's see if we know it.
//
2020-01-11 16:05:25 +00:00
FString game = GetGameFronUserFiles ( ) ;
if ( userConfig . gamegrp . IsEmpty ( ) )
{
userConfig . gamegrp = game ;
}
2019-11-19 21:35:52 +00:00
if ( userConfig . gamegrp . Len ( ) )
2019-10-31 23:32:56 +00:00
{
2019-11-19 21:35:52 +00:00
FString gamegrplower = " / " + userConfig . gamegrp . MakeLower ( ) ;
2019-11-01 18:25:42 +00:00
int g = 0 ;
for ( auto & grp : groups )
2019-10-31 23:32:56 +00:00
{
2019-11-19 20:35:35 +00:00
auto grplower = grp . FileName . MakeLower ( ) ;
2019-11-19 21:35:52 +00:00
grplower . Substitute ( " \\ " , " / " ) ;
2019-11-19 20:35:35 +00:00
if ( grplower . LastIndexOf ( gamegrplower ) = = grplower . Len ( ) - gamegrplower . Len ( ) )
2019-10-31 23:32:56 +00:00
{
2019-11-02 12:27:40 +00:00
groupno = g ;
2019-10-31 23:32:56 +00:00
break ;
}
2019-11-01 18:25:42 +00:00
g + + ;
}
}
2019-12-24 14:28:00 +00:00
if ( groupno = = - 1 )
{
int pick = 0 ;
// We got more than one so present the IWAD selection box.
if ( groups . Size ( ) > 1 )
{
// Locate the user's prefered IWAD, if it was found.
if ( defaultiwad [ 0 ] ! = ' \0 ' )
{
for ( unsigned i = 0 ; i < groups . Size ( ) ; + + i )
{
FString & basename = groups [ i ] . FileName ;
if ( stricmp ( basename , defaultiwad ) = = 0 )
{
pick = i ;
break ;
}
}
}
if ( groups . Size ( ) > 1 )
{
TArray < WadStuff > wads ;
for ( auto & found : groups )
{
WadStuff stuff ;
stuff . Name = found . FileInfo . name ;
stuff . Path = ExtractFileBase ( found . FileName ) ;
wads . Push ( stuff ) ;
}
pick = I_PickIWad ( & wads [ 0 ] , ( int ) wads . Size ( ) , queryiwad , pick ) ;
if ( pick > = 0 )
{
// The newly selected IWAD becomes the new default
defaultiwad = groups [ pick ] . FileName ;
}
groupno = pick ;
}
}
2019-12-29 03:49:28 +00:00
else if ( groups . Size ( ) = = 1 )
{
groupno = 0 ;
}
2019-12-24 14:28:00 +00:00
}
2019-11-01 18:25:42 +00:00
2019-12-23 18:37:40 +00:00
if ( groupno = = - 1 ) return TArray < GrpEntry > ( ) ;
auto & group = groups [ groupno ] ;
2019-11-01 18:25:42 +00:00
2020-04-23 19:18:40 +00:00
if ( GameStartupInfo . Name . IsNotEmpty ( ) ) I_SetWindowTitle ( GameStartupInfo . Name ) ;
2020-01-11 16:05:25 +00:00
else I_SetWindowTitle ( group . FileInfo . name ) ;
2019-11-01 18:25:42 +00:00
// Now filter out the data we actually need and delete the rest.
usedgroups . Push ( group ) ;
auto crc = group . FileInfo . dependencyCRC ;
2019-11-02 12:27:40 +00:00
if ( crc ! = 0 ) for ( auto & dep : groups )
2019-11-01 18:25:42 +00:00
{
if ( dep . FileInfo . CRC = = crc )
{
usedgroups . Insert ( 0 , dep ) ; // Order from least dependent to most dependent, which is the loading order of data.
2019-10-31 23:32:56 +00:00
}
}
2019-11-01 18:25:42 +00:00
groups . Reset ( ) ;
FString selectedScript ;
FString selectedDef ;
for ( auto & ugroup : usedgroups )
{
// For CONs the command line has priority, aside from that, the last one wins. For Blood this handles INIs - the rules are the same.
if ( ugroup . FileInfo . scriptname . IsNotEmpty ( ) ) selectedScript = ugroup . FileInfo . scriptname ;
if ( ugroup . FileInfo . defname . IsNotEmpty ( ) ) selectedDef = ugroup . FileInfo . defname ;
// CVAR has priority. This also overwrites the global variable each time. Init here is lazy so this is ok.
2019-12-23 18:37:40 +00:00
if ( ugroup . FileInfo . rtsname . IsNotEmpty ( ) & & * * rtsname = = 0 ) RTS_Init ( ugroup . FileInfo . rtsname ) ;
2019-11-01 18:25:42 +00:00
// For the game filter the last non-empty one wins.
if ( ugroup . FileInfo . gamefilter . IsNotEmpty ( ) ) LumpFilter = ugroup . FileInfo . gamefilter ;
g_gameType | = ugroup . FileInfo . flags ;
}
if ( userConfig . DefaultCon . IsEmpty ( ) ) userConfig . DefaultCon = selectedScript ;
if ( userConfig . DefaultDef . IsEmpty ( ) ) userConfig . DefaultDef = selectedDef ;
// This can only happen with a custom game that does not define any filter.
// In this case take the display name and strip all whitespace and invaliid path characters from it.
if ( LumpFilter . IsEmpty ( ) )
{
LumpFilter = usedgroups . Last ( ) . FileInfo . name ;
LumpFilter . StripChars ( " .:/ \\ <>? \" *| \t \r \n " ) ;
}
2019-10-29 18:53:46 +00:00
2019-10-31 23:32:56 +00:00
currentGame = LumpFilter ;
currentGame . Truncate ( currentGame . IndexOf ( " . " ) ) ;
2019-11-01 18:25:42 +00:00
CheckFrontend ( g_gameType ) ;
2019-12-23 18:37:40 +00:00
return usedgroups ;
}
//==========================================================================
//
//
//
//==========================================================================
int RunGame ( )
{
// Set up the console before anything else so that it can receive text.
C_InitConsole ( 1024 , 768 , true ) ;
// +logfile gets checked too late to catch the full startup log in the logfile so do some extra check for it here.
FString logfile = Args - > TakeValue ( " +logfile " ) ;
// As long as this engine is still in prerelease mode let's always write a log file.
2019-12-26 13:04:53 +00:00
if ( logfile . IsEmpty ( ) ) logfile . Format ( " %s " GAMENAMELOWERCASE " .log " , M_GetDocumentsPath ( ) . GetChars ( ) ) ;
2019-12-23 18:37:40 +00:00
if ( logfile . IsNotEmpty ( ) )
{
execLogfile ( logfile ) ;
}
I_DetectOS ( ) ;
userConfig . ProcessOptions ( ) ;
G_LoadConfig ( ) ;
2020-02-03 20:14:35 +00:00
ShutdownENet ( ) ;
2019-12-23 18:37:40 +00:00
auto usedgroups = SetupGame ( ) ;
2019-10-28 21:19:50 +00:00
2019-11-02 19:13:00 +00:00
InitFileSystem ( usedgroups ) ;
2019-12-23 18:37:40 +00:00
if ( usedgroups . Size ( ) = = 0 ) return 0 ;
2020-01-22 15:14:01 +00:00
// Handle CVARs with game specific defaults here.
2019-12-26 12:04:29 +00:00
if ( g_gameType & GAMEFLAG_BLOOD )
{
2020-01-22 12:53:26 +00:00
mus_redbook . SetGenericRepDefault ( false , CVAR_Bool ) ; // Blood should default to CD Audio off - all other games must default to on.
2020-01-22 14:21:07 +00:00
hud_size . SetGenericRepDefault ( 6 , CVAR_Int ) ;
2020-01-22 12:53:26 +00:00
hud_size_max = 7 ;
2019-12-26 12:04:29 +00:00
}
2020-01-22 14:21:07 +00:00
if ( g_gameType & GAMEFLAG_SW )
{
hud_size . SetGenericRepDefault ( 8 , CVAR_Int ) ;
hud_size_max = 9 ;
2020-02-12 21:46:18 +00:00
cl_weaponswitch . SetGenericRepDefault ( 1 , CVAR_Int ) ;
if ( cl_weaponswitch > 1 ) cl_weaponswitch = 1 ;
2020-01-22 14:21:07 +00:00
}
2020-01-22 15:14:01 +00:00
if ( g_gameType & GAMEFLAG_PSEXHUMED )
{
hud_size . SetGenericRepDefault ( 7 , CVAR_Int ) ;
hud_size_max = 8 ;
}
2019-12-26 12:04:29 +00:00
2019-12-23 18:37:40 +00:00
G_ReadConfig ( currentGame ) ;
V_InitFontColors ( ) ;
2020-04-11 22:11:50 +00:00
GStrings . LoadStrings ( language ) ;
2019-12-23 18:37:40 +00:00
2020-04-23 19:18:40 +00:00
CheckCPUID ( & CPU ) ;
CalculateCPUSpeed ( ) ;
auto ci = DumpCPUInfo ( & CPU ) ;
Printf ( " %s " , ci . GetChars ( ) ) ;
2019-12-23 18:37:40 +00:00
V_InitScreenSize ( ) ;
V_InitScreen ( ) ;
StartScreen = FStartupScreen : : CreateInstance ( 100 ) ;
2019-12-11 22:41:05 +00:00
TArray < FString > addArt ;
for ( auto & grp : usedgroups )
{
for ( auto & art : grp . FileInfo . loadart )
{
addArt . Push ( art ) ;
}
}
2019-12-17 19:08:59 +00:00
if ( userConfig . AddArt ) for ( auto & art : * userConfig . AddArt )
{
addArt . Push ( art ) ;
}
2019-12-11 22:41:05 +00:00
TileFiles . AddArt ( addArt ) ;
2019-11-02 19:13:00 +00:00
2019-12-23 18:37:40 +00:00
inputState . ClearAllInput ( ) ;
2019-11-04 22:01:50 +00:00
2019-11-02 19:13:00 +00:00
if ( ! GameConfig - > IsInitialized ( ) )
{
CONFIG_ReadCombatMacros ( ) ;
}
if ( userConfig . CommandName . IsNotEmpty ( ) )
{
playername = userConfig . CommandName ;
}
2020-07-15 10:34:42 +00:00
CheckUserMap ( ) ;
2020-04-05 20:51:53 +00:00
GPalette . Init ( MAXPALOOKUPS + 2 ) ; // one slot for each translation, plus a separate one for the base palettes and the internal one
2020-05-24 21:13:08 +00:00
TexMan . Init ( [ ] ( ) { } , [ ] ( BuildInfo & ) { } ) ;
2019-11-05 19:07:16 +00:00
V_InitFonts ( ) ;
2020-05-24 21:26:47 +00:00
TileFiles . Init ( ) ;
2019-12-26 13:04:53 +00:00
sfx_empty = fileSystem . FindFile ( " engine/dsempty.lmp " ) ; // this must be done outside the sound code because it's initialized late.
2020-04-12 06:07:48 +00:00
I_InitSound ( ) ;
Mus_InitMusic ( ) ;
timerSetCallback ( Mus_UpdateMusic ) ;
S_ParseSndInfo ( ) ;
2019-11-14 20:07:43 +00:00
InitStatistics ( ) ;
2020-06-14 17:20:04 +00:00
LoadScripts ( ) ;
2019-11-21 21:31:46 +00:00
M_Init ( ) ;
2019-11-24 23:02:00 +00:00
SetDefaultStrings ( ) ;
2019-12-28 11:59:19 +00:00
if ( Args - > CheckParm ( " -sounddebug " ) )
C_DoCommand ( " stat sounddebug " ) ;
2019-12-23 18:37:40 +00:00
if ( enginePreInit ( ) )
{
2019-12-24 18:59:14 +00:00
I_FatalError ( " app_main: There was a problem initializing the Build engine: %s \n " , engineerrstr ) ;
2019-12-23 18:37:40 +00:00
}
2020-01-23 18:14:10 +00:00
auto exec = C_ParseCmdLineParams ( nullptr ) ;
if ( exec ) exec - > ExecCommands ( ) ;
2020-04-11 22:04:02 +00:00
gamestate = GS_LEVEL ;
2019-10-31 23:32:56 +00:00
return gi - > app_main ( ) ;
2019-10-25 22:32:49 +00:00
}
2020-07-14 15:35:19 +00:00
//==========================================================================
//
//
//
//==========================================================================
void PolymostProcessVoxels ( void ) ;
void videoInit ( )
{
lookups . postLoadLookups ( ) ;
V_Init2 ( ) ;
videoSetGameMode ( vid_fullscreen , screen - > GetWidth ( ) , screen - > GetHeight ( ) , 32 , 1 ) ;
PolymostProcessVoxels ( ) ;
GLInterface . Init ( screen - > GetWidth ( ) ) ;
GLInterface . InitGLState ( 4 , 4 /*glmultisample*/ ) ;
screen - > SetTextureFilterMode ( ) ;
}
2019-12-24 18:59:14 +00:00
void G_FatalEngineError ( void )
{
2020-01-07 00:11:19 +00:00
I_FatalError ( " There was a problem initializing the engine: %s \n \n The application will now close. " , engineerrstr ) ;
2019-12-24 18:59:14 +00:00
}
2019-10-27 07:14:58 +00:00
//==========================================================================
//
//
//
//==========================================================================
CVAR ( String , combatmacro0 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro1 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro2 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro3 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro4 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro5 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro6 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro7 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro8 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro9 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
FStringCVar * const CombatMacros [ ] = { & combatmacro0 , & combatmacro1 , & combatmacro2 , & combatmacro3 , & combatmacro4 , & combatmacro5 , & combatmacro6 , & combatmacro7 , & combatmacro8 , & combatmacro9 } ;
void CONFIG_ReadCombatMacros ( )
{
FScanner sc ;
2019-11-24 09:03:19 +00:00
try
{
2019-12-26 13:46:14 +00:00
sc . Open ( " engine/combatmacros.txt " ) ;
2019-11-24 09:03:19 +00:00
for ( auto s : CombatMacros )
{
sc . MustGetToken ( TK_StringConst ) ;
if ( strlen ( * s ) = = 0 )
* s = sc . String ;
}
}
2020-04-11 21:50:43 +00:00
catch ( const CRecoverableError & )
2019-10-27 07:14:58 +00:00
{
2019-11-24 09:03:19 +00:00
// We do not want this to error out. Just ignore if it fails.
2019-10-27 07:14:58 +00:00
}
}
2019-10-26 19:50:49 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-10-27 12:40:24 +00:00
static FString CONFIG_GetMD4EntryName ( uint8_t const * const md4 )
{
return FStringf ( " MD4_%08x%08x%08x%08x " ,
B_BIG32 ( B_UNBUF32 ( & md4 [ 0 ] ) ) , B_BIG32 ( B_UNBUF32 ( & md4 [ 4 ] ) ) ,
B_BIG32 ( B_UNBUF32 ( & md4 [ 8 ] ) ) , B_BIG32 ( B_UNBUF32 ( & md4 [ 12 ] ) ) ) ;
}
int32_t CONFIG_GetMapBestTime ( char const * const mapname , uint8_t const * const mapmd4 )
{
auto m = CONFIG_GetMD4EntryName ( mapmd4 ) ;
if ( GameConfig - > SetSection ( " MapTimes " ) )
{
auto s = GameConfig - > GetValueForKey ( m ) ;
2019-12-02 01:07:32 +00:00
if ( s ) return ( int ) strtoull ( s , nullptr , 0 ) ;
2019-10-27 12:40:24 +00:00
}
return - 1 ;
}
int CONFIG_SetMapBestTime ( uint8_t const * const mapmd4 , int32_t tm )
{
FStringf t ( " %d " , tm ) ;
auto m = CONFIG_GetMD4EntryName ( mapmd4 ) ;
if ( GameConfig - > SetSection ( " MapTimes " ) )
{
GameConfig - > SetValueForKey ( m , t ) ;
}
return 0 ;
}
2019-10-26 21:45:55 +00:00
2019-12-15 12:34:00 +00:00
CCMD ( snd_reset )
2019-10-26 21:45:55 +00:00
{
2019-12-15 12:34:00 +00:00
Mus_Stop ( ) ;
2019-12-15 16:16:11 +00:00
if ( soundEngine ) soundEngine - > Reset ( ) ;
2019-12-26 12:04:29 +00:00
Mus_ResumeSaved ( ) ;
2019-10-26 21:45:55 +00:00
}
2020-05-28 23:22:45 +00:00
//==========================================================================
//
// S_PauseSound
//
// Stop music and sound effects, during game PAUSE.
//
//==========================================================================
void S_PauseSound ( bool notmusic , bool notsfx )
{
if ( ! notmusic )
{
S_PauseMusic ( ) ;
}
if ( ! notsfx )
{
soundEngine - > SetPaused ( true ) ;
GSnd - > SetSfxPaused ( true , 0 ) ;
}
}
//==========================================================================
//
// S_ResumeSound
//
// Resume music and sound effects, after game PAUSE.
//
//==========================================================================
void S_ResumeSound ( bool notsfx )
{
S_ResumeMusic ( ) ;
if ( ! notsfx )
{
soundEngine - > SetPaused ( false ) ;
GSnd - > SetSfxPaused ( false , 0 ) ;
}
}
2019-12-22 19:55:47 +00:00
//==========================================================================
//
// S_SetSoundPaused
//
// Called with state non-zero when the app is active, zero when it isn't.
//
//==========================================================================
void S_SetSoundPaused ( int state )
{
if ( state )
{
if ( paused = = 0 )
{
S_ResumeSound ( true ) ;
if ( GSnd ! = nullptr )
{
GSnd - > SetInactive ( SoundRenderer : : INACTIVE_Active ) ;
}
}
}
else
{
if ( paused = = 0 )
{
S_PauseSound ( false , true ) ;
if ( GSnd ! = nullptr )
{
2020-05-28 23:22:45 +00:00
GSnd - > SetInactive ( SoundRenderer : : INACTIVE_Complete ) ;
2019-12-22 19:55:47 +00:00
}
}
}
}
2020-01-12 13:54:43 +00:00
2020-02-02 09:27:47 +00:00
int CalcSmoothRatio ( const ClockTicks & totalclk , const ClockTicks & ototalclk , int realgameticspersec )
{
const double TICRATE = 120. ;
2020-07-14 09:52:38 +00:00
double elapsedTime = ( totalclk - ototalclk ) ;
2020-07-14 10:32:59 +00:00
double elapsedFrames = elapsedTime * ( 1. / TICRATE ) ;
double ratio = ( elapsedFrames * realgameticspersec ) ;
2020-02-02 09:27:47 +00:00
return clamp ( xs_RoundToInt ( ratio * 65536 ) , 0 , 65536 ) ;
}
2020-02-05 11:57:17 +00:00
FString G_GetDemoPath ( )
{
FString path = M_GetDemoPath ( ) ;
path < < LumpFilter < < ' / ' ;
CreatePath ( path ) ;
return path ;
}
2020-03-08 12:54:00 +00:00
CCMD ( printinterface )
{
Printf ( " Current interface is %s \n " , gi - > Name ( ) ) ;
}
2020-04-11 22:04:02 +00:00
CCMD ( togglemsg )
{
FBaseCVar * var , * prev ;
UCVarValue val ;
if ( argv . argc ( ) > 1 )
{
if ( ( var = FindCVar ( argv [ 1 ] , & prev ) ) )
{
var - > MarkUnsafe ( ) ;
val = var - > GetGenericRep ( CVAR_Bool ) ;
val . Bool = ! val . Bool ;
var - > SetGenericRep ( val , CVAR_Bool ) ;
const char * statestr = argv . argc ( ) < = 2 ? " * " : argv [ 2 ] ;
if ( * statestr = = ' * ' )
{
2020-06-30 20:53:15 +00:00
Printf ( PRINT_MEDIUM | PRINT_NOTIFY , " \" %s \" = \" %s \" \n " , var - > GetName ( ) , val . Bool ? " true " : " false " ) ;
2020-04-11 22:04:02 +00:00
}
else
{
int state = ( int ) strtoll ( argv [ 2 ] , nullptr , 0 ) ;
if ( state ! = 0 )
{
// Order of Duke's quote string varies, some have on first, some off, so use the sign of the parameter to decide.
// Positive means Off/On, negative means On/Off
int quote = state > 0 ? state + val . Bool : - ( state + val . Bool ) ;
auto text = quoteMgr . GetQuote ( quote ) ;
2020-06-30 20:53:15 +00:00
if ( text ) Printf ( PRINT_MEDIUM | PRINT_NOTIFY , " %s \n " , text ) ;
2020-04-11 22:04:02 +00:00
}
}
}
}
}
// Just a placeholder for now.
bool CheckCheatmode ( bool printmsg )
{
2020-07-04 13:51:02 +00:00
return gi - > CheatAllowed ( printmsg ) ;
2020-04-11 22:04:02 +00:00
}
2020-04-11 22:11:50 +00:00
2020-05-28 23:22:45 +00:00
void updatePauseStatus ( )
{
2020-07-26 07:20:53 +00:00
if ( M_Active ( ) | | System_WantGuiCapture ( ) )
2020-05-28 23:22:45 +00:00
{
paused = 1 ;
}
2020-07-26 07:20:53 +00:00
else if ( ! M_Active ( ) | | ! System_WantGuiCapture ( ) )
2020-05-28 23:22:45 +00:00
{
2020-07-26 07:20:53 +00:00
if ( ! pausedWithKey )
2020-05-28 23:22:45 +00:00
{
2020-07-26 07:20:53 +00:00
paused = 0 ;
2020-05-28 23:22:45 +00:00
}
2020-07-26 07:20:53 +00:00
if ( inputState . GetKeyStatus ( sc_Pause ) )
2020-05-28 23:22:45 +00:00
{
2020-07-26 07:20:53 +00:00
inputState . ClearKeyStatus ( sc_Pause ) ;
paused = pausedWithKey ? 0 : 2 ;
pausedWithKey = ! ! paused ;
2020-05-28 23:22:45 +00:00
}
}
2020-07-26 07:20:53 +00:00
paused ? S_PauseSound ( ! pausedWithKey , ! paused ) : S_ResumeSound ( paused ) ;
2020-05-28 23:22:45 +00:00
}
2020-06-12 21:06:11 +00:00
2020-06-14 16:57:55 +00:00
bool OkForLocalization ( FTextureID texnum , const char * substitute )
{
return false ;
}