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"
2020-10-04 16:31:48 +00:00
# include "razemenu.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"
2020-04-11 21:50:43 +00:00
# include "engineerrors.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"
2021-05-21 23:34:00 +00:00
# include "screenjob_.h"
2020-08-16 00:55:50 +00:00
# include "statusbar.h"
2020-08-25 16:51:56 +00:00
# include "uiinput.h"
2020-08-30 08:42:44 +00:00
# include "d_net.h"
2020-09-06 18:49:43 +00:00
# include "automap.h"
2020-10-04 16:31:48 +00:00
# include "v_draw.h"
2020-10-06 18:49:55 +00:00
# include "gi.h"
2021-04-22 17:59:45 +00:00
# include "vm.h"
2021-05-02 10:02:55 +00:00
# include "g_mapinfo.h"
2021-03-25 08:13:16 +00:00
# include "gamefuncs.h"
2021-04-05 11:55:36 +00:00
# include "hw_voxels.h"
2021-04-05 18:00:21 +00:00
# include "hw_palmanager.h"
2021-05-30 21:00:06 +00:00
# include "razefont.h"
2021-12-04 21:04:16 +00:00
# include "coreactor.h"
2022-04-25 15:26:17 +00:00
# include "wipe.h"
2022-06-06 09:45:02 +00:00
# include "findfile.h"
# include "version.h"
2022-08-03 13:50:27 +00:00
# include "hw_material.h"
2022-12-07 16:10:27 +00:00
# include "tiletexture.h"
# include "tilesetbuilder.h"
2023-10-31 03:44:58 +00:00
# include "gameinput.h"
2022-12-07 16:10:27 +00:00
# include "buildtiles.h"
2022-06-06 09:45:02 +00:00
void LoadHexFont ( const char * filename ) ;
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-10-31 11:46:53 +00:00
CVAR ( Bool , autoloadwidescreen , true , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
2020-04-11 22:11:50 +00:00
2021-05-26 22:32:57 +00:00
// Note: For the automap label there is a separate option "am_textfont".
CVARD ( Bool , hud_textfont , false , CVAR_ARCHIVE , " Use the regular text font as replacement for the tiny 3x5 font for HUD messages whenever possible " )
2020-10-04 22:46:24 +00:00
EXTERN_CVAR ( Bool , ui_generic )
2022-10-02 18:33:18 +00:00
EXTERN_CVAR ( String , language )
2023-01-07 12:18:52 +00:00
EXTERN_CVAR ( Bool , i_pauseinbackground )
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 ;
}
}
2022-03-18 08:17:46 +00:00
void I_UpdateWindowTitle ( ) ;
CUSTOM_CVAR ( Bool , i_discordrpc , false , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
{
I_UpdateWindowTitle ( ) ;
}
CUSTOM_CVAR ( Int , I_FriendlyWindowTitle , 1 , CVAR_GLOBALCONFIG | CVAR_ARCHIVE | CVAR_NOINITCALL )
{
I_UpdateWindowTitle ( ) ;
}
2020-03-29 13:22:07 +00:00
// The last remains of sdlayer.cpp
GameInterface * gi ;
int myconnectindex , numplayers ;
2021-05-03 17:01:00 +00:00
int connecthead , connectpoint2 [ MAXPLAYERS ] ;
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-08-30 10:02:32 +00:00
extern bool pauseext ;
2020-08-24 17:31:43 +00:00
2020-08-29 19:20:10 +00:00
cycle_t thinktime , actortime , gameupdatetime , drawtime ;
2020-06-23 19:12:15 +00:00
2020-09-03 21:10:28 +00:00
gameaction_t gameaction = ga_nothing ;
// gameaction state
MapRecord * g_nextmap ;
2023-01-05 09:57:14 +00:00
int g_nextskill = - 1 ;
2021-08-13 20:25:13 +00:00
int g_bossexit ;
2020-09-03 21:10:28 +00:00
2020-04-11 22:04:02 +00:00
2020-04-11 21:54:33 +00:00
FILE * hashfile ;
2019-10-27 23:24:09 +00:00
InputState inputState ;
2019-10-31 22:25:21 +00:00
int ShowStartupWindow ( TArray < GrpEntry > & ) ;
2023-08-20 00:25:12 +00:00
std : : vector < std : : string > 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-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-08-30 08:42:44 +00:00
void MainLoop ( ) ;
2020-10-25 11:47:07 +00:00
void SetConsoleNotifyBuffer ( ) ;
2020-11-10 15:22:02 +00:00
bool PreBindTexture ( FRenderState * state , FGameTexture * & tex , EUpscaleFlags & flags , int & scaleflags , int & clampmode , int & translation , int & overrideshader ) ;
2021-06-01 09:05:26 +00:00
void highTileSetup ( ) ;
2021-05-30 21:00:06 +00:00
void FontCharCreated ( FGameTexture * base , FGameTexture * untranslated ) ;
2021-04-05 11:55:36 +00:00
void LoadVoxelModels ( ) ;
2021-12-05 23:50:33 +00:00
void MarkMap ( ) ;
2022-01-10 21:22:37 +00:00
void BuildFogTable ( ) ;
2022-01-20 22:51:43 +00:00
void ParseGLDefs ( ) ;
2022-03-18 08:17:46 +00:00
void I_UpdateDiscordPresence ( bool SendPresence , const char * curstatus , const char * appid , const char * steamappid ) ;
2022-10-02 18:33:18 +00:00
bool G_Responder ( event_t * ev ) ;
void HudScaleChanged ( ) ;
bool M_SetSpecialMenu ( FName & menu , int param ) ;
void OnMenuOpen ( bool makeSound ) ;
2023-01-14 13:09:25 +00:00
void DestroyAltHUD ( ) ;
2023-10-02 19:03:59 +00:00
void MarkPlayers ( ) ;
2020-05-31 08:53:11 +00:00
2021-05-16 08:35:33 +00:00
DStatusBarCore * StatusBar ;
2020-10-28 18:27:12 +00:00
2019-10-31 23:32:56 +00:00
FString currentGame ;
2019-10-28 22:46:15 +00:00
FString LumpFilter ;
2019-10-26 11:16:32 +00:00
2022-10-02 18:33:18 +00:00
EXTERN_CVAR ( Bool , queryiwad ) ;
EXTERN_CVAR ( String , defaultiwad ) ;
2019-12-24 14:28:00 +00:00
CVAR ( Bool , disableautoload , 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 ;
2023-03-18 08:54:05 +00:00
static bool sendPause ;
2020-05-28 23:22:45 +00:00
bool pausedWithKey ;
2021-02-18 10:46:36 +00:00
int PlayClock ;
2022-04-25 15:26:17 +00:00
extern int nextwipe ;
2021-02-18 10:46:36 +00:00
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 ) ;
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 ;
}
2020-09-28 20:36:43 +00:00
bool System_DispatchEvent ( event_t * ev )
{
if ( ev - > type = = EV_Mouse & & ! System_WantGuiCapture ( ) )
{
2023-04-03 08:40:34 +00:00
gameInput . MouseAddToPos ( ev - > x , ev - > y ) ;
2020-09-28 20:36:43 +00:00
return true ;
}
inputState . AddEvent ( ev ) ;
return false ;
}
2020-04-23 19:18:40 +00:00
bool System_WantLeftButton ( )
{
2020-08-10 22:46:27 +00:00
return false ; // (gamestate == GS_MENUSCREEN || gamestate == GS_TITLELEVEL);
2020-04-23 19:18:40 +00:00
}
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 ( )
{
2022-08-04 21:47:01 +00:00
int viewbottom = viewport3d . Bottom ( ) ;
int viewheight = viewport3d . Height ( ) ;
int viewright = viewport3d . Right ( ) ;
int viewwidth = viewport3d . Width ( ) ;
2021-04-05 08:34:03 +00:00
int renderheight ;
2021-12-30 09:30:21 +00:00
2021-04-05 08:34:03 +00:00
if ( viewheight = = screen - > GetHeight ( ) ) renderheight = viewheight ;
else renderheight = ( viewwidth * screen - > GetHeight ( ) / screen - > GetWidth ( ) ) & ~ 7 ;
2020-04-28 20:55:37 +00:00
IntRect mSceneViewport ;
2022-08-04 21:47:01 +00:00
mSceneViewport . left = viewport3d . Left ( ) ;
mSceneViewport . top = screen - > GetHeight ( ) - ( renderheight + viewport3d . Top ( ) - ( ( renderheight - viewheight ) / 2 ) ) ;
2021-04-05 08:34:03 +00:00
mSceneViewport . width = viewwidth ;
mSceneViewport . height = renderheight ;
2020-04-28 20:55:37 +00:00
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 ;
2021-04-22 17:59:45 +00:00
DEFINE_GLOBAL ( userConfig )
DEFINE_FIELD_X ( UserConfigStruct , UserConfig , nomonsters )
DEFINE_FIELD_X ( UserConfigStruct , UserConfig , nosound )
DEFINE_FIELD_X ( UserConfigStruct , UserConfig , nologo )
2019-10-28 21:19:50 +00:00
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
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 " ;
2021-07-20 09:06:45 +00:00
const char * argv [ ] = { " CPART07.AR_ " , " CPART15.AR_ " } ;
2019-12-11 22:41:05 +00:00
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 ;
}
}
2023-05-14 06:41:40 +00:00
FixPathSeperator ( gamegrp ) ;
2019-10-28 21:19:50 +00:00
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 " ) ;
2021-04-07 19:46:44 +00:00
UserDef = Args - > CheckValue ( " -def " ) ;
2019-10-28 21:19:50 +00:00
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 " ) ;
2020-08-22 18:14:00 +00:00
static const char * nomos [ ] = { " -nomonsters " , " -nodudes " , " -nocreatures " , nullptr } ;
2019-10-28 21:19:50 +00:00
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 ;
2023-10-08 07:15:32 +00:00
if ( FindMapByName ( userConfig . CommandMap . GetChars ( ) ) )
2021-06-26 08:11:50 +00:00
{
return ; // we already got a record for this map so no need for further checks.
}
2020-07-15 10:34:42 +00:00
FString startupMap = userConfig . CommandMap ;
DefaultExtension ( startupMap , " .map " ) ;
startupMap . Substitute ( " \\ " , " / " ) ;
NormalizeFileName ( startupMap ) ;
2023-10-08 07:15:32 +00:00
if ( ! fileSystem . FileExists ( startupMap . GetChars ( ) ) )
2020-07-15 10:34:42 +00:00
{
2021-06-26 08:11:50 +00:00
Printf ( PRINT_HIGH , " Level \" %s \" not found. \n " , startupMap . GetChars ( ) ) ;
2020-07-15 10:34:42 +00:00
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
}
2020-10-10 08:11:22 +00:00
namespace Exhumed
2019-12-12 23:19:34 +00:00
{
: : GameInterface * CreateInterface ( ) ;
}
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 )
{
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 )
{
2020-10-10 08:11:22 +00:00
gi = Exhumed : : CreateInterface ( ) ;
2019-12-12 23:19:34 +00:00
}
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
}
2021-05-22 11:02:34 +00:00
static void System_ToggleFullConsole ( )
{
gameaction = ga_fullconsole ;
}
static void System_StartCutscene ( bool blockui )
{
gameaction = blockui ? ga_intro : ga_intermission ;
}
2022-04-25 15:26:17 +00:00
static void System_SetTransition ( int type )
{
nextwipe = type ;
}
2022-12-18 11:56:34 +00:00
bool WantEscape ( )
{
return gi - > WantEscape ( ) ;
}
2023-02-11 09:38:29 +00:00
EXTERN_CVAR ( Int , duke_menufont )
void LanguageChanged ( const char * lang )
{
duke_menufont - > Callback ( ) ;
}
2022-10-02 18:33:18 +00:00
2019-12-14 16:15:17 +00:00
void I_StartupJoysticks ( ) ;
void I_ShutdownInput ( ) ;
int RunGame ( ) ;
2020-10-08 16:02:25 +00:00
void System_MenuClosed ( ) ;
2020-10-10 16:29:15 +00:00
void System_MenuDim ( ) ;
2019-10-31 22:25:21 +00:00
2019-11-05 19:07:16 +00:00
int GameMain ( )
{
int r ;
2022-10-09 12:50:45 +00:00
I_InitTime ( ) ;
2022-10-21 22:12:17 +00:00
C_InitCVars ( 0 ) ;
2020-10-25 11:47:07 +00:00
SetConsoleNotifyBuffer ( ) ;
2020-10-03 15:04:45 +00:00
sysCallbacks =
2020-04-23 19:18:40 +00:00
{
2022-10-02 18:33:18 +00:00
G_Responder ,
2020-04-23 19:18:40 +00:00
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-09-28 20:36:43 +00:00
nullptr ,
2020-10-10 16:29:15 +00:00
System_MenuDim ,
2020-09-28 20:36:43 +00:00
nullptr ,
System_DispatchEvent ,
2020-10-04 19:15:51 +00:00
validFilter ,
StrTable_GetGender ,
2020-10-08 16:02:25 +00:00
System_MenuClosed ,
2020-11-10 15:22:02 +00:00
nullptr ,
nullptr ,
2020-11-10 20:34:49 +00:00
PreBindTexture ,
2021-05-22 11:02:34 +00:00
FontCharCreated ,
System_ToggleFullConsole ,
System_StartCutscene ,
2022-04-25 15:26:17 +00:00
System_SetTransition ,
2022-10-02 18:33:18 +00:00
CheckCheatmode ,
HudScaleChanged ,
M_SetSpecialMenu ,
OnMenuOpen ,
2023-02-11 09:38:29 +00:00
LanguageChanged ,
2022-10-02 18:33:18 +00:00
nullptr ,
2022-11-19 17:26:17 +00:00
[ ] ( ) - > FConfigFile * { return GameConfig ; } ,
2022-12-18 11:56:34 +00:00
WantEscape ,
2020-04-23 19:18:40 +00:00
} ;
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
}
2021-05-05 21:33:24 +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 ( ) ;
}
2021-05-05 21:33:24 +00:00
catch ( const std : : exception & err )
2019-12-24 15:30:33 +00:00
{
// shut down critical systems before showing a message box.
I_ShowFatalError ( err . what ( ) ) ;
r = - 1 ;
}
2021-04-30 14:21:37 +00:00
//DeleteScreenJob();
2023-10-02 06:00:50 +00:00
if ( gi )
{
gi - > FreeLevelData ( ) ;
2023-10-02 17:03:27 +00:00
for ( int i = 0 ; i < MAXPLAYERS ; i + + )
{
2023-10-02 19:03:59 +00:00
PlayerArray [ i ] - > Destroy ( ) ;
2023-10-02 17:03:27 +00:00
PlayerArray [ i ] = nullptr ;
}
2023-10-02 06:00:50 +00:00
}
2023-01-14 13:09:25 +00:00
DestroyAltHUD ( ) ;
2020-10-10 13:16:28 +00:00
DeinitMenus ( ) ;
2020-10-28 18:27:12 +00:00
if ( StatusBar ) StatusBar - > Destroy ( ) ;
StatusBar = nullptr ;
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 ( ) ;
2021-04-05 11:55:36 +00:00
voxClear ( ) ;
2021-04-05 18:00:21 +00:00
ClearPalManager ( ) ;
2020-05-24 21:26:47 +00:00
TexMan . DeleteAll ( ) ;
2019-12-24 17:53:29 +00:00
I_ShutdownGraphics ( ) ;
2019-12-24 19:06:55 +00:00
if ( gi )
{
delete gi ;
gi = nullptr ;
}
2019-12-24 15:30:33 +00:00
DeleteStartupScreen ( ) ;
2020-06-14 17:20:04 +00:00
PClass : : StaticShutdown ( ) ;
2022-10-21 22:12:17 +00:00
C_UninitCVars ( ) ;
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 ( )
{
2019-12-08 23:33:14 +00:00
// Blood hard codes its skill names, so we have to define them manually.
2021-01-02 03:46:58 +00:00
if ( isBlood ( ) )
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
}
2022-01-24 00:00:05 +00:00
// Exhumed has no skills, but we still need a menu with one entry.
else if ( isExhumed ( ) )
{
gSkillNames [ 0 ] = " Default " ;
}
2021-12-30 09:30:21 +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
2023-02-19 12:01:20 +00:00
quoteMgr . InitializeQuote ( 23 , " $MSGON " ) ;
quoteMgr . InitializeQuote ( 24 , " $MSGOFF " ) ;
2019-12-09 01:01:30 +00:00
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 ( ) ;
2021-06-26 10:10:46 +00:00
I_Error ( " Unable to find any game data. Please verify your settings. "
# ifdef WIN32
) ;
# else
" \n Install game data files in subfolders of '%s' \n \n " , M_GetAppDataPath ( false ) . GetChars ( ) ) ;
# endif
2019-11-01 18:25:42 +00:00
}
decltype ( groups ) usedgroups ;
int groupno = - 1 ;
2021-04-13 15:05:53 +00:00
auto game = GetGameFronUserFiles ( ) ;
2020-01-11 16:05:25 +00:00
if ( userConfig . gamegrp . IsEmpty ( ) )
{
2021-04-13 15:05:53 +00:00
for ( auto & str : game )
{
2021-05-02 20:56:53 +00:00
int g = 0 ;
2021-04-13 15:05:53 +00:00
for ( auto & grp : groups )
{
2023-08-20 00:25:12 +00:00
if ( grp . FileInfo . gameid . CompareNoCase ( str . c_str ( ) ) = = 0 )
2021-04-13 15:05:53 +00:00
{
userConfig . gamegrp = grp . FileName ;
2021-05-02 20:56:53 +00:00
groupno = g ;
2021-04-13 15:05:53 +00:00
goto foundit ;
}
2021-05-02 20:56:53 +00:00
g + + ;
2021-04-13 15:05:53 +00:00
}
}
2020-01-11 16:05:25 +00:00
}
2021-04-13 15:05:53 +00:00
foundit :
2020-01-11 16:05:25 +00:00
2022-12-09 23:48:34 +00:00
// If the user has specified a script, let's see if we know it.
//
if ( groupno = = - 1 & & userConfig . DefaultCon . Len ( ) )
{
FString DefaultConlower = userConfig . DefaultCon . MakeLower ( ) ;
int g = 0 ;
for ( auto & grp : groups )
{
if ( grp . FileInfo . scriptname . MakeLower ( ) = = DefaultConlower )
{
groupno = g ;
break ;
}
g + + ;
}
}
2021-04-13 15:05:53 +00:00
// If the user has specified a file name, let's see if we know it.
//
2021-05-02 20:56:53 +00:00
if ( groupno = = - 1 & & userConfig . gamegrp . Len ( ) )
2019-10-31 23:32:56 +00:00
{
2021-05-02 20:56:53 +00:00
FString gamegrplower = userConfig . gamegrp . MakeLower ( ) ;
if ( gamegrplower [ 1 ] ! = ' : ' | | gamegrplower [ 2 ] ! = ' / ' ) gamegrplower . Insert ( 0 , " / " ) ;
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 ( ) ;
2021-05-02 20:56:53 +00:00
FixPathSeperator ( grplower ) ;
2021-08-14 08:04:45 +00:00
auto pos = grplower . LastIndexOf ( gamegrplower ) ;
2021-10-08 17:06:41 +00:00
if ( pos > = 0 & & pos = = ptrdiff_t ( 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 )
{
2022-12-11 06:02:36 +00:00
FString & basename = groups [ i ] . FileInfo . name ;
2023-10-08 07:15:32 +00:00
if ( stricmp ( basename . GetChars ( ) , defaultiwad ) = = 0 )
2019-12-24 14:28:00 +00:00
{
pick = i ;
break ;
}
}
}
if ( groups . Size ( ) > 1 )
{
TArray < WadStuff > wads ;
for ( auto & found : groups )
{
WadStuff stuff ;
stuff . Name = found . FileInfo . name ;
2023-10-08 07:15:32 +00:00
stuff . Path = ExtractFileBase ( found . FileName . GetChars ( ) ) ;
2019-12-24 14:28:00 +00:00
wads . Push ( stuff ) ;
}
2022-10-02 18:33:18 +00:00
int flags = 0 ;
if ( disableautoload ) flags | = 1 ;
if ( autoloadlights ) flags | = 2 ;
if ( autoloadbrightmaps ) flags | = 4 ;
if ( autoloadwidescreen ) flags | = 8 ;
pick = I_PickIWad ( & wads [ 0 ] , ( int ) wads . Size ( ) , queryiwad , pick , flags ) ;
2019-12-24 14:28:00 +00:00
if ( pick > = 0 )
{
2022-10-02 18:33:18 +00:00
disableautoload = ! ! ( flags & 1 ) ;
autoloadlights = ! ! ( flags & 2 ) ;
autoloadbrightmaps = ! ! ( flags & 4 ) ;
autoloadwidescreen = ! ! ( flags & 8 ) ;
2019-12-24 14:28:00 +00:00
// The newly selected IWAD becomes the new default
2023-10-08 07:15:32 +00:00
defaultiwad = groups [ pick ] . FileInfo . name . GetChars ( ) ;
2019-12-24 14:28:00 +00:00
}
groupno = pick ;
}
}
2021-12-29 18:41:41 +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
// 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.
2023-10-08 07:15:32 +00:00
if ( ugroup . FileInfo . rtsname . IsNotEmpty ( ) & & * * rtsname = = 0 ) RTS_Init ( ugroup . FileInfo . rtsname . GetChars ( ) ) ;
2019-12-23 18:37:40 +00:00
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 ;
}
2021-04-13 15:08:04 +00:00
if ( userConfig . DefaultCon . IsEmpty ( ) ) userConfig . DefaultCon = GameStartupInfo . con . IsNotEmpty ( ) ? GameStartupInfo . con : selectedScript ;
2019-11-01 18:25:42 +00:00
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 " ) ;
}
2022-11-06 10:46:26 +00:00
SavegameFolder = LumpFilter ;
2019-10-31 23:32:56 +00:00
currentGame = LumpFilter ;
currentGame . Truncate ( currentGame . IndexOf ( " . " ) ) ;
2020-10-28 18:27:12 +00:00
PClass : : StaticInit ( ) ;
2019-11-01 18:25:42 +00:00
CheckFrontend ( g_gameType ) ;
2020-10-06 18:49:55 +00:00
gameinfo . gametype = g_gameType ;
2019-12-23 18:37:40 +00:00
return usedgroups ;
}
2020-09-05 19:33:04 +00:00
//==========================================================================
//
//
//
//==========================================================================
void InitLanguages ( )
{
GStrings . LoadStrings ( language ) ;
}
2020-10-28 18:27:12 +00:00
void CreateStatusBar ( )
{
2021-05-16 08:43:47 +00:00
auto stbarclass = PClass : : FindClass ( globalCutscenes . StatusBarClass ) ;
2020-10-28 18:27:12 +00:00
if ( ! stbarclass )
{
I_FatalError ( " No status bar defined " ) ;
}
2021-05-16 08:35:33 +00:00
StatusBar = static_cast < DStatusBarCore * > ( stbarclass - > CreateNew ( ) ) ;
2021-05-12 19:46:49 +00:00
StatusBar - > SetSize ( 0 , 320 , 200 ) ;
2021-05-13 17:48:27 +00:00
InitStatusBar ( ) ;
2021-05-16 08:43:47 +00:00
GC : : AddMarkerFunc ( [ ] ( ) { GC : : Mark ( StatusBar ) ; } ) ;
2020-10-28 18:27:12 +00:00
}
2021-05-05 21:33:24 +00:00
void GetGames ( )
{
auto getgames = Args - > CheckValue ( " -getgames " ) ;
if ( getgames )
{
try
{
auto groups = GrpScan ( ) ;
FSerializer arc ;
if ( arc . OpenWriter ( ) )
{
if ( arc . BeginArray ( " games " ) )
{
for ( auto & entry : groups )
{
if ( arc . BeginObject ( nullptr ) )
{
arc ( " filename " , entry . FileName )
( " description " , entry . FileInfo . name )
( " defname " , entry . FileInfo . defname )
( " scriptname " , entry . FileInfo . scriptname )
( " gamefilter " , entry . FileInfo . gamefilter )
( " gameid " , entry . FileInfo . gameid )
( " fgcolor " , entry . FileInfo . FgColor )
( " bkcolor " , entry . FileInfo . BgColor )
( " addon " , entry . FileInfo . isAddon )
. EndObject ( ) ;
}
}
arc . EndArray ( ) ;
}
unsigned int len ;
auto p = arc . GetOutput ( & len ) ;
FILE * f = fopen ( getgames , " wb " ) ;
if ( f )
{
fwrite ( p , 1 , len , f ) ;
fclose ( f ) ;
}
}
}
catch ( . . . )
{
// Ignore all errors
}
throw CExitEvent ( 0 ) ;
}
}
2019-12-23 18:37:40 +00:00
//==========================================================================
//
//
//
//==========================================================================
2022-12-07 16:10:27 +00:00
static void InitTextures ( TArray < GrpEntry > & usedgroups )
2021-06-01 09:05:26 +00:00
{
2022-12-07 16:10:27 +00:00
voxInit ( ) ;
2022-06-06 09:45:02 +00:00
2021-07-05 20:39:24 +00:00
TexMan . usefullnames = true ;
2022-06-06 09:45:02 +00:00
TexMan . Init ( ) ;
TexMan . AddTextures ( [ ] ( ) { } , [ ] ( BuildInfo & ) { } ) ;
StartWindow - > Progress ( ) ;
2021-06-01 09:05:26 +00:00
2022-12-07 16:10:27 +00:00
TArray < FString > addArt ;
for ( auto & grp : usedgroups )
{
for ( auto & art : grp . FileInfo . loadart )
{
addArt . Push ( art ) ;
}
}
if ( userConfig . AddArt ) for ( auto & art : * userConfig . AddArt )
{
addArt . Push ( art ) ;
}
InitArtFiles ( addArt ) ;
ConstructTileset ( ) ;
2021-06-01 09:05:26 +00:00
InitFont ( ) ; // InitFonts may only be called once all texture data has been initialized.
lookups . postLoadTables ( ) ;
highTileSetup ( ) ;
lookups . postLoadLookups ( ) ;
SetupFontSubstitution ( ) ;
V_LoadTranslations ( ) ; // loading the translations must be delayed until the palettes have been fully set up.
2021-07-24 07:10:21 +00:00
UpdateUpscaleMask ( ) ;
2021-06-01 09:05:26 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
2021-08-14 08:26:04 +00:00
static uint8_t palindexmap [ 256 ] ;
2019-12-23 18:37:40 +00:00
int RunGame ( )
{
2021-04-10 17:14:30 +00:00
GameStartupInfo . FgColor = 0xffffff ;
2022-07-24 10:11:00 +00:00
G_LoadConfig ( ) ;
2022-06-06 09:45:02 +00:00
auto wad = BaseFileSearch ( ENGINERES_FILE , NULL , true , GameConfig ) ;
if ( wad = = NULL )
{
I_FatalError ( " Cannot find " ENGINERES_FILE ) ;
}
LoadHexFont ( wad ) ; // load hex font early so we have it during startup.
2019-12-23 18:37:40 +00:00
// 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 ( ) )
{
2023-10-08 07:15:32 +00:00
execLogfile ( logfile . GetChars ( ) ) ;
2019-12-23 18:37:40 +00:00
}
I_DetectOS ( ) ;
userConfig . ProcessOptions ( ) ;
2021-05-05 21:33:24 +00:00
GetGames ( ) ;
2019-12-23 18:37:40 +00:00
auto usedgroups = SetupGame ( ) ;
2021-04-12 17:39:42 +00:00
bool colorset = false ;
for ( int i = usedgroups . Size ( ) - 1 ; i > = 0 ; i - - )
2021-04-10 17:14:30 +00:00
{
2021-04-12 17:39:42 +00:00
auto & grp = usedgroups [ i ] ;
2021-04-10 17:14:30 +00:00
if ( grp . FileInfo . name . IsNotEmpty ( ) )
{
if ( GameStartupInfo . Name . IsEmpty ( ) ) GameStartupInfo . Name = grp . FileInfo . name ;
2021-04-12 17:39:42 +00:00
if ( ! colorset & & grp . FileInfo . FgColor ! = grp . FileInfo . BgColor & & ( GameStartupInfo . FgColor ! = 0 | | GameStartupInfo . BkColor ! = 0 ) )
2021-04-10 17:14:30 +00:00
{
GameStartupInfo . FgColor = grp . FileInfo . FgColor ;
GameStartupInfo . BkColor = grp . FileInfo . BgColor ;
2021-04-12 17:39:42 +00:00
colorset = true ;
2021-04-10 17:14:30 +00:00
}
}
2021-07-19 02:24:47 +00:00
if ( grp . FileInfo . exclepisodes . Size ( ) )
2021-07-16 12:14:56 +00:00
{
2021-07-19 02:24:47 +00:00
for ( auto & episode : grp . FileInfo . exclepisodes )
2021-07-16 12:14:56 +00:00
{
2021-07-19 02:24:47 +00:00
gi - > AddExcludedEpisode ( episode ) ;
2021-07-16 12:14:56 +00:00
}
}
2021-04-10 17:14:30 +00:00
}
I_SetIWADInfo ( ) ;
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.
2021-01-02 03:46:58 +00:00
if ( isBlood ( ) )
2019-12-26 12:04:29 +00:00
{
2022-10-21 22:12:17 +00:00
mus_redbook - > SetGenericRepDefault ( false , CVAR_Bool ) ; // Blood should default to CD Audio off - all other games must default to on.
am_showlabel - > SetGenericRepDefault ( true , CVAR_Bool ) ;
2019-12-26 12:04:29 +00:00
}
2021-07-11 02:39:54 +00:00
if ( isSWALL ( ) )
2020-01-22 14:21:07 +00:00
{
2022-10-25 00:47:00 +00:00
hud_showmapname - > SetGenericRepDefault ( false , CVAR_Bool ) ; // SW never had this feature, make it optional.
2022-10-21 22:12:17 +00:00
cl_weaponswitch - > SetGenericRepDefault ( 1 , CVAR_Int ) ;
2020-02-12 21:46:18 +00:00
if ( cl_weaponswitch > 1 ) cl_weaponswitch = 1 ;
2020-01-22 14:21:07 +00:00
}
2023-05-04 10:34:58 +00:00
if ( isExhumed ( ) )
{
cl_viewbob - > SetGenericRepDefault ( 0 , CVAR_Int ) ; // Exhumed never had this feature, make it optional.
}
2020-08-23 22:25:42 +00:00
if ( g_gameType & ( GAMEFLAG_BLOOD | GAMEFLAG_RR ) )
{
2022-10-21 22:12:17 +00:00
am_nameontop - > SetGenericRepDefault ( true , CVAR_Bool ) ; // Blood and RR show the map name on the top of the screen by default.
2020-08-23 22:25:42 +00:00
}
2019-12-26 12:04:29 +00:00
2023-10-08 07:15:32 +00:00
G_ReadConfig ( currentGame . GetChars ( ) ) ;
2019-12-23 18:37:40 +00:00
V_InitFontColors ( ) ;
2020-09-05 19:33:04 +00:00
InitLanguages ( ) ;
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 ( ) ;
2022-06-06 09:45:02 +00:00
StartWindow = FStartupScreen : : CreateInstance ( 8 , true ) ;
StartWindow - > Progress ( ) ;
2019-12-23 18:37:40 +00:00
2019-11-02 19:13:00 +00:00
if ( ! GameConfig - > IsInitialized ( ) )
{
CONFIG_ReadCombatMacros ( ) ;
}
if ( userConfig . CommandName . IsNotEmpty ( ) )
{
2023-10-08 07:15:32 +00:00
playername = userConfig . CommandName . GetChars ( ) ;
2019-11-02 19:13:00 +00:00
}
2020-08-23 15:47:05 +00:00
GameTicRate = 30 ;
2020-07-15 10:34:42 +00:00
CheckUserMap ( ) ;
2021-08-14 08:26:04 +00:00
palindexmap [ 0 ] = 255 ;
for ( int i = 1 ; i < = 255 ; i + + ) palindexmap [ i ] = i ;
GPalette . Init ( MAXPALOOKUPS + 2 , palindexmap ) ; // one slot for each translation, plus a separate one for the base palettes and the internal one
2021-05-30 22:04:04 +00:00
gi - > loadPalette ( ) ;
2022-01-10 21:22:37 +00:00
BuildFogTable ( ) ;
2022-06-06 09:45:02 +00:00
StartWindow - > Progress ( ) ;
2022-12-07 16:10:27 +00:00
InitTextures ( usedgroups ) ;
2021-05-30 22:04:04 +00:00
2022-06-06 09:45:02 +00:00
StartWindow - > Progress ( ) ;
2020-04-12 06:07:48 +00:00
I_InitSound ( ) ;
2022-11-24 20:27:08 +00:00
gi - > StartSoundEngine ( ) ;
2022-06-06 09:45:02 +00:00
StartWindow - > Progress ( ) ;
2020-04-12 06:07:48 +00:00
Mus_InitMusic ( ) ;
S_ParseSndInfo ( ) ;
2020-10-09 20:33:02 +00:00
S_ParseReverbDef ( ) ;
2019-11-14 20:07:43 +00:00
InitStatistics ( ) ;
2020-06-14 17:20:04 +00:00
LoadScripts ( ) ;
2022-06-06 09:45:02 +00:00
StartWindow - > Progress ( ) ;
2019-11-24 23:02:00 +00:00
SetDefaultStrings ( ) ;
2021-04-26 21:10:38 +00:00
Job_Init ( ) ;
2021-05-21 23:34:00 +00:00
Local_Job_Init ( ) ;
2019-12-28 11:59:19 +00:00
if ( Args - > CheckParm ( " -sounddebug " ) )
C_DoCommand ( " stat sounddebug " ) ;
2019-12-23 18:37:40 +00:00
2020-08-26 22:25:59 +00:00
SetupGameButtons ( ) ;
2020-10-06 23:12:57 +00:00
gameinfo . mBackButton = " engine/graphics/m_back.png " ;
2022-06-06 09:45:02 +00:00
StartWindow - > Progress ( ) ;
2021-04-05 11:55:36 +00:00
2021-12-05 23:50:33 +00:00
GC : : AddMarkerFunc ( MarkMap ) ;
2023-10-02 19:03:59 +00:00
GC : : AddMarkerFunc ( MarkPlayers ) ;
2020-08-23 15:47:05 +00:00
gi - > app_init ( ) ;
2022-06-06 09:45:02 +00:00
StartWindow - > Progress ( ) ;
2021-05-02 10:02:55 +00:00
G_ParseMapInfo ( ) ;
2022-01-20 22:51:43 +00:00
ParseGLDefs ( ) ;
2021-09-05 18:35:10 +00:00
ReplaceMusics ( true ) ;
2020-10-28 18:27:12 +00:00
CreateStatusBar ( ) ;
2020-10-04 20:21:11 +00:00
SetDefaultMenuColors ( ) ;
2020-09-08 20:37:21 +00:00
M_Init ( ) ;
2020-10-06 23:31:41 +00:00
BuildGameMenus ( ) ;
2022-06-06 09:45:02 +00:00
StartWindow - > Progress ( ) ;
2020-09-13 18:15:46 +00:00
if ( ! ( paletteloaded & PALETTE_MAIN ) )
I_FatalError ( " No palette found. " ) ;
2021-04-05 18:00:21 +00:00
FMaterial : : SetLayerCallback ( setpalettelayer ) ;
2022-03-18 08:17:46 +00:00
I_UpdateWindowTitle ( ) ;
2021-05-16 18:01:14 +00:00
DeleteStartupScreen ( ) ;
2020-08-30 08:42:44 +00:00
2021-04-05 11:55:36 +00:00
V_Init2 ( ) ;
2022-06-06 09:45:02 +00:00
while ( ! screen - > CompileNextShader ( ) )
{
// here we can do some visual updates later
}
2021-04-05 13:41:57 +00:00
twod - > Begin ( screen - > GetWidth ( ) , screen - > GetHeight ( ) ) ;
twod - > End ( ) ;
UpdateJoystickMenu ( NULL ) ;
UpdateVRModes ( ) ;
2021-04-05 11:55:36 +00:00
setVideoMode ( ) ;
LoadVoxelModels ( ) ;
screen - > BeginFrame ( ) ;
screen - > SetTextureFilterMode ( ) ;
setViewport ( hud_size ) ;
2020-08-30 08:42:44 +00:00
2020-09-02 20:55:57 +00:00
D_CheckNetGame ( ) ;
2020-10-24 19:02:00 +00:00
UpdateGenericUI ( ui_generic ) ;
2022-01-16 23:51:40 +00:00
PClassActor : : StaticInit ( ) ;
2022-12-21 21:06:34 +00:00
gi - > FinalizeSetup ( ) ;
2020-09-02 20:55:57 +00:00
MainLoop ( ) ;
2022-01-16 23:51:40 +00:00
return 0 ;
2019-10-25 22:32:49 +00:00
}
2020-08-23 15:47:05 +00:00
//---------------------------------------------------------------------------
//
2022-01-16 23:51:40 +00:00
//
2020-08-23 15:47:05 +00:00
//
//---------------------------------------------------------------------------
2020-08-30 17:59:46 +00:00
void updatePauseStatus ( )
2020-08-26 14:47:30 +00:00
{
2020-08-27 20:38:52 +00:00
// This must go through the network in multiplayer games.
2023-01-27 08:02:13 +00:00
if ( M_Active ( ) | | System_WantGuiCapture ( ) )
{
paused = 1 ;
}
else if ( ! AppActive )
2020-08-26 14:47:30 +00:00
{
2023-01-07 12:18:52 +00:00
if ( i_pauseinbackground )
paused = 1 ;
else if ( ! pausedWithKey )
paused = 0 ;
2020-08-26 14:47:30 +00:00
}
else if ( ! M_Active ( ) | | ! System_WantGuiCapture ( ) )
{
if ( ! pausedWithKey )
{
paused = 0 ;
}
if ( sendPause )
{
sendPause = false ;
paused = pausedWithKey ? 0 : 2 ;
pausedWithKey = ! ! paused ;
}
}
2021-04-16 20:14:11 +00:00
if ( paused )
S_PauseSound ( ! pausedWithKey , ! paused ) ;
else
S_ResumeSound ( paused ) ;
2020-08-26 14:47:30 +00:00
}
2020-07-14 15:35:19 +00:00
//==========================================================================
//
//
//
//==========================================================================
2021-04-05 11:55:36 +00:00
void LoadVoxelModels ( void ) ;
2020-07-14 15:35:19 +00:00
2021-02-25 11:16:21 +00:00
void setVideoMode ( )
{
2022-08-04 21:47:01 +00:00
int xdim = screen - > GetWidth ( ) ;
int ydim = screen - > GetHeight ( ) ;
2021-02-25 11:16:21 +00:00
V_UpdateModeSize ( xdim , ydim ) ;
2022-08-04 21:47:01 +00:00
viewport3d = { 0 , 0 , xdim , ydim } ;
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 )
2022-10-21 22:12:17 +00:00
FStringCVarRef * const CombatMacros [ ] = { & combatmacro0 , & combatmacro1 , & combatmacro2 , & combatmacro3 , & combatmacro4 , & combatmacro5 , & combatmacro6 , & combatmacro7 , & combatmacro8 , & combatmacro9 } ;
2019-10-27 07:14:58 +00:00
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 ) ;
2020-07-27 16:12:24 +00:00
UCVarValue val ;
val . String = sc . String ;
2022-10-21 22:12:17 +00:00
s - > get ( ) - > SetGenericRepDefault ( val , CVAR_String ) ;
2019-11-24 09:03:19 +00:00
}
}
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-26 21:45:55 +00:00
2023-03-18 08:54:05 +00:00
CCMD ( pause )
{
sendPause = true ;
}
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
}
2022-01-16 01:20:46 +00:00
2020-02-05 11:57:17 +00:00
FString G_GetDemoPath ( )
{
FString path = M_GetDemoPath ( ) ;
path < < LumpFilter < < ' / ' ;
2023-10-08 07:15:32 +00:00
CreatePath ( path . GetChars ( ) ) ;
2020-02-05 11:57:17 +00:00
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
}
}
}
}
}
2020-06-14 16:57:55 +00:00
bool OkForLocalization ( FTextureID texnum , const char * substitute )
{
return false ;
}
2020-08-24 18:34:18 +00:00
// Mainly a dummy.
CCMD ( taunt )
{
if ( argv . argc ( ) > 2 )
{
int taunt = atoi ( argv [ 1 ] ) ;
int mode = atoi ( argv [ 2 ] ) ;
2021-12-30 09:30:21 +00:00
2020-08-24 18:34:18 +00:00
// In a ZDoom-style protocol this should be sent:
// Net_WriteByte(DEM_TAUNT);
// Net_WriteByte(taunt);
// Net_WriteByte(mode);
if ( mode = = 1 )
{
// todo:
//gi->PlayTaunt(taunt);
// Duke:
// startrts(taunt, 1)
// Blood:
// sndStartSample(4400 + taunt, 128, 1, 0);
// SW:
// PlaySoundRTS(taunt);
// Exhumed does not implement RTS, should be like Duke
//
}
Printf ( PRINT_NOTIFY , " %s " , * * CombatMacros [ taunt - 1 ] ) ;
}
}
2020-08-30 10:02:32 +00:00
2020-11-10 21:07:45 +00:00
void GameInterface : : loadPalette ( )
{
paletteLoadFromDisk ( ) ;
}
2020-08-30 10:02:32 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-09-03 21:10:28 +00:00
void GameInterface : : FreeLevelData ( )
{
// Make sure that there is no more level to toy around with.
2021-12-04 21:04:16 +00:00
InitSpriteLists ( ) ;
2021-12-05 09:40:31 +00:00
sector . Reset ( ) ;
wall . Reset ( ) ;
2020-09-03 21:10:28 +00:00
currentLevel = nullptr ;
2021-12-05 23:50:33 +00:00
GC : : FullGC ( ) ;
2020-09-03 21:10:28 +00:00
}
2020-09-04 22:58:25 +00:00
2020-09-05 11:57:26 +00:00
//---------------------------------------------------------------------------
//
// DrawCrosshair
//
//---------------------------------------------------------------------------
2022-11-30 23:26:43 +00:00
void ST_DrawCrosshair ( int phealth , double xpos , double ypos , double scale , DAngle angle ) ;
2020-10-28 15:56:00 +00:00
//void DrawGenericCrosshair(int num, int phealth, double xdelta);
void ST_LoadCrosshair ( int num , bool alwaysload ) ;
CVAR ( Int , crosshair , 0 , CVAR_ARCHIVE )
2020-09-05 11:57:26 +00:00
2023-01-01 12:05:42 +00:00
void DrawCrosshair ( int health , double xdelta , double ydelta , double scale , DAngle angle , PalEntry color )
2020-09-05 11:57:26 +00:00
{
if ( automapMode = = am_off & & cl_crosshair )
{
2023-01-01 12:05:42 +00:00
if ( crosshair = = 0 )
2020-09-05 11:57:26 +00:00
{
2023-01-01 12:05:42 +00:00
auto tex = TexMan . FindGameTexture ( " CROSSHAIR " , ETextureType : : Any ) ;
if ( tex & & tex - > isValid ( ) )
2020-09-05 11:57:26 +00:00
{
2022-12-11 05:16:16 +00:00
double crosshair_scale = crosshairscale > 0.0f ? crosshairscale * scale : 1. ;
2023-01-01 12:05:42 +00:00
DrawTexture ( twod , tex , 160 + xdelta , 100 + ydelta , DTA_Color , color , DTA_Rotate , angle . Degrees ( ) ,
2020-09-05 11:57:26 +00:00
DTA_FullscreenScale , FSMode_Fit320x200 , DTA_ScaleX , crosshair_scale , DTA_ScaleY , crosshair_scale , DTA_CenterOffsetRel , true ,
2022-08-04 21:47:01 +00:00
DTA_ViewportX , viewport3d . Left ( ) , DTA_ViewportY , viewport3d . Top ( ) , DTA_ViewportWidth , viewport3d . Width ( ) , DTA_ViewportHeight , viewport3d . Height ( ) , TAG_DONE ) ;
2020-09-05 11:57:26 +00:00
return ;
}
}
2020-10-28 15:56:00 +00:00
// 0 means 'game provided crosshair' - use type 2 as fallback.
ST_LoadCrosshair ( crosshair = = 0 ? 2 : * crosshair , false ) ;
2022-08-04 21:47:01 +00:00
double xpos = viewport3d . Width ( ) * 0.5 + xdelta * viewport3d . Height ( ) / 240. ;
2022-11-30 23:26:43 +00:00
double ypos = viewport3d . Height ( ) * 0.5 + ydelta * viewport3d . Width ( ) / 320. ;
ST_DrawCrosshair ( health , xpos , ypos , 1 , angle ) ;
2020-09-05 11:57:26 +00:00
}
2020-09-16 14:42:44 +00:00
}
2020-10-04 16:31:48 +00:00
bool M_Active ( )
{
return CurrentMenu ! = nullptr | | ConsoleState = = c_down | | ConsoleState = = c_falling ;
}
struct gamefilter
{
const char * gamename ;
int gameflag ;
} ;
static const gamefilter games [ ] = {
{ " Duke " , GAMEFLAG_DUKE } ,
{ " Nam " , GAMEFLAG_NAM | GAMEFLAG_NAPALM } ,
{ " NamOnly " , GAMEFLAG_NAM } , // for cases where the difference matters.
{ " Napalm " , GAMEFLAG_NAPALM } ,
{ " WW2GI " , GAMEFLAG_WW2GI } ,
{ " Redneck " , GAMEFLAG_RR } ,
{ " RedneckRides " , GAMEFLAG_RRRA } ,
{ " Blood " , GAMEFLAG_BLOOD } ,
{ " ShadowWarrior " , GAMEFLAG_SW } ,
{ " Exhumed " , GAMEFLAG_POWERSLAVE | GAMEFLAG_EXHUMED } ,
2020-10-08 20:20:41 +00:00
{ " Plutopak " , GAMEFLAG_PLUTOPAK } ,
2020-10-04 16:31:48 +00:00
{ " Worldtour " , GAMEFLAG_WORLDTOUR } ,
2020-10-07 21:22:29 +00:00
{ " Shareware " , GAMEFLAG_SHAREWARE } ,
2020-10-04 16:31:48 +00:00
} ;
bool validFilter ( const char * str )
{
for ( auto & gf : games )
{
if ( g_gameType & gf . gameflag )
{
if ( ! stricmp ( str , gf . gamename ) ) return true ;
}
}
return false ;
}
2020-10-05 18:44:30 +00:00
# include "vm.h"
DEFINE_ACTION_FUNCTION ( _Screen , GetViewWindow )
{
PARAM_PROLOGUE ;
2022-08-04 21:47:01 +00:00
if ( numret > 0 ) ret [ 0 ] . SetInt ( viewport3d . Left ( ) ) ;
if ( numret > 1 ) ret [ 1 ] . SetInt ( viewport3d . Top ( ) ) ;
if ( numret > 2 ) ret [ 2 ] . SetInt ( viewport3d . Width ( ) ) ;
if ( numret > 3 ) ret [ 3 ] . SetInt ( viewport3d . Height ( ) ) ;
2021-10-30 08:21:43 +00:00
return min ( numret , 4 ) ;
2020-10-05 18:44:30 +00:00
}
2021-04-23 08:07:02 +00:00
DEFINE_ACTION_FUNCTION_NATIVE ( _Raze , ShadeToLight , shadeToLight )
2020-10-06 22:50:26 +00:00
{
PARAM_PROLOGUE ;
PARAM_INT ( shade ) ;
ACTION_RETURN_INT ( shadeToLight ( shade ) ) ;
}
2021-04-23 10:24:42 +00:00
DEFINE_ACTION_FUNCTION ( _Raze , PlayerName )
{
PARAM_PROLOGUE ;
PARAM_INT ( index ) ;
ACTION_RETURN_STRING ( unsigned ( index ) > = MAXPLAYERS ? " " : PlayerName ( index ) ) ;
}
2021-05-15 12:27:32 +00:00
DEFINE_ACTION_FUNCTION_NATIVE ( _Raze , GetBuildTime , I_GetBuildTime )
{
ACTION_RETURN_INT ( I_GetBuildTime ( ) ) ;
}
2023-10-05 02:10:47 +00:00
DEFINE_ACTION_FUNCTION ( _Raze , forceSyncInput )
2023-03-18 08:28:02 +00:00
{
2023-03-23 06:09:02 +00:00
PARAM_PROLOGUE ;
PARAM_INT ( playeridx ) ;
2023-10-05 02:10:47 +00:00
gameInput . ForceInputSync ( playeridx ) ;
2023-03-18 08:28:02 +00:00
return 0 ;
}
2021-05-15 12:27:32 +00:00
DEFINE_ACTION_FUNCTION ( _Raze , PickTexture )
{
PARAM_PROLOGUE ;
PARAM_INT ( texid ) ;
TexturePick pick ;
2023-11-09 18:22:32 +00:00
if ( PickTexture ( TexMan . GetGameTexture ( FSetTextureID ( texid ) ) , TRANSLATION ( Translation_Remap , 0 ) . index ( ) , pick ) )
2021-05-15 12:27:32 +00:00
{
ACTION_RETURN_INT ( pick . texture - > GetID ( ) . GetIndex ( ) ) ;
}
ACTION_RETURN_INT ( texid ) ;
}
2021-05-01 22:35:56 +00:00
DEFINE_ACTION_FUNCTION ( _MapRecord , GetCluster )
{
PARAM_SELF_STRUCT_PROLOGUE ( MapRecord ) ;
ACTION_RETURN_POINTER ( FindCluster ( self - > cluster ) ) ;
}
2021-05-22 08:26:53 +00:00
DEFINE_ACTION_FUNCTION ( _Screen , GetTextScreenSize )
{
ACTION_RETURN_VEC2 ( DVector2 ( 640 , 480 ) ) ;
}
2020-10-05 18:44:30 +00:00
extern bool demoplayback ;
DEFINE_GLOBAL ( multiplayer )
DEFINE_GLOBAL ( netgame )
DEFINE_GLOBAL ( gameaction )
DEFINE_GLOBAL ( gamestate )
DEFINE_GLOBAL ( demoplayback )
DEFINE_GLOBAL ( consoleplayer )
2021-04-23 10:24:42 +00:00
DEFINE_GLOBAL ( currentLevel )
2021-04-25 18:02:40 +00:00
DEFINE_GLOBAL ( paused )
2021-05-11 20:21:52 +00:00
DEFINE_GLOBAL ( automapMode )
DEFINE_GLOBAL ( PlayClock )
2021-04-23 10:24:42 +00:00
2021-05-01 22:35:56 +00:00
DEFINE_FIELD_X ( ClusterDef , ClusterDef , name )
DEFINE_FIELD_X ( ClusterDef , ClusterDef , InterBackground )
2021-04-23 10:24:42 +00:00
DEFINE_FIELD_X ( MapRecord , MapRecord , parTime )
DEFINE_FIELD_X ( MapRecord , MapRecord , designerTime )
DEFINE_FIELD_X ( MapRecord , MapRecord , fileName )
DEFINE_FIELD_X ( MapRecord , MapRecord , labelName )
DEFINE_FIELD_X ( MapRecord , MapRecord , name )
DEFINE_FIELD_X ( MapRecord , MapRecord , music )
DEFINE_FIELD_X ( MapRecord , MapRecord , cdSongId )
DEFINE_FIELD_X ( MapRecord , MapRecord , flags )
2022-12-13 18:39:27 +00:00
DEFINE_FIELD_X ( MapRecord , MapRecord , gameflags )
2021-04-23 10:24:42 +00:00
DEFINE_FIELD_X ( MapRecord , MapRecord , levelNumber )
2021-04-27 22:51:28 +00:00
DEFINE_FIELD_X ( MapRecord , MapRecord , cluster )
2021-05-02 08:35:43 +00:00
DEFINE_FIELD_X ( MapRecord , MapRecord , NextMap )
DEFINE_FIELD_X ( MapRecord , MapRecord , NextSecret )
2021-04-23 10:24:42 +00:00
//native readonly String messages[MAX_MESSAGES];
2021-05-01 20:52:28 +00:00
DEFINE_FIELD_X ( MapRecord , MapRecord , Author )
2021-05-01 22:35:56 +00:00
DEFINE_FIELD_X ( MapRecord , MapRecord , InterBackground )
2021-04-26 00:00:40 +00:00
DEFINE_FIELD_X ( SummaryInfo , SummaryInfo , kills )
DEFINE_FIELD_X ( SummaryInfo , SummaryInfo , maxkills )
DEFINE_FIELD_X ( SummaryInfo , SummaryInfo , secrets )
DEFINE_FIELD_X ( SummaryInfo , SummaryInfo , maxsecrets )
DEFINE_FIELD_X ( SummaryInfo , SummaryInfo , supersecrets )
2021-04-26 19:13:11 +00:00
DEFINE_FIELD_X ( SummaryInfo , SummaryInfo , playercount )
2021-04-26 00:00:40 +00:00
DEFINE_FIELD_X ( SummaryInfo , SummaryInfo , time )
2022-10-25 15:09:45 +00:00
DEFINE_FIELD_X ( SummaryInfo , SummaryInfo , totaltime )
2021-04-26 00:00:40 +00:00
DEFINE_FIELD_X ( SummaryInfo , SummaryInfo , cheated )
DEFINE_FIELD_X ( SummaryInfo , SummaryInfo , endofgame )
2021-04-05 18:12:11 +00:00
void InitBuildTiles ( )
{
// need to find a better way to handle this thing.
}
2022-03-18 08:17:46 +00:00
static FString LevelName ;
2022-03-18 08:56:55 +00:00
void TITLE_InformName ( const char * newname )
{
LevelName = newname ;
if ( newname [ 0 ] = = ' $ ' )
LevelName = GStrings ( newname + 1 ) ;
I_UpdateWindowTitle ( ) ;
}
2022-03-18 08:17:46 +00:00
void I_UpdateWindowTitle ( )
{
FString titlestr ;
if ( ! ( GameStartupInfo . Name . IsNotEmpty ( ) ) )
return ;
switch ( I_FriendlyWindowTitle )
{
case 1 :
if ( LevelName . IsNotEmpty ( ) )
{
titlestr . Format ( " %s - %s " , LevelName . GetChars ( ) , GameStartupInfo . Name . GetChars ( ) ) ;
break ;
}
[[fallthrough]] ;
case 2 :
titlestr = GameStartupInfo . Name ;
break ;
default :
I_UpdateDiscordPresence ( false , NULL , GameStartupInfo . DiscordAppId . GetChars ( ) , GameStartupInfo . SteamAppId . GetChars ( ) ) ;
I_SetWindowTitle ( NULL ) ;
return ;
}
// Strip out any color escape sequences before setting a window title
TArray < char > copy ( titlestr . Len ( ) + 1 ) ;
2023-10-08 07:15:32 +00:00
const char * srcp = titlestr . GetChars ( ) ;
2022-03-18 08:17:46 +00:00
char * dstp = copy . Data ( ) ;
while ( * srcp ! = 0 )
{
if ( * srcp ! = TEXTCOLOR_ESCAPE )
{
* dstp + + = * srcp + + ;
}
else if ( srcp [ 1 ] = = ' [ ' )
{
srcp + = 2 ;
while ( * srcp ! = ' ] ' & & * srcp ! = 0 ) srcp + + ;
if ( * srcp = = ' ] ' ) srcp + + ;
}
else
{
if ( srcp [ 1 ] ! = 0 ) srcp + = 2 ;
else break ;
}
}
* dstp = 0 ;
if ( i_discordrpc )
I_UpdateDiscordPresence ( true , copy . Data ( ) , GameStartupInfo . DiscordAppId . GetChars ( ) , GameStartupInfo . SteamAppId . GetChars ( ) ) ;
else
I_UpdateDiscordPresence ( false , nullptr , nullptr , nullptr ) ;
I_SetWindowTitle ( copy . Data ( ) ) ;
}